Wer mit Ansible anfängt, erzeugt meistens funktionierenden, aber nicht unbedingt sauberen Code. Sie können jedoch Verbesserungen an bestehenden Playbooks ohne großen Aufwand umsetzen und damit Ihre Automatisierung deutlich verbessern. Wir zeigen, mit welchen einfachen Tricks Sie mehr aus Ihren Playbook herausholen und für Ordnung darin sorgen.
Administratoren verwenden verschiedenste Methoden, um stets wiederkehrende Aufgaben automatisiert ablaufen zu lassen. Während diesen Job früher Bash-, Perl- und PowerShell-Skripte erledigten, etabliert sich Ansible zunehmend als universelles und simples Automatisierungstool für praktisch alle Aufgaben einer modernen IT-Infrastruktur. Fast jeder Anwender, der zu Ansible wechselt, schreibt anfangs sehr unstrukturierten Code, der prinzipiell auf der Architektur seiner bisherigen Skripte basiert.
Ansible geht mit seinem Code ziemlich liberal um, was bedeutet, dass auch sehr schlechter Ansible-Code irgendwie funktioniert. Erschwerend kommt hinzu, dass viele Administratoren ihre Skripte allein entwickeln und pflegen, weil diese auch selten direkt mit den Skripten anderer interagieren müssen. Das sieht bei Ansible anders aus. Eine der großen Stärken von Ansible liegt darin, dass verschiedene Teams ihre Automatisierungs-Fragmente in Form von Rollen deklarieren und diese später im eigentlichen Automatisierungsjob zusammenführen.
Aber gerade weil Ansible so liberal mit seinem Code umgeht, können Anwender problemlos alte, schlechte Playbooks verbessern und teamfähig aufbauen. Ansible-Playbooks sind keine Programme im Sinne einer klassischen Programmiersprache. Ihnen fehlen absichtlich viele Elemente, die Sprachen wie Python oder Perl nutzen. Das beginnt beim prozeduralen Ablauf, geht über Verzweigungen bis hin zu Variablendeklaration und Handling. Dafür ist Ansible darauf spezialisiert, ein Playbook parallel auf Hunderten von Systemen simultan auszuführen. Dabei bleibt der Code derselbe, jedoch die Variablen ändern sich. Einer der wichtigsten Faktoren für guten Ansible-Code ist daher, wie Sie als Admin mit Variablen umgehen.
Administratoren verwenden verschiedenste Methoden, um stets wiederkehrende Aufgaben automatisiert ablaufen zu lassen. Während diesen Job früher Bash-, Perl- und PowerShell-Skripte erledigten, etabliert sich Ansible zunehmend als universelles und simples Automatisierungstool für praktisch alle Aufgaben einer modernen IT-Infrastruktur. Fast jeder Anwender, der zu Ansible wechselt, schreibt anfangs sehr unstrukturierten Code, der prinzipiell auf der Architektur seiner bisherigen Skripte basiert.
Ansible geht mit seinem Code ziemlich liberal um, was bedeutet, dass auch sehr schlechter Ansible-Code irgendwie funktioniert. Erschwerend kommt hinzu, dass viele Administratoren ihre Skripte allein entwickeln und pflegen, weil diese auch selten direkt mit den Skripten anderer interagieren müssen. Das sieht bei Ansible anders aus. Eine der großen Stärken von Ansible liegt darin, dass verschiedene Teams ihre Automatisierungs-Fragmente in Form von Rollen deklarieren und diese später im eigentlichen Automatisierungsjob zusammenführen.
Aber gerade weil Ansible so liberal mit seinem Code umgeht, können Anwender problemlos alte, schlechte Playbooks verbessern und teamfähig aufbauen. Ansible-Playbooks sind keine Programme im Sinne einer klassischen Programmiersprache. Ihnen fehlen absichtlich viele Elemente, die Sprachen wie Python oder Perl nutzen. Das beginnt beim prozeduralen Ablauf, geht über Verzweigungen bis hin zu Variablendeklaration und Handling. Dafür ist Ansible darauf spezialisiert, ein Playbook parallel auf Hunderten von Systemen simultan auszuführen. Dabei bleibt der Code derselbe, jedoch die Variablen ändern sich. Einer der wichtigsten Faktoren für guten Ansible-Code ist daher, wie Sie als Admin mit Variablen umgehen.
Code versus Variablen
Reguläre Programmiersprachen wie C++ trennen lokale von globalen Variablen. Lokale Variablen existieren dabei nur innerhalb einer bestimmten Funktion und stehen dem Hauptprogramm außerhalb des Funktionsaufrufs nicht zur Verfügung. Ansible kennt diese Abgrenzung nicht - es gibt nur globale Variablen. Deklarieren Sie Variablen im Rahmen eines Workflows, können alle zugehörigen Playbooks damit arbeiten.
Gerade weil mehrere Teams parallel an einzelnen Rollen eines Playbooks arbeiten, müssen sich die Entwickler auf eine eindeutige Benennung der Variablen einigen. Sonst laufen sie Gefahr, gleiche Variablennamen zu verwenden die sich gegenseitig überschreiben. Wenn Sie Ansible-Automation mit einem Team entwickeln, brauchen Sie als Erstes einen verbindlichen Styleguide, der die Handhabung von Task- und Rollennamen, Verzeichnisstrukturen und vor allem Variablennamen regelt. Gut hierfür eignen sich hierarchische Variablennamen, die sich auch bei Dictionary-Loops verwenden lassen. Darüber hatten wir bereits im Artikel "Selbstautomatisierung" in der Juni-Ausgabe [1] verweisen. Dabei können Sie den Namen der Rolle voranstellen, beispielsweise:
webserver.nginx.port: 80
Diese Struktur vereinfacht auch die Variablendeklaration in einer separaten Datei:
webserver:
nginx:
port: 80
directory: /var/ww/html
php:
memory_limit: 128M
Als Dictionary sieht die Struktur beispielsweise so aus:
firewall:
ssh:
port: 22
proto: tcp
zone: intern
http:
port: 80
proto: tcp
zone: dmz
vpn:
port: 1194
proto: udp
zone: intern
Der passende Task aus der Rule "Firewall" greift dann auf das Dictionary zu:
- name: Set Firewall Rules
ansible.posix.firewalld:
port: {{ item.value.port }}/{{ item.value.proto}}
zone: "{{ item.value.zone }}"
permanent: true
state: enabled
with_dictionary: "{{ firewall }}"
Wobei in diesem Beispiel der Name der Rule wie "vpn" aus "{{ item.name }}" gar nicht zum Einsatz kommt. Dieses Beispiel zeigt noch einen anderen, sehr wichtigen Punkt auf: Trennen Sie die Deklaration der Variablen vom eigentlichen Code. Schreiben Sie keine statischen Werte in den Code selbst, sondern nutzen Sie stets Variablen. Nur so gestalten Sie den Code flexibel und wiederverwendbar. Der obere Codeschnipsel lässt sich für alle Firewallkonfigurationen nutzen - zumindest fast alle. Das Beispiel ließe sich noch flexibler gestalten, wenn Sie "state: enabled" nicht statisch in den Code schreiben, sondern stattdessen in die Regelvariablen auch noch firewall.<rule_name>.state" deklarieren und im Task den state-Paramter auf state: "{{ item.value.state }" ändern. Dann kann das Skript sowohl Firewallports öffnen als auch schließen.
Ansible-Code ist nicht in Stein gemeißelt. Im Gegensatz zu anderen Programmiersprachen fallen kleine Codeänderungen sehr leicht, wie hier der Umbau von einem statischen Wert zu einer Variablen. Stehen Sie am Anfang eines neuen Playbooks, dürfen Sie durchaus während der Entwicklungsphase erst einmal statische Werte programmieren, so lange, bis Funktion und Ablauf des Playbooks funktionieren, dann lagern Sie im Folgeschritt die Variabeln aus.
Bild 1: Das Playbook "makevm.yml" erzeugt virtuelle Maschinen auf Qemu/KVM. Die Details über die Konfigurationen der VMs lagern in einer separaten Variablendatei, auf die das Template über das "vm_def_file" verweist.
Variablen richtig deklarieren
Ansible lässt sich auf zwei verschiedene Arten ausführen. Während der Entwicklungs- und Testphase eines Playbooks nutzen Sie die Kommandozeile mit "ansible-navigator". Geht Ihre Automatisierung in die Produktion über, sollten Sie die Web-UI Ansible Controller (kommerziell) oder AWX (Open Source) nutzen. Bei Playbooks mit wenigen Variablen packen Sie die Variablendeklaration temporär in ein separates YML-File, das Sie aus dem Playbook heraus via vars_files: einbinden. Sobald Sie das Playbook von der Testphase mit Ansible Navigator in die Web-UI mit AWX/AAP umziehen, entfernen Sie das "vars_file"-Statement und deklarieren die Variablen im "Variables"-Dialog des jeweiligen AWX-Job-Templates.
Wenn Sie für Ihr Playbook jedoch sehr viele Variablen deklarieren, wird das "Variables"-Feld schnell sehr unübersichtlich. Dann behalten Sie einfach das YML-File für die Variablendeklaration, binden dieses dann aber dynamisch aus dem Playbook ein. Dort geben Sie also anstatt vars_files: myvars.yml dann etwas wie vars_files: " {{ varfile }}" an und definieren diese Variable mit dem Dateinamen später im "Variables"-Dialog des Templates. Für verschiedene Vorlagen liefern Sie dann einfach unterschiedliche Dateien mit den Variablendeklarationen mit.
Dieses Konzept funktioniert auch, wenn Sie wiederverwendbare Funktionen aus Ihrem Playbook auslagern und in separaten Rollen sichern. Im Endstadium besteht ein Automatisierungsprojekt dann eigentlich nur noch aus der passenden Variablendeklaration und einem Ansible-Workflow oder einem simplen Playbook, das im Verlauf die bestehenden Rollen mit den passenden Parametern involviert.
Playbooks sind keine Workflows
Was unterscheidet ein Playbook von einem Workflow? Ein Playbook führt sequenziell eine Serie von Tasks aus: Installiere einen Webserver, erstelle die passende Konfiguration, starte den Dienst, fertig. Und für diese Aufgabe sind Ansible-Playbooks gedacht. Ein Workflow hingegen kann während seiner Ausführung verzweigen und gewisse Aufgaben nur unter Umständen und auch nur auf selektiven Systemen ausführen. Solche Aufgaben sollten Sie nicht mit einem einzelnen Playbook umsetzen, selbst wenn Ihnen die Ansible-Automatisierungssprache dazu alle nötigen Kommandos zur Verfügung stellt.
Diese "Workflow-as-Playbook-Monster" sorgen bei vielen Ansible-Anwendern für massive Probleme, da sie stundenlang laufen können und dabei viele Ressourcen und Verbindungen blockieren; dennoch reicht ein einziger kleiner Fehler, um den kompletten Ablauf abzubrechen. Auch kommen Ansible-Entwickler immer wieder auf kreative, aber nicht besonders praktikable Ideen, um Informationen aus einem frühen Part des Workflows-Playbooks für einen späteren irgendwo zwischenzulagern. Ein trauriges Beispiel waren Playbooks, die Informationen von Remote-Systemen in Dateien auf dem Ansible ausführenden Rechner selbst speicherten, sodass sie folgenden Playbooks zur Verfügung standen. Als Ansible dann Execution Environments einführte, die den Ansible-Code in temporären Stateless-Containern ausführen, funktionierte dieser Trick nicht mehr und zwang die Playbook-Entwickler zum Umprogrammieren. Und dabei ging es nicht nur um ein paar neue Zeilen Code, sondern eine komplett überarbeitete Archtiektur der Playbooks.
Das populärste "Workflow-Playbook", das in vielen IT-Abteilungen mit Ansible existiert und dort mit der Zeit zu einem Moloch heranwächst, ist das universelle "System-Update"-Playbook. Das beginnt üblicherweise mit einer ganzen Reihe von "Get"-irgendwas- und "register"-Output-Tasks, die Informationen der Remote-Systeme einsammeln und dann mit "set_fact" zwischenspeichern. Dann folgt der "if-then-else"-Teil, der aufgrund der zuvor gesammelten Informationen weitere Playbooks mit "Include-Tasks-when" ausführt.
Ein so gestaltetes Playbook läuft stundenlang, selbst wenn es im Endeffekt von den 200 Servern im ursprünglichen Inventory nur fünf tatsächlich aktualisiert. Mit einer Reihe simpler Tricks lässt sich dieser Ablauf aber einfach entwirren und in einem Workflow deklarieren. Stellenweise helfen aber auch simple Bordmittel wie "custom Facts" und Smart Inventories.
Bild 2: Ansible Controller und AWX können längere Abläufe als Workflows modellieren, die mehrere Playbooks verketten. Sie sollten nicht versuchen, die Logik eines Workflows in einem einzelnen Playbook umzusetzen.
Die richtigen Fakten schaffen
Ein Ansible-Playbook sammelt, sofern nichts anderes vorgegeben ist, zu Beginn seiner Ausführung Informationen des Zielsystems als "Facts" ein. Viele Anwender übersehen dabei, dass sie neben den vorgegebenen Systemfakten auch eigene definieren und auf dem Zielsystem speichern können. Diese lagern dort im Verzeichnis "/etc/ansible/facts.d/" als "*fact"-Dateien. Ein solches File kann mehrere Formate haben, wie beispielsweise das INI-Format:
/etc/ansible/facts.d/one.fact
[two]
three=3
four=4
[six]
seven=7
In diesem Fall erhalten Sie Ansible-Facts wie etwa:
ansible.local.one.two.three = 3
ansible.local.one.two.four = 4
ansible.local.one.six.seven = 7
Im einfachsten Fall reicht ein einziges Wort:
/etc/ansible/facts.d/updatable.fact
"true"
und folglich der Fakt ansible.local.updatable = true. Optional zum INI-Format können Sie in FACT-Dateien auch das JSON-Format verwenden. Die dritte Option ist ein ausführbares Bash-Skript, das JSON-Code zurückgibt, wie beispielsweise
Wobei Sie mit dieser Option extrem vorsichtig umgehen sollten: Arbeitet Ihr Custom-Fact-Bash-Skript zu langsam, verzögern Sie das Fact-Gathering von Ansible.
Aber wie helfen nun Custom-Facts beim Update-Playbook? Dazu erstellen Sie den ersten Schritt des Update-Workflows, den "Get-irgendwas- und register-Output"-Teil als separates Playbook. Dieses läuft nachts als Cron-Job, sammelt die benötigten Informationen und legt diese dann als Custom-Fact in den Zielsystemen ab, eben wie "update=true" oder eine komplexere Variable mit einer Liste diverser Hotfixes. Je nach interner Struktur haben Sie vielleicht verschiedene "Update-Types", etwa Systemupdate, gegenüber Datenbank- oder Applikationsupdate.
Am Ende des Tasks senden Sie ein simples Playbook hinterher, das lediglich die Fakten der Zielsysteme sammelt und diese dann im Inventory ablegt. Jetzt lagern alle nötigen Informationen dazu, was wann und wo aktualisiert werden soll, in Ihrem Inventory.
Berücksichtigen Sie dabei, dass ein simples Playbook ohne jeglichen "gather_facts"-Header-Eintrag stets alle Systemfakten einsammelt. Das kann, je nach Zielsystem und Konfiguration, recht lange dauern und den Speicherbedarf des Execution Environments belasten. Ermitteln Sie zunächst, welche Fakten Ihr Playbook überhaupt braucht und zu welchem Subset diese Informationen gehören. Ansible teilt die Fakten in Gruppen auf und erlaubt, nur die Informationen von ausgewählten Gruppen einzusammeln. Eine Übersicht, welche Facts-Gruppen Ansible kennt, finden Sie unter [2]. Ein Beispiel:
---
- hosts: myhosts
become: true
gather_facts: true
gather_subset:
- "!all"
- "!min"
- "local"
…
Für das folgende Playbook sammeln Sie in diesem Fall nur "local", also die von Ihnen definierten Custom-Facts ein. Wenn Fakten im Cache des Inventory vorliegen, können Sie nun separate Smart-Inventories aufbauen. Ein solches filtert die Host-Liste nach gegebenen Suchkriterien, wie beispielsweise dem Wert eines Custom-Facts. Nehmen wir an, Sie setzen drei verschiedene Update-Playbooks für Typ 1, 2 oder 3 ein und sichern dies im Custom-Fact "update.type". Dann finden Sich alle Typ-1-Maschinen im Smart-Inventory mit der Suchfunktion "ansible.local.update.type=1".
Haben Sie zuvor sichergestellt, dass jede Maschine nur einem Updatetyp zugeordnet ist, können Sie anschließend Ihre Update-Playbooks für die Typen 1, 2 und 3 simultan auf den jeweiligen Smart-Inventories starten. Damit läuft Ihr Update deutlich schneller als mit einem übergroßen Update-Playbook, das die Systeme sequenziell abarbeitet. Die einzelnen Playbooks fallen kleiner aus und lassen sich von separaten Teams pflegen.
Achten Sie beim Formulieren der Suchfunktionen im Smart-Inventory darauf, dass Ansible aufgrund der API-Struktur keine Fakten mit "." in der Suchleiste erlaubt und Sie diese recht unübersichtlich mit einzelnen oder doppelten Unterstrichen ersetzen müssen. Haben Sie beispielsweise den Custom-Fact "update.fact" mit dem Inhalt "type=1" definiert, lautet die Suchzeile im Smart-Inventory:
ansible_facts__ansible_local__update_type=1
Mit dieser Methode entkoppeln Sie die Datensammlung vom eigentlichen Update. Statt die benötigten Informationen mit "kreativen" Methoden irgendwo zwischenzuspeichern, legen Sie diese genau dort ab, wo sie später benötigt werden: Auf dem verwalteten System selbst.
Bild 3: Gesammelte Fakten lassen sich in Ihrem Inventory cachen und stehen somit weiteren Playbooks oder Smart Inventories zur Verfügung.
Pull statt Push
Ansible ist oftmals schneller als die automatisierten Systeme selbst. Das führt unweigerlich zu Wartezeiten und unschönen "wait-for"-Tasks in den Workflow-Templates. Optional erlaubt Ansible aber, dass ein Clientsystem ein Playbook über einen recht simplen Pull-Aufruf startet. Wenn Sie beispielsweise den folgenden populären Workflow mit mehreren Playbooks umsetzen:
- Installiere VMs (lokale Virtualisierung oder Cloud),
- warte, bis die Systeme ausgerollt sind und auf SSH-Requests antworten und
- rolle Apps und Konfigurationen aus,
lässt sich das auch anderes lösen. Fügen Sie dem "Applikation und Konfiguration-Ausrollen"-Job-Template in Ihrem Ansible-Controller- beziehungsweise AWX-Setup eine sogenannte Callback-URL mit einem Password hinzu. Ruft ein Client diese URL über ein simples Tool wie "curl" auf, startet Ansible den verknüpften Job für das einzelne Zielsystem, das den Callback ausgelöst hat. Zuvor prüft AWX/Ansible Controller jedoch, ob das rufende System auch tatsächlich zum im Template festgelegten Inventory gehört.
Jetzt bauen Sie in Ihr OS-Template nur noch ein Bash-Skript ein, das am Ende des Startvorgangs (bevorzugt via systemd) die Callback-URL des Application-Rollout Templates aufruft. Dann starten Sie lediglich das VM-Rollout-Skript ohne Workflow und Warteschleife. Direkt nach dem Start melden sich die ausgerollten Systeme selbstständig bei Ansible. Da AWX/Ansible-Controller für jeden Callback-Aufruf ein eigenes Execution-Environment aufsetzt, kann dieser Schritt parallel für alle Systeme ablaufen.
Allerdings gilt es, bei Provisioning-Callbacks einige Hürden zu überwinden, falls Sie AWX oder den Ansible-Controller auf einer Kubernetes-Umgebung einsetzen. Ansible ermittelt bei einem Callback-Aufruf, woher dieser kommt, damit es das Zielsystem identifiziert und verifiziert. Läuft die Automatisierung allerdings auf Kubernetes, steht ein Reverse-Proxy wie Nginx, Haproxy oder Traefik zwischen dem AWX-Webserver und dem rufenden System. Ansible sieht daher den Proxy als Quelle des Callback-Aufrufs statt des eigentlichen Systems. Da der Proxy natürlich nicht zum Inventory des Templates gehört, beantwortet Ansible den Callback in diesem Fall mit {"msg":"No matching host could be found!"}. Um das zu vermeiden, müssen Sie sowohl die AWX/Ansible-Controller-Konfiguration als auch die Kubernetes-Ingress-Funktion anpassen.
In Ihrem AWX/Ansible-Controller-Setup gibt es unter "Settings / Miscellaneous System settings" eine JSON-Liste der "Remote Host Headers". Im Default-Setup steht hier lediglich [ "REMOTE_ ADDR", "REMOTE_HOST" ], was voraussetzt, dass Ansible die echte Adresse des rufenden Systems sieht. Das funktioniert mit einem Proxy zwischen Ansible und Zielsystem nicht. Hier müssen Sie weitere Header-Schlüsselworte eintragen, wie etwa
"HTTP_X_FORWARDED_FOR",
"HTTP_X_ORIGINAL_FORWARDED_FOR",
"HTTP_TRUE_CLIENT_IP"
Welcher der Header letzten Endes funktioniert, hängt dann aber wiederum davon ab, wie Ihr Kubernetes-Reverse-Proxy die Anfragen des Clients weiterleitet. Hier müssen Sie die jeweilige Dokumentation Ihres Kubernetes-Setups konsultieren. Im Zweifelsfall müssen Sie Ihre globalen Kubernetes-Proxy-Einstellungen und die jeweilige Ingress-Regel anpassen. Das kann, je nach Proxy, beispielsweise so aussehen:
Neben den Variablen steuern auch die sogenannten "Tags", was Ansible wo ausführt. Dabei weisen Sie Tasks, Blöcken oder Rollen in Ihrer Automatisierung einen oder mehrere Tags zu. Wenn Sie ein Playbook ausführen, können Sie Ansible eine Liste von Tags mitgeben. Das Tool wird somit nur diejenigen Tasks ausführen, die über die passenden Tags verfügen. Leider setzen Ansible-Autoren diese Tags gerne falsch ein. Viele Programmierer schreiben sehr große Playbooks mit vielen unterschiedlichen Funktionen und entscheiden dann mithilfe von Tags, welchen Teilbereich des Playbooks sie ausführen.
Tags sollten jedoch nicht dazu verleiten, zu viel Funktionalität in ein einzelnes Playbook zu packen. Vielmehr sind Tags eigentlich dazu gedacht, Teilbereiche einzelner Rollen zu steuern. Ein Beispiel: Sie schreiben eine Rolle, um damit einen MariaDB-Datenbank-Server auszurollen. Alle Funktionen der Rolle haben dabei ausschließlich mit dem DB-Setup zu tun. Ihre Rolle kann einen frischen, leeren Server aufsetzen, einen bestehenden DB-Dump importieren oder einfach nur den DB-Server updaten, ohne die bestehenden Daten zu verändern. Genau diese Funktionen können Sie beim Rollenaufruf via Tags steuern. Standard-Tasks kümmern sich um Installation, Update und Konfiguration der Datenbank, während optionale Tasks mit Tags Funktionen wie den Import ausführen:
community.mysql.mysql_db:
name: "{{ mydb.name }}
state: import
target: "{{ mydb.dbdumpfile }}"
tag: importdb
Die richtige Ausführung
Mit der Einführung der Execution Environments sollten Sie Ihre Playbooks nur noch containerisiert laufen lassen. Das gilt auch für die Entwicklungsphase auf der Kommandozeile. Das CLI-Tool "ansible-playbook" hat somit ausgedient, weil es für die Ausführung nur die lokal installierten Bibliotheken verwendet. Beginnen Sie die Entwicklung mit einer blanken Umgebung wie unter [3], die keine Collections enthält. Während der Entwicklungsphase Ihres Playbooks nutzen Sie die Datei "collections/requirements.yml" in Ihrem Projektorder, um die benötigten Collections zur Laufzeit in die Ausführungsumgebung nachzuladen.
Führen Sie während der Entwicklungsphase Ihre Automatisierung mit "ansible-navigator run" aus, die das Environment via Podman einbindet. Läuft alles wie gewünscht, bauen Sie aus der Umgebungsvorlage und Ihrer "collections/requirements.yml" mithilfe von ansible-builder Ihr eigenes Environment. Dieses integriert alle nötigen Collections. Das spart bei der späteren Ausführung in AWX/Ansible Controller erheblich Zeit ein, weil die angepasste Umgebung sofort loslegt und den separaten Schritt für die Installation der Abhängigkeiten überspringt. Vergessen Sie aber nicht, in Ihrem fertigen Projekt die Datei "colletions/requirements. yml" zu löschen, da Ansible sonst den "ansible-galaxy collections install"-Schritt trotzdem startet.
Bild 4: Auf Kubernetes-Setups müssen Sie die Remote-Host-Header ändern, damit Provisioning-Callbacks via Reverse-Proxy funktionieren.
Tipps für Ansible-Projekte
Mit den nachfolgenden Tipps können Sie Ihre bestehende Automatisierung mit Ansible verbessern und dabei auch die Ausführungszeiten Ihrer Playbooks optimieren. In einer idealen Ansible-Welt würde ein Automatisierungsprojekt in etwa so aussehen: Das Git-Repository des Projekts enthält ein einzelnes, sehr simples "Rahmen"-Playbook, das die Automatisierung beschreibt und dazu die passenden Rollen aus anderen Repositories importiert.
Daneben liegen die benötigten Variablen wahlweise in einer eigenen Datei oder als extra Vars in der Template-Definition. Es gibt keine "collections/requirements.yml" im Projektordner, denn Sie haben dem Projekt das exakt dazu passende Execution Environment zur Seite gestellt. Dafür gibt es die Datei "roles/requirements.yml", die die standardisierten Funktionen aus anderen Git-Repositories importiert. Im Projekt existieren keine separaten Playbooks für besondere Funktionen, da diese ausschließlich aus den Rollen stammen. Schematisch könnte so ein Rahmen-Playbook wie folgt aussehen:
---
- name: Mein Playbook 1
hosts: all
Welche Zielsystem Sie ansprechen, bestimmt das Inventory, das Sie dem Template zuweisen, und nicht das Playbook selbst. Melden Sie sich immer und ausschließlich als regulärer Benutzer mit einem Ansible-Account aus dem Directory an, aber nie direkt als Root. Setzen Sie nur die Privilege Escalation für den Root-Zugriff ein: become: yes. Sammeln Sie nur die Fakten ein, die Sie unbedingt für Ihre Automatisierung brauchen:
gather_facts: true
gather_subset:
- "!all"
- "!min"
- "<nur was es unbedingt braucht>
Alternativ starten Sie das Playbook mit gather_facts auf False und sammeln Fakten gegebenenfalls zu einem späteren Zeitpunkt über das "ansible.builtin.setup" Modul ein. Arbeiten Sie mit Umgebungen wie Kubernetes, bei denen Sie sich nicht direkt mit Remote-Ressourcen verbinden, steht im Header bis hier her natürlich nur host: localhost und gather_facts: false. Je nachdem, wie viele Variablen Sie deklarieren, können diese statt in einer Datei im Extra-Vars-Feld des Templates stehen: vars_files: "{{ vars_files.yml }}". Zusätzliche Variablen einzelner Rollen lassen sich separat angeben, falls diese nicht schon zu Anfang zusammen mit allen anderen Variablen deklariert wurden:
roles:
- role.vm.rollout
- role.common
- role.dbprep
- role.mariadb
vars:
mydb.dbdumpfile: sqldump01.sql
tags: importdb
…
Fazit
Der Ansible-Weg von einzelnen individuellen Playbooks hin zu aufgeräumten Rollen ist zwar lang, aber nicht steinig. Die Automatisierungssprache und die benötigten Tools liefern ausreichend Flexibilität, damit Sie Ihre Automatisierung durchdacht strukturieren können. Im Endausbau sollte diese dann so weit wie möglich modularisiert sein, damit Sie Ihre Rollen in verschiedenen Automatisierungsprojekten verwenden können.