Einleitung

Nach knapp 8 Jahren habe ich mir gewünscht meine Infrastruktur neuzubauen und in diesem Zuge auch einen neuen Server gebaut,d er zukünftig meine Dienste bereitstellen soll und mein digitales Leben sicher, privat und komfortabel halten soll.

Eines meiner Hauptprobleme bis dato war die Dokumentation und Verwaltung meiner Systeme. Abgesehen von Security Updates musste ich alle Updates händisch machen, sodass ich kontrollieren konnte ob der Dienst weiter klappt oder es breaking changes gab.
Als Kontext ich habe bis dato Unraid genutzt, was an sich ein unglaublich gutes NAS OS ist, aber bis vor kurzem z.B. keine Snapshots geboten hat. Das hat sich mittlerweile geändert, hat aber dazu geführt, das es recht umständlich war mal eben ein Backup des gesamten System zu machen, was mich dazu bewogen hat die Updates händisch zu installieren...

However neben diesen Problemen gibt es noch andere Kleinigkeiten, wie die korrekte Dokumentation und damit Flexibilität wenn sich Anforderungen ändern.
Daher habe ich mich (und um dabei zu lernen) dazu entschieden, dass ich meine neue Infrastruktur auf Basis von OpenTofu und Ansible aufsetzen möchte. Dieser Beitrag beschäftigt sich mit Ansible. Einen Beitrag zu Tofu wird in Zukunft erscheinen.

Hauptteil

Was ist Ansible überhaupt?

Im Kern ist Ansible dafür gedacht, wiederkehrende Aufgaben zu standardisieren und zu automatisieren. Dazu gehören unter anderem Konfigurationsmanagement, Deployments und auch geordnete Abläufe über mehrere Systeme hinweg.
Die offizielle Dokumentation beschreibt Ansible folgend:
Ansible is an open source, command-line IT automation software application written in Python. It can configure systems, deploy software, and orchestrate advanced workflows to support application deployment, system updates, and more.

Der große Vorteil ist aus meiner Sicht der recht einfache Einstieg. Ansible nutzt menschenlesbare Playbooks in YAML (oder auch JSON...) und verfolgt eine agentenlose Architektur.
Auf Linux- und Unix-Systemen wird typischerweise über SSH mit bestehenden Zugangsdaten gearbeitet. Bei Windows kommen standardmäßig andere Verbindungsmechanismen wie WinRM beziehungsweise passende Connection-Plugins zum Einsatz. Das spart Wartungsaufwand, weil nicht auf jedem Zielsystem erst noch ein zusätzlicher Agent betrieben werden muss.
In meinem Kontext recht praktisch, da der Workflow für meine Infrastruktur dann folgend aussieht:

1. Ein System wird in Proxmox per OpenTofu erstellt incl. default root credentials / ssh key
2. Ansible greift mit dem SSH key auf den Server zu
3. Ansible installiert Standardsoftware auf dem System
4. Es wird die Fach-/Host-Software installiert
5. Die Credentials werden verändert und in mein Bitwarden gespeichert
6. Das System dokumentiert (neben der Ansible Dokumentation in Form des Playbooks) alles in meiner Netbox (die auch per Ansible aufgesetzt ist... Henne-Ei Problem und so ^^)
7. Cooles neues System :)

Der Workflow ist einfach umsetzbar, dokumentiert sich selbst und in meiner Netbox und gibt mir die Möglichkeit alle Systeme in einem Zustand zu halten und wenn mal was nicht funktioniert entweder einen Snapshot zu nutzen oder die ganze Maschine zu löschen und per Script neu zu installieren.

DISCLAIMER: Für Heimanwendungen nutzt Ansible eher weniger. Es ist mehr ein Hobby-Projekt alles per Ansible zu machen um zu lernen. Jedoch ist es in jedem Fall für Administratoren mit hudnerten gleichartigen Systemen eine unglaubliche Erleichterung.
In Fact werden Systeme, die bei Nodework erstellt werden, ausschließlich per Ansible verwaltet. Das hat den Vorteil, dass Änderungen immer zu gleichen Reaktionen führen und menschliche Fehler ausgeschlossen sind und vor allem nachdem das Script auf einem Testsystem funktioniert, kann in Produktion fast nichts schiefgehen. Entsprechend werden Updates per Ansible angestoßen auf einem Testsystem, dieses wird danach automatisch überprüft, ob die Kernfunktionalitäten funktionieren und danach automatisch nach Prod gebracht nachdem von jedem Kundensystem ein Backup erstellt ist.

Wie funktioniert Ansible?

Das Grundprinzip ist eigentlich schnell erklärt. Auf dem Steuerungssystem (in meinem Fall mein PC) ist Ansible installiert. Dort wird im Inventory hinterlegt, welche Hosts oder Hostgruppen angesprochen werden sollen. Anschließend beschreibt man in einem Playbook, welcher Zustand auf diesen Systemen erreicht werden soll. Ein Playbook besteht aus einem oder mehreren Plays, diese enthalten Tasks, und jeder Task ruft wiederum ein Modul auf. Genau diese Module übernehmen dann die eigentliche Arbeit, also zum Beispiel Pakete installieren, Dienste starten, Dateien verteilen oder Benutzer verwalten.

Praktisch ist dabei, dass Ansible deklarativ arbeitet. Man beschreibt also eher das gewünschte Ergebnis als jeden einzelnen manuellen Zwischenschritt. Damit hängt auch einer der wichtigsten Begriffe zusammen: Idempotenz. Wenn ein System bereits in dem Zustand ist, den das Playbook vorgibt, soll Ansible beim erneuten Ausführen nichts mehr verändern. Gerade bei wiederholten Änderungen, Deployments oder Wartungsaufgaben ist das extrem hilfreich, weil Abläufe nachvollziehbarer und reproduzierbarer werden.
Das heißt in unserem Fall: Wenn man ein Tool ausrollt per Ansible und das Playbook erneut aufruft, soll danach nicht nochmal alles heruntergeladen werden sondern Ansible merkt, dass alles schon so ist wie es sein soll. Dies MUSS aber bei der Erstellung der Playbooks beachtet werden.

Inventory, Module und Playbooks – die drei Grundlagen

Das Inventory ist im Grunde die Liste aller Systeme, die verwaltet werden sollen. Diese Hosts können in Gruppen organisiert werden, etwa webserver, dbserver oder test. Zusätzlich lassen sich Variablen hinterlegen, zum Beispiel Benutzer, Ports oder Verbindungstypen. Ansible kann ein Inventory statisch aus Dateien aufbauen, unterstützt aber auch dynamische Inventories, etwa aus Cloud-Quellen.

Module sind die eigentliche Arbeitskraft von Ansible. Die Dokumentation beschreibt sie als Code-Einheiten, die Ressourcen steuern oder Befehle ausführen können. Genau dadurch muss man viele Dinge nicht mehr per unübersichtlichem Shell-Skript lösen, sondern kann sauber mit dafür vorgesehenen Modulen arbeiten. Das macht Playbooks in der Regel lesbarer und wartbarer.

Zuletzt kommen die Playbooks, die alles vereinen. Das Playbook definiert die Zielsysteme und greift dann auf die Module zu, die am Ende die Aktionen durchführen.

Potenziell kann man noch weitergehen und sowas wie Rollen, Tasks, Handler etc. erklären und damit tiefer einsteigen, das ist aber für jetzt gerade noch nicht wichtig.

Wofür eignet sich Ansible besonders gut?

Ansible spielt seine Stärke vor allem dann aus, wenn mehrere Systeme konsistent behandelt werden sollen. Klassische Beispiele sind Paketinstallationen, Konfigurationsänderungen, das Starten oder Neustarten von Diensten, das Anlegen von Benutzern oder das Ausrollen von Anwendungen. Auch Einzelaufgaben sind möglich. Dafür gibt es Ad-hoc-Kommandos, also schnelle Einzeiler für einen einmaligen oder seltenen Zweck.

Gerade in kleineren und mittleren Umgebungen ist das aus meiner Sicht ein sehr angenehmer Mittelweg. Man bekommt wesentlich mehr Struktur als mit einer Sammlung aus Shell-Skripten, ohne sofort in hochkomplexe Plattformen einzusteigen zumal für mein Dafürhalten auch die Nutzung von YAML / JSON (Ansible kann beides) einfacher für dritte zu verstehen ist, als ein Shellscript.

Ein kleines Beispiel

Um das alles ein wenig Verständlicher zu machen ein einfaches Beispiel. Wir haben jetzt 2 Dateien, ein Inventory mit unseren Systemen und ein sehr einfaches Playbook, das dafür sorgt, dass NGINX auf dem System installiert und aktiv ist.

Zuerst unsere inventory.ini.
Diese besteht aus einer Gruppendefinition (websetver) und 2 Systemen die in dieser Gruppe sind (web01 und web02). Die beiden Systeme starten mit dem jeweiligen Namen und danach der IP Adresse.

[webserver]
web01 ansible_host=192.168.1.10
web02 ansible_host=192.168.1.11

Danach das Playbook mit 2 einfachen Schritten:
1. Installiere NGinX - Wobei der Befehl NICHT ist "Installiere NGINX" sondern Ansible sagt eher "Prüfe bitte ob NGINX installiert ist (state: present) und wenn nicht, dann installiere es bitte.
2. Starte und aktiviere nginx service - Der zweite und letzte Schritt macht nichts anderes als zu sagen, dass der NGINX Dienst 1. gestartet sein soll (state: started) und der Service aktiviert sein soll (enabled: true).

- name: Nginx auf Webservern bereitstellen
  hosts: webserver
  become: true

  tasks:
    - name: Nginx installieren
      ansible.builtin.package:
        name: nginx
        state: present

    - name: Nginx Dienst starten und aktivieren
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true

Das Playbook wird dann folgend ausgeführt:

ansible-playbook -i inventory.ini webserver.yml

Bitte darauf achten, dass erstens die Zugangsdaten (hier keys) im Identity Management des Systems geladen sind, sonst funktioniert der Zugriff nicht und das die beiden Dateien im selben Ordner sind.
Sofern nicht bekannt ist, wie SSH Keys verwendet wird, kann das hier nachgelesen werden.

Nützliches nebenbei

Um Ansible zu verstehen, muss man mit dem Tool arbeiten. Es gibt hier keinen Shortcut, wenn man es verstehen will. Aber es gibt ein paar Features, die praktisch sind und es nutzt wenn man diese sofort kennt. Dazu gehören z.B:

- Rollen -> Playbooks können in Rollen aufgeteilt werden, die wiederrum Tasks, Handlers, Templates etc. enthalten können
- Ansible Vault -> Ein sehr cooles Feature ist ansible Vault - Man kann damit eine Vaultfile verschlüsseln, was es ermöglicht Zugangsdaten sicher zu speichern. Dazu gibt es den Befehl: ansible-vault create secret.yml um ein neues Vaultfile zu erstellen (kann man natürlich auch selber machen...), ansible-vault encrypt credentials.yml - Um das Vaultfile zu verschlüsseln und decrypt für das Gegenteil...
Es gibt noch ein paar mehr, aber das ist das wichtigste. Die Nutzung erfolgt dann im Playbook indem man beim Aufruf den Parameter --ask-vault-pass angibt.
- Host- und Groupvars -> Ansible unterstützt die Verwendung von Variablen. Diese können für ein oder mehrere Systeme gelten und ggf. das Verhalten der Playbooks nach eigenem Wunsch beeinflussen.
- Templates -> Ansible kann Templates auf Basis von Jinja2 Verwenden und damit z.B. Konfigurationsdateien vorbereiten und ausrollen.

Wo liegen die Grenzen?

Man muss verstehen, wie Inventories, Variablen, Berechtigungen und Module zusammenspielen. Außerdem wird nicht automatisch jeder Task gut, nur weil er in YAML geschrieben ist. Wer am Ende doch fast alles nur über rohe Shell-Kommandos abbildet, verschenkt einen Teil der Vorteile. Meistens ist es sinnvoller, passende Module zu verwenden und Playbooks sauber zu strukturieren.

Auch die Verbindung zu den Zielsystemen sollte man vernünftig planen. Unter Linux ist SSH der übliche Weg, bei Windows sieht die Welt etwas anders aus, weil dort standardmäßig andere Verbindungsmechanismen genutzt werden. Das ist kein Problem, aber etwas, das man von Anfang an im Hinterkopf behalten sollte.

Und das mit ABSTAND wichtigste: Achtet bei den Playbooks auf die oben erwähnte Idempotenz! - Es bringt nichts, wenn sich das Playbook bei jedem Aufruf anders verhält. Wenn das Script durchgelaufen ist und man es erneut startet, sollte changed = 0 sein. Ansonsten sollte es gute Erklärungen geben, warum dem nicht so ist.

Wenn Playbooks nicht idempotent aufgebaut sind, können bei jeder erneuten Ausführung unerwartete Nebenwirkungen entstehen. Statt den gewünschten Zustand lediglich zu überprüfen und bei Bedarf herzustellen, werden Änderungen immer wieder neu angewendet. Das kann dazu führen, dass Dienste unnötig neu gestartet werden, Konfigurationsdateien mehrfach überschrieben oder sogar inkonsistente Zustände erzeugt werden. In komplexeren Umgebungen steigt damit auch das Risiko von Ausfällen, weil scheinbar „harmlose“ erneute Runs plötzlich produktive Systeme beeinflussen. Idempotenz sorgt genau dafür, dass ein Playbook beliebig oft ausgeführt werden kann, ohne dabei unbeabsichtigte Änderungen zu verursachen.

Schluss

Unterm Strich ist Ansible für mich eines der Werkzeuge, die sehr schnell einen praktischen Nutzen bringen können. Schon mit relativ wenig Aufwand lassen sich viele Aufgaben vereinheitlichen, dokumentieren und reproduzierbar machen. Gerade der Einstieg über lesbare YAML-Dateien ist angenehm, weil die Denkweise recht nah an der eigentlichen Aufgabe bleibt: Welcher Zustand soll am Ende auf dem System vorhanden sein?

Wer regelmäßig an Servern, VMs, Containern oder Netzwerkkomponenten arbeitet, sollte sich Ansible auf jeden Fall einmal ansehen. Für seltene Einzelaktionen reichen oft Ad-hoc-Kommandos, für alles Wiederkehrende sind Playbooks jedoch der eigentliche Hebel. Genau darin liegt aus meiner Sicht der größte Vorteil: weniger Handarbeit, weniger Abweichungen und am Ende ein deutlich kontrollierbarer Betrieb.