Entorns d’execució (RTE)
Què és un RTE, com executa un playbook i què pot tocar
Per a administradors: entén l’RTE abans d’executar-ne un, de dimensionar-lo o de moure’l a una altra xarxa.
| Pàgina | Què hi trobaràs |
|---|---|
| Imatges | Les tres imatges de l’RTE, i com construir la teva pròpia amb les col·leccions que fan servir els teus playbooks |
| Modes | Sortint, entrant o base de dades: qui obre la connexió, l’spool, els fitxers de la tasca |
| Executar un RTE | Els ajustos de cada mode, i un exemple de docker run de cadascun |
| Diversos RTE | Capacitat, rèpliques darrere d’una adreça, i què passa quan un RTE s’atura o mor |
| Un altre centre de dades | Un runbook: afegir un RTE en un segon centre de dades, des d’un host buit fins a la seva primera tasca |
Què és un RTE
AnsibleForms 7 no executa cap playbook al contenidor de l’aplicació. La tasca d’un formulari ansible es passa a un runner de tipus rte, un entorn d’execució: el servidor d’AnsibleForms iniciat amb AF_ROLE=rte, al costat de python i ansible, sense interfície web. Parla amb l’aplicació per un sol WebSocket i no té cap credencial de base de dades (RTE_MODE outbound o inbound, vegeu Modes): l’aplicació li envia la tasca, l’RTE executa ansible-playbook, i l’aplicació desa la sortida i l’estat final que li torna, d’on els llegeix el navegador.
L’aplicació resol les credencials de la tasca i les passa a l’RTE segellades només per a aquell RTE i aquella tasca. L’RTE mai no llegeix la taula de credencials ni cap magatzem de secrets, i un playbook no hereta les variables d’entorn pròpies d’AnsibleForms (ENCRYPTION_SECRET, DB_*, RTE_TOKEN i les altres): passa un valor d’aquests com a extravar o com a credencial. L’RTE de la 7.0 a la 7.2, que llegia i escrivia ell mateix la base de dades, encara funciona a la 7.x com a mode base de dades obsolet (vegeu Mode base de dades).
Com s’executa l’RTE
L’RTE és un contenidor que executes tu mateix, al costat de l’aplicació o en una altra xarxa: AnsibleForms mai no en crea cap. Aquí es repassa on s’executa, què comparteix amb l’aplicació i què hi passa a dins des del moment que es llança un formulari fins que el procés del playbook desapareix:
El diagrama mostra un sòcol per RTE, l’obri qui l’obri: l’aplicació truca a un RTE entrant, un RTE sortint truca a l’aplicació (vegeu Modes). Un cop obert, hi circulen els mateixos missatges en tots dos sentits, i només l’aplicació llegeix i escriu la base de dades.
Qui l’inicia. Un RTE és un contenidor de llarga durada, iniciat pel mateix que executa els teus altres contenidors. Espera tasques i continua funcionant entre una i una altra:
| Com executes AnsibleForms | Qui inicia l’RTE |
|---|---|
docker run o podman run | Tu, amb una ordre com la d’Executar un RTE, en qualsevol host que arribi a l’aplicació, o al qual arribi l’aplicació |
| Docker Compose | El servei rte de docker-compose.yml, al costat d’app i mysqldb. La seva llavor de configuració el declara com el runner predeterminat rte, entrant, a http://rte:8000 |
| Chart de Helm | Kubernetes, com un segon contenidor anomenat rte al pod de l’aplicació (containers.rte). L’aplicació hi truca a 127.0.0.1, entrant, i la llavor del chart el declara com el runner predeterminat |
| L’instal·lador | Docker Compose o el chart de Helm com més amunt, o un servei systemd sense contenidors; un Runner propi, que demana unir-se i espera l’aprovació d’un admin |
El motor de contenidors és teu: Docker o Podman a l’amfitrió, o el runtime de contenidors del teu clúster de Kubernetes, instal·lat i mantingut al dia per tu. AnsibleForms mai no hi parla. Ni l’aplicació ni l’RTE criden Docker o Podman, en munten el sòcol o inicien cap contenidor: l’aplicació només parla amb l’RTE pel seu sòcol.
Què hi ha a dins. Un contenidor, amb el que cal per executar un playbook i res més:
- El servidor : el mateix servidor Node.js que l’aplicació, iniciat amb
AF_ROLE=rte(definit a la imatge) sotatini - El que queda fora : la interfície web, l’inici de sessió, el planificador, el worker i tots els mòduls de base de dades, que aquest rol no carrega
- On escolta : al seu
PORT(8000):/livei/readyper a les sondes i/metricsper a Prometheus, i en mode entrant el sòcol al qual truca l’aplicació,/rte/v1/socket. Un RTE sortint no accepta cap connexió de l’aplicació - Python i ansible : els aporta la capa d’imatge que triïs (vegeu Les imatges de l’RTE)
- L’usuari : el servidor i tots els playbooks s’executen com a
node(uid 1000), mai com a root
Què comparteix amb l’aplicació. El seu token, i amb RTE_FILES=local els fitxers de l’aplicació. Amb RTE_FILES=transfer, el predeterminat, els playbooks i els inventaris arriben amb cada tasca, i l’RTE no necessita cap disc compartit:
| Compartit amb l’aplicació | Què en fa l’RTE |
|---|---|
RTE_TOKEN | El presenta quan truca a l’aplicació (sortint), o el comprova quan l’aplicació li truca (entrant). L’aplicació té el mateix valor a la credencial RTE del runner, i els secrets de la tasca es segellen amb ell |
El volum persistent, /app/dist/persistent, només amb RTE_FILES=local | La carpeta de playbooks (ANSIBLE_PATH), o el repositori de playbooks que el worker ha clonat sota REPO_PATH, els inventaris (INVENTORY_PATH i els repositoris d’inventari) i els fitxers pujats. L’RTE escriu els fitxers d’extravars de la tasca a la carpeta del playbook, així que munta-la en lectura i escriptura |
La carpeta ssh, /home/node/.ssh | La clau privada i el known_hosts que fan servir les connexions ssh del playbook: munta la de l’aplicació, gestionada a les seves pàgines de configuració, o dona a l’RTE la seva pròpia |
L’RTE no clona res: un repositori li arriba als fitxers d’una tasca, o al volum que comparteix amb l’aplicació. Les col·leccions, els rols i ansible.cfg vénen de la imatge, de les carpetes /etc/ansible/collections, /etc/ansible/roles i /etc/ansible/ansible.cfg quan les hi muntes (el projecte Docker Compose ho fa), o d’un ansible.cfg al costat dels playbooks.
La vida d’una tasca. Els números de sota són els del diagrama, per a un formulari de playbook llançat des del navegador o l’API:
- Llançament : l’aplicació comprova el formulari i els seus valors, construeix les extravars i desa la tasca amb l’estat
running. Un formulari amb una aprovació s’atura aquí fins que un aprovador l’accepta, i abans no arriba a cap RTE - Tria del runner : l’aplicació pren l’RTE que el formulari nomena amb
runner: <name>, o si no el runner predeterminat de tipus RTE, i en desa el nom a la tasca. Resol les credencials de la tasca a partir de la base de dades o d’un magatzem de secrets, el seu accés d’ansible i la seva contrasenya de vault, i les segella amb AES-256-GCM sota una clau derivada del token d’aquell runner i de l’id de la tasca - Lliurament : el node d’aplicació que té el sòcol de l’RTE li envia la tasca: les extravars, les opcions del formulari, els secrets segellats i els hashes dels fitxers de la tasca. Qualsevol node d’aplicació pot llançar la tasca; el que té el sòcol l’envia en menys de dos segons. Un RTE ple, o un que no està connectat, fa esperar la tasca, fins a
RTE_QUEUE_MINUTES - Reclamació : l’RTE respon
claimed, orefusedquan està ple, s’està aturant, no pot obrir els secrets, o la tasca arriba amb un altre contracte que el seu. AmbRTE_FILES=transferpren els playbooks i els inventaris de la seva memòria cau pel seu hash, demana a l’aplicació un hash que no té, i els desempaqueta en una carpeta pròpia de la tasca - Inici : a la carpeta del playbook (més el
playbookSubPathdel formulari, que ha d’existir) escriu dos fitxers:extravars_<id>.jsonamb els valors del formulari i les credencials, i un d’ocult amb l’accés d’ansible. Després iniciaansible-playbookcom a procés fill seu, sense shell, en un grup de processos propi, amb-e @aquests fitxers, un-iper inventari, i els tags, el limit, el check, el diff i la verbositat del formulari. La contrasenya de vault entra per stdin, mai en un fitxer ni a la línia d’ordres, i el procés s’executa com anodeamb un entorn filtrat (vegeu més avall) - Execució : el playbook es connecta als hosts gestionats des del contenidor de l’RTE, per ssh amb la clau i el
known_hostsde/home/node/.ssh, ambsshpassper a un accés amb contrasenya, o per les API que criden els seus mòduls. És l’RTE, no l’aplicació, qui hi ha d’arribar - Sortida i final : l’RTE agrupa stdout i stderr cada mig segon o cada 64 KB, amb les credencials emmascarades, i numera cada lot. Escriu cada element a l’spool de la tasca al seu disc abans d’enviar-lo: la sortida, els esdeveniments, el registre de la tasca de
.joblogs/job_log_<id>.log(cada 2 segons), l’inici i el final amb l’estat final (success,failedoaborted). L’aplicació desa cada element una sola vegada i en confirma la recepció, i l’RTE descarta l’spool un cop desat el final. PassatsPROCESS_MAX_BUFFERbytes en un flux (50 MB per defecte) ja no n’envia més, i el playbook continua. L’aplicació envia llavors les notificacions del formulari - Seguiment : el navegador mai no parla amb l’RTE: la pàgina del formulari torna a llegir la tasca a través de l’aplicació cada 2 segons, i la pàgina Tasques la segueix en directe a mesura que l’aplicació desa cada element
Un sòcol que cau. El playbook continua. L’RTE torna a trucar (sortint), o un node d’aplicació hi torna a trucar (entrant), i torna a enviar el que l’aplicació no havia confirmat: l’aplicació salta el que ja havia desat, de manera que no es perd ni es desa dues vegades cap línia. Això val mentre el sòcol torni en menys de dos minuts: l’aplicació escriu el heartbeat de l’RTE només mentre el seu sòcol està obert, i quan aquest heartbeat té dos minuts el worker acaba les tasques en execució de l’RTE com a abandonades, encara que els seus playbooks continuïn funcionant. Mantén l’spool (RTE_SPOOL_PATH) en un volum, perquè un RTE reiniciat encara enviï el que havia conservat. Un playbook que s’estava executant quan el seu RTE s’ha reiniciat no pot continuar: l’aplicació n’acaba la tasca com a abandonada quan l’RTE torna sense ell.
Avortament. Un avortament activa l’indicador d’avortament de la tasca, i el node d’aplicació que té el sòcol de l’RTE envia cancel en menys de dos segons. L’RTE envia SIGTERM al grup de processos del playbook, de manera que ansible-playbook i tots els forks que ha iniciat s’aturen alhora, i la tasca acaba aborted. Una cancel·lació que arriba abans que la seva tasca es conserva durant un minut, i llavors la tasca comença avortada.
Neteja. Tan bon punt el procés acaba, sigui quin sigui el resultat, l’RTE elimina els dos fitxers d’extravars (un formulari amb keepExtravars conserva el visible, mai l’ocult), el fitxer d’artefactes de /tmp, el fitxer de registre de la tasca i la carpeta de fitxers pròpia de la tasca. El final es desa una sola vegada: un segon escriptor, com ara l’aplicació fent fallar una tasca que l’RTE ja ha acabat, no canvia res.
Un procés per tasca, no un contenidor per tasca. AWX i AAP inicien un pod o un contenidor nou a partir d’una imatge d’execution environment per a cada tasca. AnsibleForms no inicia res: el contenidor de l’RTE ja està en marxa, i cada tasca és un procés fill a dins seu. Una tasca comença sense descarregar cap imatge ni planificar cap pod, i AnsibleForms no necessita cap permís sobre el teu motor de contenidors o el teu clúster. A canvi, els playbooks que s’executen en un RTE en comparteixen la CPU, la memòria, /tmp, la carpeta personal i python: dona al contenidor prou recursos per a RTE_MAX_JOBS playbooks amb els seus forks. Per canviar el que pot fer servir un playbook, canvia la imatge, no la tasca.
El límit de seguretat
L’RTE executa codi que han escrit els autors dels formularis, així que està fet per contenir tan poc com sigui possible:
- Només el seu token : l’RTE no té cap credencial de base de dades ni cap
ENCRYPTION_SECRET. No accepta cap crida tret del sòcol de l’aplicació, amb el seu token, i no té usuaris, ni inici de sessió, ni pàgines web. Pel sòcol només pot executar les tasques que li envia l’aplicació, informar-ne i demanar-ne els fitxers: no pot crear cap tasca, llegir-ne una altra ni repartir credencials - Secrets segellats : per executar una tasca, l’RTE mai no llegeix la taula de credencials ni cap magatzem de secrets. Rep els secrets de les tasques que executa, segellats només per al seu token i aquell id de tasca, de manera que un paquet pensat per a un altre RTE o una altra tasca no s’obre, i un byte canviat es rebutja
- Una connexió comprovada : en mode sortint, l’RTE comprova el certificat de l’aplicació. En mode entrant per HTTPS, l’aplicació fixa el certificat propi de l’RTE quan s’uneix, i només hi truca quan presenta aquest certificat (vegeu Modes). Un RTE entrant per HTTP sense xifrar ha de ser en una xarxa privada, com la del projecte Docker Compose
- Cap entorn heretat :
ansible-playbookrep l’entorn de l’RTE sense cap de les variables que llegeix el mateix AnsibleForms (ENCRYPTION_SECRET,DB_PASSWORD,RTE_TOKEN,VAULT_TOKEN,ACCESS_TOKEN_SECRET, lesMYSQL_*i les altres), de manera quelookup('env', ...)no hi pot arribar. El que defineixis per als playbooks (PATH, proxies,ANSIBLE_*) passa tal com és - Cap plantilla dels usuaris : un valor de formulari que conté
{{,{%o{#arriba al playbook com a cadena unsafe que ansible mai no processa com a plantilla, tret que el formulari defineixiallowJinjaInExtravars, i cap shell no executa mai la línia d’ordres - No root : l’RTE i els seus playbooks s’executen com a
node(uid 1000), de manera que un playbook pot fer el que pot fer aquest usuari al contenidor, com ara llegir-ne els fitxers i la clau ssh, i res més
Tracta el seu contenidor, la seva imatge i el seu entorn amb la mateixa cura que els de l’aplicació.
Mode base de dades (obsolet)
L’RTE de la 7.0 a la 7.2, sense RTE_MODE, funciona durant tota la 7.x i s’elimina a la 8.0.0. Es diferencia dels passos de més amunt en quatre punts:
- Té la base de dades : els seus ajustos
DB_*, iENCRYPTION_SECRETquan es registra ell mateix (RTE_REGISTER) - El lliurament és una crida HTTP : l’aplicació crida
POST <address>/rte/v1/jobsamb el token i els secrets segellats, i l’RTE reclama la tasca escrivint-hi el seu nom a la base de dades (passos 3 i 4) - Escriu ell mateix la sortida : a la base de dades, per lots, sense spool (pas 7). Un avortament li arriba per l’indicador d’avortament de la tasca, que llegeix cada 2 segons, i per una crida
/cancel - Llegeix els fitxers del volum de l’aplicació : necessita el mateix disc que l’aplicació, com amb
RTE_FILES=local
La seva pàgina de runner no té cap dels ajustos de l’RTE: llegeix les seves pròpies variables. Per passar-lo a un mode de sòcol, vegeu Modes.