Schalen
AnsibleForms verdeeld over meerdere app-nodes en een worker, naast een of meer RTE’s
Rollen
Elke AnsibleForms-container draait dezelfde servercode, en AF_ROLE bepaalt wat hij doet:
AF_ROLE | Image | Draait | Hoeveel |
|---|---|---|---|
all (niet ingesteld) | ansibleforms | De webapp en de worker in één proces: de standaard | 1 |
app | ansibleforms | De webinterface en de API | Zoveel als je wilt, achter een load balancer |
worker | ansibleforms | Het achtergrondwerk: het databaseschema en de upgrades ervan, de configuratie-seed, de planningen, de nachtelijke back-up, de synchronisaties van repositories en de opschoningen | Eén aan het werk; meer wachten en nemen het over |
rte | ansibleforms-rte-full (of -rte-core, -rte-base) | De playbooks | Zoveel als je wilt |
De kleinste installatie is één container zonder AF_ROLE naast één RTE: dat is wat het Docker Compose-project en de Helm-chart draaien. Elke andere waarde van AF_ROLE stopt het proces bij de start.
De RTE
De RTE, de runtime-omgeving die de playbooks draait, heeft een eigen sectie: Runtime-omgevingen. Daar staat hoe een RTE een job draait, de images, hoe je er een draait, meerdere RTE’s als runners of replica’s, en een RTE in een ander datacenter.
Meerdere app-nodes
Om meer gebruikers te bedienen, of het verlies van één node te overleven, splits je de app op in app-nodes achter een load balancer en een worker.
Wat de nodes delen
De nodes stemmen met elkaar af via de database en een gedeeld volume:
- De database : de jobs, de workervergrendeling, de designer-vergrendeling, de lijst met nodes, de runnerleases (welke app-node de socket van elke RTE vasthoudt) en de wijzigingsberichten die de caches van elke node actueel houden. Een referentie, een runner of een planning die op één node is bewerkt, geldt binnen 5 seconden op alle nodes
- Het persistente volume (
/app/dist/persistent) : formulieren, de configuratie, repositories, uploads, back-ups en de beheerde.env. Elke app-node en de worker koppelen hetzelfde volume, met lees- en schrijfrechten - De ssh-map (
/home/node/.ssh) : de sleutel en de known hosts voor git over ssh; de worker maakt de sleutel bij zijn eerste start aan ACCESS_TOKEN_SECRET: hetzelfde op elke app-node, anders wordt een token dat door de ene node is ondertekend door de volgende geweigerd. Een app-node weigert zonder dit geheim te startenENCRYPTION_SECRET: hetzelfde op elke app-node en worker, anders kan een referentie die door de ene is opgeslagen door de andere niet worden gelezen. Een RTE in socketmodus leest het nooit; alleen een verouderde RTE in databasemodus die zichzelf registreert, heeft het nodig
Geef elke container ook dezelfde databasegebruiker: een wachtende worker kan de sessie van een dode worker alleen beëindigen als het zijn eigen sessie is.
De workervergrendeling
Er werkt steeds maar één worker. Die houdt de databasevergrendeling ansibleforms_worker vast; als hij stopt, geeft de database de vergrendeling vrij en neemt een wachtende worker haar binnen 10 seconden over. Als de host van de worker zonder afscheid uitvalt, zou de database de vergrendeling uren kunnen vasthouden, dus beëindigt een wachtende worker die sessie zodra geen enkele worker twee minuten lang een heartbeat heeft geschreven, en neemt hij het over.
Een worker die de vergrendeling aan een andere verliest, stopt, en zijn container herstart als de wachtende. Een worker waarvan de start mislukt, stopt ook, zodat hij de vergrendeling nooit vasthoudt zonder iets te doen. Galera en group replication delen de vergrendeling niet tussen databasenodes: laat elke worker naar dezelfde databasenode wijzen.
Jobs en nodes
Elke container noemt zichzelf <role>-<hostname>-<port>, zonder dat je iets hoeft in te stellen, en de pagina Status toont ze allemaal met hun rol en versie, welke de worker is, en een waarschuwing als ze verschillende versies draaien.
Een job die op een RTE draait, hoort bij die RTE, en loopt door als de app-node die hem startte wegvalt. Elke andere job (een templatejob op AWX of een andere runner, een workflowjob, of de stappen van een verouderd multistep-formulier) hoort bij de app-node die hem startte. Een container die herstart, beëindigt zijn eigen onvoltooide jobs, en de worker beëindigt de jobs van een container die twee minuten lang niet antwoordde, zoals een vervangen pod.
Een SIGHUP die naar een willekeurige node wordt gestuurd, past de configuratie-seed opnieuw toe: een app-node vraagt de worker dat te doen.
Upgraden
Upgrade eerst de worker, daarna de app-nodes. De worker patcht het schema, en een app-node van de nieuwe versie wacht tot het schema is gepatcht en logt waarop hij wacht. App-nodes die eerst zijn geüpgraded, blijven niet gereed totdat de worker volgt, terwijl de oude blijven bedienen. RTE’s kunnen op elk moment volgen, zolang ze hetzelfde contract spreken.
Beperkingen
Een paar dingen blijven bij één node:
- De chat-assistent bewaart zijn gesprekken in het geheugen van één app-node: gebruik sticky sessions op de load balancer (nginx
ip_hash), of één enkele app-node - Logs blijven in elke container. Zet
LOG_PATHbuiten het gedeelde volume, omdat meerdere processen die één bestand roteren regels verliezen. De logviewer toont het log van de node die antwoordt, dus verzamel de logs centraal - Instellingen die op de instellingenpagina’s worden gewijzigd, worden naar de beheerde
.envop het gedeelde volume geschreven en gelden op elke node; de instellingen die een herstart vereisen, vereisen die op elke node
Voorbeeldopzet
De app-repository heeft een compleet startpunt in examples/scale:
| Service | Image | Rol |
|---|---|---|
db | mariadb:11 | De database, met een gebruiker die alleen rechten heeft op het schema AnsibleForms |
app1, app2 | ghcr.io/ansibleforms/ansibleforms:7 | AF_ROLE=app, achter de load balancer |
worker1, worker2 | ghcr.io/ansibleforms/ansibleforms:7 | AF_ROLE=worker: de tweede wacht en neemt het over |
rte | ghcr.io/ansibleforms/ansibleforms-rte-full:7 | Draait de playbooks met RTE_MODE=inbound en RTE_FILES=local: de app belt haar |
lb | nginx:stable | Handelt HTTPS af op poort 8443 en verdeelt de browsers over de app-nodes met ip_hash |
De runner rte van de RTE en de referentie waarmee hij zich aanmeldt (zijn adres http://rte:8000 en RTE_TOKEN) worden gedeclareerd in de configuratie-seed seed.yaml, die de worker bij elke start toepast; de RTE stelt geen RTE_URL in en wordt niet op de host gepubliceerd.
De app-nodes en de workers delen één .env, met de databasegebruiker, ENCRYPTION_SECRET, ACCESS_TOKEN_SECRET, RTE_TOKEN, CONFIG_SEED_PATH, HTTPS=0 achter de load balancer en een LOG_PATH buiten het gedeelde volume. De RTE heeft geen env_file: hij krijgt alleen RTE_TOKEN uit compose.yaml, dus hij bevat geen databasewachtwoord. De app-nodes, de workers en de RTE koppelen dezelfde persistente en ssh-volumes:
cp .env.example .env # and fill in the secrets
docker compose up -d # then https://localhost:8443