Laufzeitumgebungen (RTE)
Was eine RTE ist, wie sie ein Playbook ausführt und worauf sie zugreifen darf
Für Administratoren: Verstehen Sie die RTE, bevor Sie eine betreiben, dimensionieren oder in ein anderes Netz verlegen.
| Seite | Inhalt |
|---|---|
| Images | Die drei RTE-Images und der Bau eines eigenen mit den Collections, die Ihre Playbooks verwenden |
| Modi | Ausgehend, eingehend oder Datenbank: wer die Verbindung öffnet, der Spool, die Dateien des Jobs |
| Eine RTE betreiben | Die Einstellungen jedes Modus und jeweils ein Beispiel mit docker run |
| Mehrere RTEs | Kapazität, Replikate hinter einer Adresse und was passiert, wenn eine RTE anhält oder ausfällt |
| Ein anderes Rechenzentrum | Ein Runbook: eine RTE in einem zweiten Rechenzentrum hinzufügen, vom leeren Host bis zu ihrem ersten Job |
Was eine RTE ist
AnsibleForms 7 führt kein Playbook im App-Container aus. Der Job eines ansible-Formulars wird an einen Runner vom Typ rte übergeben, eine Laufzeitumgebung: der AnsibleForms-Server, gestartet mit AF_ROLE=rte, neben Python und Ansible, ohne Weboberfläche. Sie spricht mit der App über einen WebSocket und hält keine Datenbank-Anmeldedaten (RTE_MODE outbound oder inbound, siehe Modi): Die App sendet ihr den Job, die RTE führt ansible-playbook aus, und die App speichert die Ausgabe und den Endstatus, die sie zurückschickt, wo der Browser sie liest.
Die App löst die Anmeldedaten des Jobs auf und übergibt sie der RTE versiegelt, nur für diese RTE und diesen Job. Die RTE liest weder die Tabelle der Anmeldedaten noch einen Secret Store, und ein Playbook erbt nicht die Umgebungsvariablen von AnsibleForms selbst (ENCRYPTION_SECRET, DB_*, RTE_TOKEN und die anderen): Übergeben Sie einen solchen Wert als Extravar oder Anmeldedaten. Die RTE von 7.0 bis 7.2, die selbst in der Datenbank las und schrieb, läuft in 7.x weiterhin als veralteter Datenbankmodus (siehe Datenbankmodus).
Wie die RTE läuft
Die RTE ist ein Container, den Sie selbst betreiben, neben der App oder in einem anderen Netz: AnsibleForms erzeugt nie einen. Dieser Abschnitt geht durch, wo sie läuft, was sie mit der App teilt und was in ihr geschieht, vom Start eines Formulars an bis der Prozess des Playbooks beendet ist:
Das Diagramm zeigt einen Socket pro RTE, egal welche Seite ihn geöffnet hat: Die App wählt sich bei einer eingehenden RTE ein, eine ausgehende RTE wählt sich bei der App ein (siehe Modi). Sobald er offen ist, gehen dieselben Nachrichten in beide Richtungen, und nur die App liest und schreibt die Datenbank.
Wer sie startet. Eine RTE ist ein langlebiger Container, gestartet von dem, was auch Ihre anderen Container betreibt. Sie wartet auf Jobs und läuft zwischen ihnen weiter:
| Wie Sie AnsibleForms betreiben | Wer die RTE startet |
|---|---|
docker run oder podman run | Sie, mit einem Befehl wie dem in Eine RTE betreiben, auf einem beliebigen Host, der die App erreicht oder den die App erreicht |
| Docker Compose | Der Dienst rte von docker-compose.yml, neben app und mysqldb. Sein Config-Seed deklariert ihn als Standard-Runner rte, eingehend, unter http://rte:8000 |
| Helm-Chart | Kubernetes, als zweiter Container namens rte im Pod der App (containers.rte). Die App wählt sich unter 127.0.0.1 ein, eingehend, und der Seed des Charts deklariert ihn als Standard-Runner |
| Das Installationsprogramm | Docker Compose oder das Helm-Chart wie oben, oder ein systemd-Dienst ohne Container; ein eigener Runner, der um Beitritt bittet und auf die Freigabe eines Admins wartet |
Die Container-Engine gehört Ihnen: Docker oder Podman auf dem Host oder die Container-Runtime Ihres Kubernetes-Clusters, von Ihnen installiert und aktuell gehalten. AnsibleForms spricht nie mit ihr. Weder die App noch die RTE ruft Docker oder Podman auf, hängt deren Socket ein oder startet einen Container: Die App spricht mit der RTE nur über deren Socket.
Was drin ist. Ein Container, mit dem, was zum Ausführen eines Playbooks nötig ist, und nichts weiter:
- Der Server : derselbe Node.js-Server wie die App, gestartet mit
AF_ROLE=rte(im Image gesetzt) untertini - Weggelassen : die Weboberfläche, die Anmeldung, der Zeitplaner, der Worker und jedes Datenbankmodul, die diese Rolle nicht lädt
- Worauf sie lauscht : ihren
PORT(8000):/liveund/readyfür Probes und/metricsfür Prometheus, und eingehend den Socket, bei dem sich die App einwählt,/rte/v1/socket. Eine ausgehende RTE nimmt keine Verbindung von der App an - Python und Ansible : mitgebracht von der gewählten Image-Schicht (siehe Die RTE-Images)
- Der Benutzer : Der Server und jedes Playbook laufen als
node(uid 1000), nie als root
Was sie mit der App teilt. Ihr Token und mit RTE_FILES=local die Dateien der App. Mit RTE_FILES=transfer, dem Standard, kommen die Playbooks und Inventare mit jedem Job, und die RTE braucht keine gemeinsame Festplatte:
| Mit der App geteilt | Was die RTE damit tut |
|---|---|
RTE_TOKEN | Legt es vor, wenn sie sich bei der App einwählt (ausgehend), oder prüft es, wenn die App sich bei ihr einwählt (eingehend). Die App hält denselben Wert in den RTE-Anmeldedaten des Runners, und die Geheimnisse des Jobs werden damit versiegelt |
Das persistente Volume, /app/dist/persistent, nur mit RTE_FILES=local | Der Playbook-Ordner (ANSIBLE_PATH) oder das Playbook-Repository, das der Worker unter REPO_PATH geklont hat, die Inventare (INVENTORY_PATH und die Inventar-Repositories) und die Uploads. Die RTE schreibt die Extravars-Dateien des Jobs in den Playbook-Ordner, hängen Sie ihn also mit Schreibzugriff ein |
Der ssh-Ordner, /home/node/.ssh | Der private Schlüssel und die known_hosts, die die SSH-Verbindungen des Playbooks verwenden: Hängen Sie den der App ein, der auf ihren Einstellungsseiten verwaltet wird, oder geben Sie der RTE einen eigenen |
Die RTE klont nichts: Ein Repository erreicht sie in den Dateien eines Jobs oder auf dem Volume, das sie mit der App teilt. Collections, Rollen und ansible.cfg kommen aus dem Image, aus den Ordnern /etc/ansible/collections, /etc/ansible/roles und /etc/ansible/ansible.cfg, wenn Sie sie dort einhängen (das Docker Compose-Projekt tut das), oder aus einer ansible.cfg neben den Playbooks.
Das Leben eines Jobs. Die Nummern unten sind die Nummern im Diagramm, für ein Playbook-Formular, das aus dem Browser oder über die API gestartet wird:
- Start : Die App prüft das Formular und seine Werte, baut die Extravars und speichert den Job mit dem Status
running. Ein Formular mit einer Genehmigung hält hier an, bis ein Genehmiger es akzeptiert, und erreicht vorher keine RTE - Den Runner wählen : Die App nimmt die RTE, die das Formular mit
runner: <name>nennt, sonst den Standard-Runner vom Typ RTE, und speichert seinen Namen am Job. Sie löst die Anmeldedaten des Jobs aus der Datenbank oder einem Secret Store auf, seine Ansible-Anmeldung und sein Vault-Passwort, und versiegelt sie mit AES-256-GCM unter einem Schlüssel, der aus dem Token dieses Runners und der Job-ID abgeleitet ist - Übergabe : Der App-Knoten, der den Socket der RTE hält, sendet ihr den Job: die Extravars, die Optionen des Formulars, die versiegelten Geheimnisse und die Hashes der Dateien des Jobs. Jeder App-Knoten kann den Job starten; der Knoten mit dem Socket sendet ihn innerhalb von zwei Sekunden. Eine volle RTE oder eine nicht verbundene lässt den Job warten, bis zu
RTE_QUEUE_MINUTES - Übernahme : Die RTE antwortet
claimedoderrefused, wenn sie voll ist, anhält, die Geheimnisse nicht öffnen kann oder der Job mit einem anderen Vertrag als ihrem eigenen kommt. MitRTE_FILES=transfernimmt sie die Playbooks und Inventare anhand ihres Hashs aus ihrem Cache, fragt die App nach einem Hash, den sie nicht hat, und entpackt sie in einen eigenen Ordner des Jobs - Start des Prozesses : Im Playbook-Ordner (plus dem
playbookSubPathdes Formulars, der existieren muss) schreibt sie zwei Dateien:extravars_<id>.jsonmit den Werten des Formulars und den Anmeldedaten sowie eine versteckte mit der Ansible-Anmeldung. Dann startet sieansible-playbookals eigenen Kindprozess, ohne Shell, in einer eigenen Prozessgruppe, mit-e @auf diese Dateien, einem-ipro Inventar sowie Tags, Limit, Check, Diff und Ausführlichkeit des Formulars. Das Vault-Passwort geht über stdin hinein, nie in eine Datei oder auf die Befehlszeile, und der Prozess läuft alsnodemit gefilterter Umgebung (siehe unten) - Ausführung : Das Playbook verbindet sich vom RTE-Container aus mit den verwalteten Hosts, über SSH mit dem Schlüssel und den
known_hostsaus/home/node/.ssh, mitsshpassfür eine Passwortanmeldung, oder über die APIs, die seine Module aufrufen. Die RTE, nicht die App, muss sie erreichen - Ausgabe und Ende : Die RTE bündelt stdout und stderr jede halbe Sekunde oder alle 64 KB, mit maskierten Anmeldedaten, und nummeriert jedes Bündel. Sie schreibt jedes Element in den Spool des Jobs auf ihrer Festplatte, bevor sie es sendet: die Ausgabe, die Ereignisse, das Job-Protokoll aus
.joblogs/job_log_<id>.log(alle 2 Sekunden), den Start und das Ende mit dem Endstatus (success,failedoderaborted). Die App speichert jedes Element einmal und bestätigt es, und die RTE verwirft den Spool, sobald das Ende gespeichert ist. Jenseits vonPROCESS_MAX_BUFFERBytes auf einem Stream (standardmäßig 50 MB) sendet sie nichts mehr, und das Playbook läuft weiter. Die App sendet dann die Benachrichtigungen des Formulars - Verfolgen : Der Browser spricht nie mit der RTE: Die Formularseite liest den Job alle 2 Sekunden über die App neu, und die Seite Jobs verfolgt ihn live, während die App jedes Element speichert
Ein abgerissener Socket. Das Playbook läuft weiter. Die RTE wählt sich erneut ein (ausgehend), oder ein App-Knoten wählt sich erneut bei ihr ein (eingehend), und sie sendet erneut, was die App noch nicht bestätigt hatte: Die App überspringt, was sie bereits gespeichert hat, sodass keine Zeile verloren geht oder doppelt gespeichert wird. Das gilt, solange der Socket innerhalb von zwei Minuten zurück ist: Die App schreibt den Heartbeat der RTE nur, solange ihr Socket offen ist, und sobald dieser Heartbeat zwei Minuten alt ist, beendet der Worker die laufenden Jobs der RTE als verwaist, auch wenn ihre Playbooks noch laufen. Halten Sie den Spool (RTE_SPOOL_PATH) auf einem Volume, damit eine neu gestartete RTE noch sendet, was sie aufbewahrt hatte. Ein Playbook, das lief, als seine RTE neu startete, kann nicht weiterlaufen: Die App beendet seinen Job als verwaist, wenn die RTE ohne ihn zurückkommt.
Abbruch. Ein Abbruch setzt das Abbruch-Flag des Jobs, und der App-Knoten, der den Socket der RTE hält, sendet innerhalb von zwei Sekunden cancel. Die RTE sendet SIGTERM an die Prozessgruppe des Playbooks, sodass ansible-playbook und jeder von ihm gestartete Fork gemeinsam anhalten, und der Job endet aborted. Ein Abbruch, der vor seinem Job eintrifft, wird eine Minute aufbewahrt, und der Job startet dann abgebrochen.
Aufräumen. Sobald der Prozess endet, unabhängig vom Ergebnis, entfernt die RTE beide Extravars-Dateien (ein Formular mit keepExtravars behält die sichtbare, nie die versteckte), die Artefaktdatei in /tmp, die Job-Protokolldatei und den eigenen Dateiordner des Jobs. Das Ende wird einmal gespeichert: Ein zweiter Schreiber, etwa die App, die einen Job fehlschlagen lässt, den die RTE bereits beendet hat, ändert nichts.
Ein Prozess pro Job, nicht ein Container pro Job. AWX und AAP starten für jeden Job einen neuen Pod oder Container aus einem Execution-Environment-Image. AnsibleForms startet nichts: Der RTE-Container läuft bereits, und jeder Job ist ein Kindprozess darin. Ein Job startet, ohne ein Image zu ziehen oder einen Pod einzuplanen, und AnsibleForms braucht keine Rechte auf Ihrer Container-Engine oder Ihrem Cluster. Im Gegenzug teilen sich die Playbooks, die auf einer RTE laufen, deren CPU, Speicher, /tmp, Home-Ordner und Python: Geben Sie dem Container genug Ressourcen für RTE_MAX_JOBS Playbooks mit ihren Forks. Um zu ändern, was ein Playbook verwenden kann, ändern Sie das Image, nicht den Job.
Die Sicherheitsgrenze
Die RTE führt Code aus, den Formularautoren geschrieben haben, daher ist sie so gebaut, dass sie so wenig wie möglich enthält:
- Nur ihr Token : Die RTE hält keine Datenbank-Anmeldedaten und kein
ENCRYPTION_SECRET. Sie nimmt keinen Aufruf an außer dem Socket der App, mit ihrem Token, und hat keine Benutzer, keine Anmeldung und keine Webseiten. Über den Socket kann sie nur die Jobs ausführen, die die App ihr sendet, über sie berichten und nach ihren Dateien fragen: Sie kann keinen Job anlegen, keinen anderen Job lesen und keine Anmeldedaten herausgeben - Versiegelte Geheimnisse : Um einen Job auszuführen, liest die RTE nie die Tabelle der Anmeldedaten oder einen Secret Store. Sie erhält die Geheimnisse der Jobs, die sie ausführt, versiegelt nur für ihr Token und diese Job-ID, sodass sich ein Bündel für eine andere RTE oder einen anderen Job nicht öffnen lässt, und ein geändertes Byte wird abgewiesen
- Eine geprüfte Verbindung : Ausgehend prüft die RTE das Zertifikat der App. Eingehend über HTTPS pinnt die App beim Beitritt das eigene Zertifikat der RTE und wählt sich nur ein, wenn sie dieses vorlegt (siehe Modi). Eine eingehende RTE über einfaches HTTP gehört in ein privates Netz, wie das des Docker Compose-Projekts
- Keine geerbte Umgebung :
ansible-playbookerhält die Umgebung der RTE ohne jede Variable, die AnsibleForms selbst liest (ENCRYPTION_SECRET,DB_PASSWORD,RTE_TOKEN,VAULT_TOKEN,ACCESS_TOKEN_SECRET, dieMYSQL_*-Variablen und die anderen), sodasslookup('env', ...)sie nicht erreicht. Was Sie für Playbooks setzen (PATH, Proxys,ANSIBLE_*), wird unverändert durchgereicht - Keine Templates von Benutzern : Ein Formularwert, der
{{,{%oder{#enthält, erreicht das Playbook als unsicherer String, den Ansible nie als Template auswertet, es sei denn, das Formular setztallowJinjaInExtravars, und keine Shell führt je die Befehlszeile aus - Nicht root : Die RTE und ihre Playbooks laufen als
node(uid 1000), sodass ein Playbook tun kann, was dieser Benutzer im Container kann, etwa seine Dateien und den SSH-Schlüssel lesen, und nicht mehr
Behandeln Sie ihren Container, ihr Image und ihre Umgebung mit derselben Sorgfalt wie die der App.
Datenbankmodus (veraltet)
Die RTE von 7.0 bis 7.2, ohne RTE_MODE, läuft die ganze Reihe 7.x hindurch und wird in 8.0.0 entfernt. Sie unterscheidet sich in vier Punkten von den obigen Schritten:
- Sie hält die Datenbank : ihre
DB_*-Einstellungen undENCRYPTION_SECRET, wenn sie sich selbst registriert (RTE_REGISTER) - Die Übergabe ist ein HTTP-Aufruf : Die App ruft
POST <address>/rte/v1/jobsmit dem Token und den versiegelten Geheimnissen auf, und die RTE übernimmt den Job, indem sie ihren Namen in der Datenbank daran schreibt (Schritte 3 und 4) - Sie schreibt die Ausgabe selbst : in die Datenbank, gebündelt, ohne Spool (Schritt 7). Ein Abbruch erreicht sie über das Abbruch-Flag des Jobs, das sie alle 2 Sekunden liest, und einen Aufruf
/cancel - Sie liest die Dateien vom Volume der App : Sie braucht dieselbe Festplatte wie die App, wie bei
RTE_FILES=local
Ihre Runner-Seite hat keine der RTE-Einstellungen: Sie liest ihre eigenen Variablen. Um sie in einen Socket-Modus zu überführen, siehe Modi.