ADMIN

2024

06

2024-05-30T12:00:00

Datenbanken

PRAXIS

044

Automatisierung

Ansible

Automation-as-Code mit Ansible

Selbstautomatisierung

von Andreas Stolzenberger

Veröffentlicht in Ausgabe 06/2024 - PRAXIS

Mit dem Automatisierungstool Ansible lassen sich sehr einfach IT-Szenarien "as Code" umsetzen. In diesem Workshop nutzen wir strukturierten YAML-Code, um damit Ansible selbst in Form von AWX auszurollen. Dabei kommt es auf korrekt formulierte Playbooks ebenso an wie die richtige Abstimmung mit der jeweiligen Kubernetes-Anwendungsplattform.

Ein großer IT-Trend heute lautet "Everything as Code". Die komplette Konfiguration von Hard- und Software befindet sich dabei in Textdateien und ein Automatisierungstool rollt alles aus. Zum Glück kommen hierbei keine undurchschaubaren Dateiformate wie sendmail.cf mehr zum Einsatz, sondern gut lesbare Formate wie YAML und vielleicht noch JSON. Informationen in solchen Dateien lassen sich meistens verlustfrei in eines der anderen Formate umwandeln.
YAML ist das präferierte Format des Automatisierungstools Ansible, das wir in diesem Workshop für die As-Code-Automatisierung verwenden. Dank seines modularen Aufbaus kann Ansible so ziemlich alles im Rechenzentrum und der Cloud steuern. Für unsere Zwecke verwenden wir das Tool, um sich selbst auszurollen. Klingt komisch, ergibt aber Sinn. In unserem Automation-as-Code-Workshop rollt Ansible auf der Kommandozeile ein komplettes AWX-Setup auf Kubernetes aus und konfiguriert den AWX-Server mit allen benötigten Einstellungen.
Der vorgestellte Code kann Ihnen als "Skeleton", also Vorlage für eigene, umfangreichere As-Code-Szenarien dienen. Und natürlich auch für solche, bei denen Sie andere Applikationen ausrollen und konfigurieren. Weil es sich hierbei um eine ganze Menge Programmzeilen handelt, können wir in diesem Workshop nur Fragmente daraus zitieren und detailliert beschreiben. Den kompletten Code finden sie auf GitHub [1]. Im Übrigen funktioniert er sowohl mit dem freien AWX als auch (mit ein paar kleineren Änderungen) mit der kommerziellen Implementierung Ansible Controller.
Ein großer IT-Trend heute lautet "Everything as Code". Die komplette Konfiguration von Hard- und Software befindet sich dabei in Textdateien und ein Automatisierungstool rollt alles aus. Zum Glück kommen hierbei keine undurchschaubaren Dateiformate wie sendmail.cf mehr zum Einsatz, sondern gut lesbare Formate wie YAML und vielleicht noch JSON. Informationen in solchen Dateien lassen sich meistens verlustfrei in eines der anderen Formate umwandeln.
YAML ist das präferierte Format des Automatisierungstools Ansible, das wir in diesem Workshop für die As-Code-Automatisierung verwenden. Dank seines modularen Aufbaus kann Ansible so ziemlich alles im Rechenzentrum und der Cloud steuern. Für unsere Zwecke verwenden wir das Tool, um sich selbst auszurollen. Klingt komisch, ergibt aber Sinn. In unserem Automation-as-Code-Workshop rollt Ansible auf der Kommandozeile ein komplettes AWX-Setup auf Kubernetes aus und konfiguriert den AWX-Server mit allen benötigten Einstellungen.
Der vorgestellte Code kann Ihnen als "Skeleton", also Vorlage für eigene, umfangreichere As-Code-Szenarien dienen. Und natürlich auch für solche, bei denen Sie andere Applikationen ausrollen und konfigurieren. Weil es sich hierbei um eine ganze Menge Programmzeilen handelt, können wir in diesem Workshop nur Fragmente daraus zitieren und detailliert beschreiben. Den kompletten Code finden sie auf GitHub [1]. Im Übrigen funktioniert er sowohl mit dem freien AWX als auch (mit ein paar kleineren Änderungen) mit der kommerziellen Implementierung Ansible Controller.
Zwei Wege zum automatisierten AWX
Um AWX automatisiert auszurollen, gibt es zwei Ansätze: Beim ersten setzen Sie einen leeren AWX-Server auf, konfigurieren ihn grafisch über seine Web-UI und exportieren im Anschluss die komplette grafisch erstellte Konfiguration als JSON- oder YAML-Datei. Diese lässt sich im Gegenzug auf einen neuen, leeren AWX-Server hochladen. Diesen Weg haben wir im Groben bereits in einem Artikel aus dem Jahr 2022 [2] beschrieben. Er kommt vor allem dann zum Einsatz, wenn Sie ein Setup klonen oder auf eine neue Plattform oder ein neues Release (Ansible Tower zu Ansible Controller) migrieren möchten.
Für den As-Code-Ansatz eignet sich diese Herangehensweise aber weniger, denn die exportierten YAML-Dateien sind recht groß und unübersichtlich. Bei AWX-as-Code möchten Sie ja ihre AWX-Konfiguration direkt in YAML erstellen und dann automatisiert ausrollen können. Daher sollte der hier verwendete Code simpler, übersichtlicher und für den Administrator lesbar bleiben.
Der zweite Ansatz setzt daher ein Playbook ein, das die Konfigurationsdaten aus einem YAML-Dokument bezieht. Das formatieren und erweitern Sie nach ihren eigenen Vorgaben. Damit ist das YAML-Dokument deutlich einfacher, jedoch nicht mehr zum exportierten Format kompatibel. Aber das As-Code-Konzept sieht ja ohnehin vor, stets aus dem Code die Installation auszurollen und nicht den entgegengesetzten Weg einzuschlagen.
Code versus Config
Eine Grundregel bei der Verwendung von Ansible sieht vor, dass in ihrem Ansible-Playbook keine Zuweisungen von Werten zu Variablen stehen – das würde die Flexibilität massiv einschränken. Bewahren Sie daher jegliche Variablendeklarationen stets separat vom eigentlichen Ansible-Code auf.
In unserem Fall sind das zwei gesonderte Dateien: "vars.yaml" beherbergt die komplette Konfiguration des auszurollenden AWX-Servers. In "secrets.yaml" sichern Sie Passwörter und Keys, die Sie für die Credentials ihres AWX-Services benötigen.
Sobald Sie alle Zugangsdaten und Passwörter in lesbarer Form in das YAML-File eingetragen haben, verschlüsseln Sie "secrets.yaml" mit Ansible Vault. Erst bei der Ausführung des Codes entriegeln Sie den Zugriff mit der Option "--ask-vault-pass".
Bild 1: Je nach Kubernetes-Distribution wählen Sie eine OpenShift- oder – wie hier – eine Ingress-Route für den Zugriff auf den AWX-Dienst aus.
Hierarchische Variablen und Dictionaries
Die Konfigurationsvariablen für unser As-Code-Szenario gruppieren wir zum einen hierarchisch, beispielsweise:
scm:
   name:"Github"
   benutzer: "user01"
   url: "https://www.github.com"
Das Playbook greift dann auf Variablen wie "{{ scm.url }}" zu. So ist von vornherein klar, zu welchem Teilbereich eine Variable gehört. Wollen Sie später den Code erweitern, können Sie jederzeit Untervariablen hinzufügen.
Das zweite Variablenkonzept ist das sogenannte Dictionary. Dabei handelt es sich um ein nicht nummeriertes Array, das Ansible in einer Loop für jedes Element ausführen kann, hier ein Beispiel:
user:
   kirk:
     vorname: "James T"
     rang: "Admiral"
   spock:
     rang: "Captain"
   uhura:
     vorname: "Nyota"
     rang: "lieutenant"
Ein Ansible-Task durchläuft nun mit
"with_dictionary: "{{ user }}"
oder gemäß der moderneren (aber nicht unbedingt besseren) Notation als
"loop: "{{ user | dict2items }}"
eine Schleife. Innerhalb eines Loop-Tasks greift Ansible dann über die hierarchische Schleifen-Variable "items" auf die Teile des Dictionary zu. Für den ersten Lauf eines Tasks mit der Loop wäre der Variableninhalt nach diesem Beispiel folgender:
item.key = "kirk"
item.value.vorname = "James T"
item.value.rang = "Admiral"
Bei der Deklaration eines Dictionary müssen nicht alle Hierarchien einer Variable deklariert sein, wie das am Beispiel des "Spock"-Eintrags der Fall ist. Allerdings muss der Task des Playbooks dann auch damit klarkommen, wenn eine Variable leer ist. Ansible kennt hierfür eine "default"-Funktion, die wie folgt aussieht:
- ansible.builtin.debug:
msg: "Vorname : {{ item.value.vorname | default('Mister') }}"
Sollte in diesem Fall die Variable "item.value.vorname" leer sein, setzt Ansible dafür "Mister" ein. Dieses Konzept kommt später beispielsweise zur Anwendung, um die SSH-Ports der Inventory-Einträge zu konfigurieren.
Mit diesen simplen Tipps zur Formatierung von YAML-Dictionaries und deren Auswertung in Ansible via Loops können Sie nun prinzipiell die Konfiguration jeglicher Hard- und Software via Ansible erledigen. Der Rollout von AWX wie in diesem Beispiel ist nur eine von vielen Möglichkeiten.
Route vs. Ingress
OpenShift und MicroShift verwenden "Name based Routes", um HTTP- beziehungsweise HTTPS-Traffic an Applikationen in Pods weiterzuleiten. Dabei lösen Sie ganz einfach in Ihrem DNS alle Unterdomains mit derselben IP-Adresse wie Ihren Kubernetes-Knoten (oder Cluster-Virtual-IP) auf. Läuft Ihr Single-Node-Setup beispielsweise auf "kube.demo.com" mit der IP-Adresse 192.168.1.1, lösen Sie alle Sub-Domains (*.kube.demo.com) ebenfalls zu 192.168.1.1 auf.
Der Router ihrer Kubernetes-Installation kann dann anhand des Namens die Pakete zum Dienst weiterleiten. Ein AWX-Setup könnte dann beispielswiese auf "http:// awx01.kube.demo.com" laufen und ein anderes entsprechend auf "http://awx02.kube.demo.com". Im Democode auf GitHub finden Sie daher im Playbook "01a_awx_ kube_route.yml" eine Routendefinition mit
apiVersion: route.openshift.io/v1
kind: Route
Das funktioniert jedoch nur bei OpenShift oder MicroShift. Ab der Kubernetes-Version 1.19 haben die Entwickler das Routenkonzept von OpenShift übernommen und dort als Ingress-Route implementiert. Daher gibt es im Democode ein zweites Playbook "01a_awx_kube_ingress.yml", das eine Ingress-Route deklariert:
apiVersion: networking.k8s.io/v1
kind: Ingress
Das funktioniert dann auch bei Micro-K8s oder K3s. Eventuell müssen Sie die Ingress-Definition anpassen, falls Router wie Traffic damit nicht direkt klarkommen.
Listing 1: Persistenter Speicher für AWX und PGSQL
spec:
...
     postgres_storage_requirements:
          requests:
               storage: 10Gi
      projects_persistence: true
      projects_storage_size: 10Gi
Den jeweiligen Task beziehen Sie aus dem Haupt-Playbook heraus via "include" ein. Je nachdem, welche Kubernetes-Distribution Sie verwenden, kommentieren Sie "Route" oder "Ingress" aus. Passend dazu müssen Sie auch im Playbook "99_ cleanup.yml" den Abschnitt "Route" oder "Ingress" nutzen.
AWX in fünf Minuten
Für AWX benötigen Sie eine Kubernetes-Umgebung. Dabei genügt bereits ein simpler Single-Node-Aufbau mit Distributionen wie MicroShift, K3s oder Micro-K8s. Auf dieser Grundlage installieren Sie dann den AWX-Operator mit den Standardvorgaben über Kustomize oder ganz einfach mit Helm, wie es unter [3] in der Dokumentation steht. Für diesen Workshop kam die Operator-Version 2.12.2 zum Einsatz.
Das Haupt-Playbook für den AWX-Roll-out "00_awx_full.yml" ist aufgebaut wie ein Workflow-Template in AWX selbst. Es bezieht an den passenden Stellen weitere Playbooks ein. Korrekterweise sollten Sie hier eigentlich in bester Ansible-Manier nicht einfach weitere Playbooks per "include_tasks" übernehmen, sondern dazu passende "Roles" erstellen.
Weil die zu inkludierenden Tasks jedoch so einfach sind und keine eigenen Variablen, Files oder Templates außer den allgemeinen Konfigurationsvariablen verwenden, wäre der Aufwand mit Rollen aber einfach zu groß und unübersichtlich. Sollten Sie jedoch planen, einen AWX-Server zum Ausrollen weiterer AWX-Server zu verwenden, bauen Sie das Playbook "00_ awx_full.yml" in ein entsprechendes Workflow-Template um.
Das Playbook bezieht alle Variablenwerte aus den beiden Dateien "secrets.yml" und "vars.yml". Dann definiert es Shell-Variablen (environment) mit den Zugangsdaten zum Kubernetes-Cluster und dem auszurollenden AWX-Setup, die von den folgenden Playbook-Modulen übernommen werden. Um das Beispiel einfach zu halten, geben wir dem Playbook einfach die kubeconfig-Datei zur Authentisierung mit.
In größeren Installationen würden Sie hier natürlich nur ein Bearer-Token mit einem eingeschränkten Zugriff auf den definierten Namespace festlegen. Der AWX-Dienst läuft in diesem Szenario ohne SSL. Hier wählen Sie in einem produktiven Umfeld das HTTPS-Protokoll aus und deklarieren eine Route oder Ingress mit SSL
Was zu Beginn allerdings fehlt, ist das "CONTROLLER_PASSWORD". Gemäß der Dokumentation des AWX-Operators könnten Sie beim Rollout eines AWX-Setups ein eigenes Password als Kubernetes-Secret hinterlegen. In unseren Tests hat das aber leider aus unbekannten Gründen nicht funktioniert. Also lassen wir die Deklaration weg, sodass der AWX-Operator beim Rollout selbst ein zufälliges, sicheres Password für das AWX-Setup generiert. Das lesen wir dann einfach zu einem späteren Zeitpunkt aus und fügen es dem Environment nachträglich hinzu.
Das erste Playbook "01_awx_kube.yml" braucht noch keine AWX-Credentials, denn es rollt erst einmal nur den AWX-Dienst auf Kubernetes aus. In diesem Rollout fordert das Playbook, wie in Listing 1 zu sehen, zwei statt wie üblich nur ein Persistent Volume (PV).
Mit "postgres storage requirements" geben Sie die Größe des PVs der Datenbank an. Diesen Parameter brauchen Sie unbedingt, denn per Default weist der AWX-Operator der Datenbank nur 1 GByte zu und das läuft in einer aktiv genutzten AWX-Umgebung in kurzer Zeit voll. Das zweite Volume nutzt der Controller, um die Projektdaten, also die Git-Repositories mit Playbooks, in einen nichtflüchtigen Speicher zu sichern. AWX funktioniert grundsätzlich auch ohne persistenten Projektspeicher, muss dann aber bei einem Pod-Neustart oder einem erneuten Deployment erst einmal alle Projekt-Repositories komplett neu einlesen. Mit Persistent Volumes geht dieser Task schneller.
Darauf folgt, wie im Abschnitt "Route vs. Ingress" beschrieben, je nach genutzter Kubernetes-Variante eines der Playbooks, das die OpenShift- oder Ingress-Route zu Ihrem AWX-Setup festlegt. Danach heißt es warten. In der Praxis werden Sie leider an vielen Stellen ihrer Ansible-Playbooks Tasks einbauen müssen, die auf irgendetwas warten. Ansible ist oftmals schneller als die Dienste, die es automatisiert. Das eigentliche Konfigurations-Playbook "02_awx_config.yml" beginnt daher mit einer Warteschleife – siehe Listing 2.
Listing 2: Warten auf die AWX-API
 Wait for the AWX Api to be accessible
     ansible.builtin.uri:
         url: "http://{{ awxsetup.name }}.{{ awxsetup.baseurl }}/api/v2/ping"
     register: result
     until: "result.status == 200"
     retries: 30
     delay: 10
Dabei lassen wir das URI-Modul von Ansible in vorgegebenen Abständen von zehn Sekunden die Rest-API von AWX prüfen. Die API kennt eine Ping-Funktion, die mit dem HTTP-Code 200 "OK" antwortet, sobald die AWX-Umgebung bereit ist. Sollte AWX nach fünf Minuten immer noch nicht reagieren, bricht das Playbook mit einem Fehler ab. Die Ping-API antwortet, ohne dass Sie sich am AWX-Server anmelden müssen, denn zu diesem Zeitpunkt kennen wir das Admin-Passwort noch nicht. Wer wie oben beschrieben HTTPS für den AWX-Server einsetzt, muss das im Warteschlangen-Task entsprechend anpassen.
Auf die Warteschleife folgt eine zweite, statische Warteschleife von 30 Sekunden. Leider hat sich in der Praxis gezeigt, dass die Ping-API gerne zu früh ein OK zurückliefert und die folgenden Konfigurations-Tasks sie dann doch nicht erreichen können. Geben wir der API weitere 30 Sekunden Zeit, treten hier keine Fehler auf.
Wenn die API reagiert, ist das AWX-Roll-out vollständig und der Operator hat auch das Admin-Password in einem Kubernetes-Secret hinterlegt. Der nächste Task holt sich – siehe Listing 3 – das Passwort aus dem Secret und stellt es den weiteren Schritten zur Verfügung.
Listing 3: Passwort abholen
- name: Get Admin PW
     kubernetes.core.k8s_info:
          api_version: v1
          kind: Secret
          name: "{{ awxsetup.name }}-admin-password"
          namespace: "{{ awxsetup.namespace }}"
      register: awxadmin
Hier müssen wir nun wiederum einen kleinen Trick bemühen: Ansible legt Umgebungsvariablen entweder für den kompletten Lauf der Automatisierung fest. Dann stehen sie am Beginn des Playbooks vor dem Abschnitt "tasks". Alternativ kann Ansible auch jedem einzelnen Task Umgebungsvariablen zuweisen, die dann aber auch nur für den jeweiligen Task gelten. Eine Funktion in der Art "add key/value to existing environment" gibt es nicht.
Also fassen wir die nun folgenden Konfigurationstasks in einem "block" zusammen. Da dieser selbst als Task gilt und wir dem Block die Umgebungsvariable "CONTROLLER_PASSWORD" hinzufügen, steht sie damit wie in Listing 4 automatisch allen Tasks innerhalb des Blocks zur Verfügung.
Listing 4: Tasks in einem Block zusammenfassen
- name: Upload Config to AWX block:
  block:
   - name: Create Organization
                 awx.awx.organization:
... ((viele Config-Tasks )) ...
   environment:
           CONTROLLER_PASSWORD: "{{ awxadminresources[0].data.password | b64decode }}"
Um die AWX-Konfiguration hochzuladen, setzt das Playbook die Collection "awx.awx" ein. Die Module der Collection adressieren die Rest-API von AWX und erzeugen darüber die gewünschten Einstellungen. Dabei müssen Sie aber die richtige Reihenfolge einhalten. Sie können beispielsweise kein Job-Template erstellen, solange das zugehörige Project-Template nicht existiert und mit seinem SCM synchronisiert wurde.
Und das Projekt lässt sich nur dann erzeugen, nachdem Sie zuvor sowohl eine Organization als auch passende SCM-Credentials angelegt haben. Die richtige Reihenfolge lautet: Organisation / Credentials (SCM, Machine, optional: Registry) / optional: Registry und Execution Environments / Inventory / Inventory hosts (falls statisch, sonst dynamisches Inventory ) / Project (zwingend mit "Update on Launch") / Job Templates / und danach optional: Workflow-Templates.
Unser Beispiel erzeugt nur eine Organisation mit einem Projekt und mehreren Job-Templates. Das Konzept lässt sich aber nach Belieben erweitern – beispielsweise, wenn Sie die Konfigurationen für mehrere Projekte in separate Dateien packen, den Teil des Playbooks ab "awx .awx.project" wiederum in eine eigenen Datei auslagern und in einer Loop nach der Art "with items: projekt1.yml, projekt2.yml" einbeziehen.
Organizations richtig anlegen
Gleich beim Aufruf des ersten Moduls "awx.awx.organization" lauert eine Falle: Wenn Sie auf AWX einen Task starten, generiert das System zuerst einen Kubernetes-Pod mit dem Execution-Environment, das dann die eigentliche Automatisierung durchführt. Das Standard-Container-Template hierfür (in der Regel "quay.io/ansible/awx-ee:latest") bringt dabei nur sehr wenige Ansible-Collections mit.
Um nun in Ihrem Playbook Module aus Collections wie "ansible.windows", "containers.podman" oder "kubernetes.core" nutzen zu können, muss das Execution-Environment diese erst laden. In ihrem Projektorder listet die Datei "collections/ requirements.yml" diejenigen Collections auf, die das Execution-Environment direkt nach dem Start einfügt. In der Regel beziehen Sie dabei frei verfügbare Collec-tions von galaxy.ansible.com. An diesem Dienst müssen Sie sich vor der Nutzung nicht anmelden.
Alternativ gibt es aber Repositories für Collections, die eine Anmeldung erfordern. Und hier gibt es nun einen kleinen Bug in AWX: Einer "Organisation" müssen Sie immer mindestens ein Credential vom Typ "Ansible Galaxy/Automation Hub API Token" zuweisen, selbst wenn Sie das für den offenen Ansible-Galaxy-Dienst eigentlich nicht brauchen.
Legen Sie nun händisch eine neue Organisation in der Web-UI von AWX an, bekommen Sie davon nichts mit, denn AWX fügt automatisch die Pseudo-Credentials "Ansible Galaxy" ein, falls Sie nichts anderes auswählen.
Der Haken: Erzeugen Sie eine "Organization" über die Rest-API, wird AWX hier eben nicht im Hintergrund tätig und die Credentials bleiben leer. Das führt dann dazu, dass ein Execution-Environment, das in dieser Organization läuft, keine Collections nachladen kann.
Leider gibt es dabei dann keine aussagekräftigen Fehlermeldungen, die Sie auf diesen Missstand hinweisen. Daher lautet der passende Eintrag in unserem Playbook wie in Listing 5. Die Angabe "galaxy_credentials" muss dabei zwingend in diesem Task stehen.
Listing 5: Organisation mit Credentials anlegen
  - name: Create Organization
     awx.awx.organization:
          name: "{{ org.name }}"
          description: "{{ org.desc }}"
          galaxy_credentials: - Ansible Galaxy
          state: present
In den folgenden Tasks legt unser Playbook die wichtigsten Basis-Credentials an. Das muss leider in separaten Tasks nacheinander erfolgen, da sich verschiedene Credential-Typen nur schwer in einer "Credentials Loop" zusammenfassen lassen. Dazu sind die zu übergebenden Parameter je nach Typ zu unterschiedlich.
Sie können natürlich Credential-Dictionaries für einander ähnliche Typen bauen und in einer Loop zusammenfassen, beispielsweise für alle Anmeldeinformationen, die nur eine Kombo aus Benutzernamen und Password brauchen.
Der Beispielcode kopiert Ihren Standard-SSH-private-Key aus "~/.ssh/id_rsa" in das Machine-Credential. Diese Zeile müssen Sie ändern, wenn ihr AWX-Setup einen anderen Key verwendet.
Danach wird es relativ einfach. Die weiteren Tasks erzeugen nacheinander die gewünschten Ressourcen auf dem AWX-Setup. Im Beispiel generieren wir ein Inventory mit statischen Hosts, wie das üblicherweise für die Standard-Infrastrukturmaschinen des LAN der Fall ist.
Hierbei setzen wir den bereits erwähnten Trick mit der Default-Funktion ein, wie Listing 6 zeigt.
Listing 6: Default-Funktionen einsetzen
- name: Create Inventory Hosts
  awx.awx.host:
          name: "{{ item.key }}"
          description: "{{ item.value.description }}"
          inventory: "{{ inventory.name }}"
          state: present
          enabled: true
          variables:
               ansible_host: "{{ item.value.ip }}"
               ansible_port: "{{ item.value.port }}"
  default('22') }}"
  with_dict: "{{ inventory_host }}"
Aus dem Dictionary in "vars.yml" liest der Task die Informationen zum Host wie Name und IP-Adresse aus. Sollten Sie dort bei einigen Hosts keinen Port-Parameter angeben, füllt Ansible das Feld einfach mit "22" aus.
Als letzten Schritt legt der AWX-Rollout dann ihre Job-Templates an. Dies funktioniert natürlich nur dann, wenn die in den Job-Templates angegebenen Playbooks tatsächlich in ihrem Projekt-Git existieren und beim Anlegen des Projekts korrekt synchronisiert wurden.
Unser Workshop-Code enthält lediglich den As-Code-Rollout, ein Projekt mit Templates müssen Sie selbst beisteuern.
Bild 2: Unser Playbook rollt einen AWX-Server auf Kubernetes aus und lädt dessen komplette Konfiguration via API hoch.
Noch mehr Automatismus
In der Praxis benötigt die hier vorgestellte Automatisierung keine fünf Minuten, um einen vollständig funktionstüchtigen AWX-Server aufzusetzen. Nachdem Sie die Files "vars.yml" und "secrets.yml" angepasst haben, starten Sie den Code einfach mit:
ansible-playbook 00_awx_full.yml –ask-vault-password
Hier können Sie nach wie vor das "alte" CLI-Tool "ansible-playbook" verwenden, da das modernere "ansible-navigator"-Werkzeug leider kein interaktives "--ask-vault-password" beherrscht.
Natürlich lässt der Code viel Raum für Erweiterung zu. Auf der einen Seite können Sie dem Beispiel noch Elemente hinzufügen, um neben Job- auch Workflow-Templates anzulegen. Am Anfang des Rollouts lassen sich Tasks addieren, die das Basissetup von AWX, beispielsweise LDAP- und Directory-Integration, einfügen.
Wie eingangs erwähnt, können Sie das Playbook "00_awx_full" auch als Workflow in einem bereits bestehenden AWX-Server modellieren. Dieses lässt sich dann über Callback oder Webhook an ein Git-Repository mit ihrer As-Code-Deklaration anbinden und schon läuft das Ansible-via-Ansible-Rollout als Gitops-Pipeline, die Sie über einen simplen Commit auslösen.
Fazit
Strukturiertes YAML und Ansible machen es den Administratoren leicht, komplette IT-Umgebungen als Code zu beschreiben und automatisiert auszurollen. Das vollautomatisierte AWX-Setup ist dabei erst der Anfang. In AWX lassen sich dann die As-Code-Setups mehrerer Systeme zu Workflows verknüpfen: Erst der Rollout der physischen oder virtuellen Systeme, dann die Basissoftware gefolgt von den Anwendungsdaten. Unser Democode hat beispielsweise "Luft nach unten". Das verwendete MicroShift-Szenario samt AWX-Operator lässt sich problemlos nach demselben Prinzip ausrollen.
(ln)
Link-Codes
[1] Automatisierter AWX-Rollout: https://github.com/astolzen/awx_auto