Automatisierung mit Ansible
Telerec't
Server im Rechenzentrum oder daheim mit Ansible automatisieren
Ansible ist die Automatisierung der Admin-Tätigkeit: Installieren, Kopieren, Patchen – Ansible setzt Ihre Befehle reproduzierbar um, auf so vielen Servern, wie Sie wollen. Gleichzeitig sind die Anweisungen eine vollständige Dokumentation. Als Beispiel bauen wir einen Server-Baukasten namens „Telerec’t“.
Login per SSH, Docker installieren, immer die gleichen Container hochfahren. Die Arbeit eines Admins kann eintönig sein. Nur nicht vertippen! Wenn Sie da keine Lust drauf haben, sind Sie nicht allein.
Da man ohnehin Computer benutzt, um Computer zu administrieren, lässt sich das alles automatisieren. Eine beliebte Software dafür ist Ansible. Stellen Sie sich das Programm wie einen etwas unselbstständigen Admin-Kollegen im Homeoffice vor. Weil der ein Roboter ist, können sie nicht normal mit ihm reden, sondern geben ihm stattdessen sogenannte „Playbooks“. Das sind To-do-Listen, die er stets fehlerfrei und in Rekordzeit abarbeitet.
Im Grunde verschiebt Ansible Know-how vom Notizzettel des Admins in ausführbare Playbooks. Der Fachbegriff dafür lautet „Infrastructure-as-Code“. Playbooks lassen sich im Unterschied zu einer Zettelwirtschaft mit Git verwalten und mit einem vertrauten Texteditor oder einer verlässlichen Entwicklungsumgebung für Programmierer editieren.

Playbooks enthalten eine geordnete Liste an Teilaufgaben („Tasks“), die Robo-Kollege Ansible abzuarbeiten hat. Für eine bessere Übersicht können Sie zusammengehörende Aufgaben in „Roles“ organisieren. Eine Menge Fehler beim Administrieren von Servern entstehen durch Copy & Paste und danach vergessene Anpassungen. Damit Sie gar nichts anpassen müssen, unterstützt Ansible Variablen und Templates.
Letztendlich passiert aber nichts anderes als ein Log-in auf dem Server mit der Secure-Shell SSH [1]. Auf dem müssen Sie also keine Client-Software installieren. Ansible führt auf dem Server nur stinknormale Shell-Skripte aus, die Sie nicht mal selbst schreiben müssen. Bei all der Admin-Magie, die wir Ihnen im Folgenden vorführen, sollten Sie nicht vergessen, dass Sie alles auch per Hand ausführen könnten. Wenn Sie und kein anderer Admin eine Idee haben, wie es per Hand geht, werden Sie es Ansible auch nicht beibringen können.
Als Beispiel automatisieren wir die Administration eines eigenen Heim- und eines Root-Servers mit Debian oder Ubuntu und unserem Baukasten „Telerec’t“ (von „tele erect“ – „aus der Ferne aufgerichtet“). Der Heimserver soll später mal Smart-Home-Dienste wie Mosquitto (MQTT-Broker) und NodeRed (No-Code Automatisierungsregeln) ausführen, der angemietete Root-Server im Rechenzentrum Wordpress (Blog-Software) und Nextcloud (Dateisynchronisation) beherbergen. SSH-Schlüssel hinterlegen sowie Basis-Pakete und Docker installieren ist für beide Server gleich. Im nächsten Artikel zu Ansible stellen wir vier Dienste vor, die in unseren Augen auf jedem Server laufen sollten. Erst danach folgen in weiteren c’t-Ausgaben die eigentlichen Bausteine in Form zusätzlicher Ansible-Roles als eigene Artikel, sodass Sie ganz individuell auswählen können, was Sie auf Ihrem Server installieren wollen.
Die Bausteine setzen wir als Git-Submodules um. Submodules sind ein eher selten genutztes Feature der Versionsverwaltung Git [2, 3]. Jedes Submodule ist ein simpler Unterordner, aber gleichzeitig auch ein eigenständiges Git-Repository. Das binden Sie so in ein Basis-Repository ein, dass dieses protokolliert, auf welchem Stand das Gesamtkonstrukt war. Zum Baukasten wird das, weil Sie die Submodules unverändert in Ihr Basis-Repository einbinden können. Sie pflegen nur dieses eine Repository, denn das bildet Ihr Setup vollständig ab. Updates in den Submodules spielen Sie aber trotzdem mit nur einem Befehl ein.
Infrastructure-as-Code
In diesem Artikel zeigen wir, wie Sie Ansible installieren und eigene Playbooks schreiben. Im ersten Playbook dieses Artikels übertragen wir kryptografische Schlüssel, damit Sie und Ansible sich danach mit der Secure Shell SSH [1] ohne Passwort am Server anmelden können. Außerdem werden wir Docker und docker-compose installieren. Docker ist meist die erste Wahl, um containerisierte Anwendungen auszuführen. In Container verpackte Anwendungen kommen sich nicht gegenseitig in die Quere. Fertige Images lädt Docker aus dem Docker Hub herunter. Falls Ihnen eine große Auswahl fertiger Images nicht wichtig ist, können sie Telerec’t stattdessen auch mit der Docker-Alternative Podman nachbauen. Wir werden für Telerec’t aber Docker benutzen.
Ein Wort der Warnung: Telerec’t ist entstanden, weil wir ein Setup für unsere eigenen Server gebraucht haben. Wir veröffentlichen das Basis-Repository und mehrere Submodules, weil diese ein hervorragendes Beispiel sind, wie Sie Ansible für Ihren Server nutzen können. Denken Sie dabei aber bitte mit, prüfen Sie, was wir machen, und tragen Sie gern Verbesserungsvorschläge als Pull-Requests an uns heran. Telerec’t ist nämlich mit all seinen Bausteinen Open Source und unsere Konfiguration haben wir unter der Lizenz GPLv3 veröffentlicht. Wir leisten keinen professionellen Support und geben keine Garantie, die Repositories in Zukunft zu pflegen. Sehen Sie das Projekt als Freie Software ohne Maintainer. Forken Sie die Repositories, falls Sie Maintainer werden wollen.
Ansible installieren
Für einen stressfreien Start bestellen Sie einen Root-Server mit einem vorinstallierten Debian oder Ubuntu. Falls Sie einen Heimserver aufsetzen, installieren Sie eines der Systeme wie gewohnt und aktivieren Sie das Log-in per SSH. Bei Raspis empfehlen wir ganz langweilig das Debian-basierte Raspberry Pi OS. Telerec’t funktioniert auf allen Linux-Distributionen mit dem Paketmanager apt. Der händische Teil der Installation endet, sobald Sie ssh-Zugang haben. Den sollten Sie testen, was Ihren Rechner nebenbei auf dem Server auch in ~/.ssh/known_hosts einträgt (Ansible trägt sich dort nicht automatisch ein).
Danach wechseln Sie zu Ihrem Admin-PC oder -Notebook mit Linux, macOS oder Windows mit dem WSL (Windows Subsystem for Linux). Dort installieren Sie Ansible – es läuft auch nur dort. Der Server bekommt nämlich gar nicht mit, dass Sie sich von Ansible helfen lassen. Sie können auch von mehreren verschiedenen Rechnern aus den Server administrieren. Dafür klonen Sie das Repository mit dem Infrastruktur-Code und geben jedem der Rechner Zugriff auf den Server.
Ansible ist in Python geschrieben, weshalb Sie es auf allen Desktopbetriebssystemen mit Python-Paketmanagern installieren können. Wir empfehlen diese Art der Installation zusammen mit pipenv, weil das automatisch ein virtuelles Environment mitverwaltet, sodass unterschiedliche Python-Projekte einander nicht in die Quere kommen können.
Alternativ: In den meisten Linux-Distributionen sind die Ansible-Pakete aus den Paketquellen aktuell genug. Wenn Sie Ansible auf diesem Weg installieren, läuft es ohne virtuelle Umgebung und Sie können pipenv run zu Beginn der folgenden Befehle weglassen. Zusätzlich brauchen Sie dann noch python-passlib, sparen sich später aber pipenv install. Wir haben den Weg über pipenv gewählt, weil Sie dann auf allen Betriebssystemen und Distributionen die aktuelle Version installieren.
Am einfachsten administrieren Sie einen Linux-Server von einem Linux-Rechner aus. Installieren Sie die Pakete für git, sshpass (fürs Einloggen mit Passwort) und pipenv beziehungsweise Ansible (falls Sie die Paketverwaltung bevorzugen). Unter Ubuntu geht das beispielsweise mit dieser Zeile:
sudo apt install git sshpass pipenv
Unter Windows raten wir zum WSL, das Sie ruckzuck installieren, indem Sie in ein cmd- oder PowerShell-Fenster mit Administratorrechten wsl --install -d ubuntu eintippen. Danach öffnen Sie Ubuntu über das Startmenü, was die Ersteinrichtung anstößt, die einige Sekunden dauert und während der Sie einen Benutzeraccount anlegen. In der Ubuntu-Konsole haben Sie danach den Ubuntu-Paketmanager apt zur Verfügung, um die restlichen Abhängigkeiten zu installieren:
sudo apt install python3 git pipenv sshpass
Mac-Nutzern empfehlen wir Homebrew, das Sie mit dem folgenden Befehl installieren:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Danach installieren Sie git und pipenv mit folgendem Befehl:
brew install git pipenv
Alternativ können Sie Ansible auf dem Mac ebenfalls mit brew installieren und pipenv run dann ebenfalls weglassen.
Die nächsten Schritte funktionieren auf allen Systemen gleich.
Privates Repository anlegen
Unser Beispiel-Setup Telerec’t fußt auf der Idee, dass Sie sich in einem privaten Git-Repository im Baukastensystem eine Konfiguration für Ihren eigenen Server zusammenbauen. Dafür brauchen Sie zunächst ein leeres privates Repository. Privat sollte das sein, damit Sie dort die Passwörter hinterlegen können. Auch bei kostenlosen GitHub-Accounts können Sie private Repositories erstellen. Falls Sie GitHub nicht vertrauen, können Sie auch mit git init ein lokales Repository anlegen und erst später mit git add remote ein Repository zum Synchronisieren hinzufügen, beispielsweise auf einem selbst gehosteten Gitea.
Um Ihnen die Arbeit an Ihrem individuellen Setup zu erleichtern, haben wir ein Beispiel-Repository erstellt, bei dem Sie sich bedienen können (siehe ct.de/yu9e). Statt mit Copy & Paste befüllen Sie Ihr Repository daraus schneller mit git:
git remote add ct-example https://github.com/ct-Open-Source/telerec-t-base git pull ct-example main git remote remove ct-example
Danach haben Sie eine Basiskonfiguration für Ansible bestehend aus den Dateien ansible.cfg und hosts sowie den Verzeichnissen group_vars/ und roles/. In letzterem befinden sich bereits Git-Submodule, die aber noch nicht geklont wurden. Das holen Sie mit folgendem Befehl nach:
git submodule update --init \ --recursive
Weil Sie den länglichen Befehl für Updates der Submodule immer mal wieder brauchen, lohnt es sich, ein Alias für die Kurzform git supdate anzulegen:
git config alias.supdate 'submodule update --remote --merge'
Nun haben Sie zwar pipenv und die nötige Verzeichnisstruktur, aber Ansible und Passlib sind noch nicht installiert. Die stehen nämlich nur als Voraussetzung in der Datei Pipfile im Repository. Die Voraussetzungen installieren Sie aus dem Basis-Ordner des Repositorys mit:
pipenv install
Setup anpassen
Das neu angelegte und mit Beispieldaten befüllte Repository müssen Sie nun an den eigenen Server anpassen. Tragen Sie dafür zunächst in der zweiten Zeile der Datei hosts die statische IP-Adresse Ihres Servers oder bei einem Heimserver optional auch dessen Hostname ein. Passend zu diesem Rechner können Sie nun Variablen in host_vars/<ip_oder_hostname>.yml anlegen. Hat Ihr Server beispielsweise die IP-Adresse 192.168.7.140, lautet der Dateiname also host_vars/192.168.7.140.yml.
Wie die meisten Konfigurationsdateien für Ansible ist auch diese Datei im YAML-Format, mit dem Sie Variablen als hierarchische Strukturen definieren können. Das folgende Beispiel müssen Sie für Ihren Server passend anpassen:
ansible_user: mariamuster
admin:
name: mariamuster
key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
email: "maria.muster@gmail.com"
locale: de_DE.UTF-8
timezone: Europe/Berlin
docker_dir: "/docker"
In der ersten Zeile steht der lokale Benutzername, der Block darunter definiert einen Account mit Systemverwalterrechten auf dem Server. Die lookup-Funktion hinter dem Schlüssel key sucht lokal, also auf dem Admin-System, im angegebenen Verzeichnis ~/.ssh/ nach dem öffentlichen Schlüssel für passwortloses Login mit ssh. Den Pfad und Dateinamen müssen Sie wahrscheinlich an die Dateinamen und Pfade an Ihrem Rechner anpassen. Moderne Schlüssel heißen oft id_ed25519.pub, ältere oft id_rsa.pub [4].
Mit der E-Mail-Adresse darunter bauen Sie schon für die Dienste vor, die wir im nächsten Artikel installieren. Wichtig ist hier zunächst nur, dass es sich um eine externe Adresse handelt, die nicht vom eigenen Mailserver auf demselben Server bereitgestellt wird.
In der letzten Zeile können Sie das Verzeichnis umstellen, in dem auf dem Server letztlich die Daten aller Dienste landen.
Struktur erklärt
Ein Ansible-Projekt besteht aus „Tasks“, die zu „Roles“ zusammengefasst sein können. Aus diesen Elementen kann man dann Programme schreiben die im Ansible-Jargon „Playbooks“ heißen. Tasks, die immer gemeinsam ausgeführt werden müssen, sollten zusammen in einer Role stehen. Die Roles sind simple Unterverzeichnisse im roles-Ordner. Playbooks können alternativ auch direkt Tasks enthalten. Solche haben wir für Telerec’t aber nicht gebraucht, weil sie in Roles stets besser aufgehoben waren.
Das Herzstück jeder Role ist der tasks-Ordner, in dem Ansible nach einer Datei main.yml sucht. Die kann mit include_tasks: auch weitere YAML-Dateien einbinden, automatisch ausgeführt werden die aber nicht. In defaults/main.yml kann man jeweils Role-spezifische Variablen definieren. Der Ordner heißt „defaults“, weil im Playbook definierte Variablen die Variablen aus main.yml überschreiben. Ansible bevorzugt stets spezifisch definierte Variablen gegenüber allgemeinen. In files/ stellt man Dateien bereit, die Tasks unverändert auf den Server kopieren (copy-Task), in templates/ landen Dateien in der Template-Sprache Jinja2, die Ansible mit dem template-Task ausfüllt und überträgt. Neben Variablen können die Templates auch if-Abfragen und Schleifen enthalten.
Mit ansible-galaxy lassen sich Roles über eine Art Paketverwaltung teilen. Das haben wir im Playbook initial-setup.yml genutzt und installieren Docker mit einer Role von Raspi-YouTuber Jeff Geerling:
pipenv run ansible-galaxy role install geerlingguy.docker
Eine so geteilte Role ist im Prinzip nichts anderes als ein Verzeichnis unterhalb von roles/. Für Telerec't haben wir einfach Git-Repositories als Git-Submodules ins roles-Verzeichnis geklont. Wir hätten sie genauso auf Ansible-Galaxy veröffentlichen können. Mit diesen beiden Methoden bauen Sie auch später weitere Bausteine in Ihr Ansible-Setup ein: Sie suchen sich beispielsweise ein passendes Repository bei github.com/ct-Open-Source (siehe ct.de/yu9e) wie 2fauth [5] aus und klonen es mit dem folgenden Befehl, den Sie im Wurzelverzeichnis des Projekts ausführen:
git submodule add https://github.com/ct-Open-Source/telerec-t-2fauth roles/twofauth git supdate
Beim Basis-Setup sind sechs Roles dabei, die in unseren Augen jeder Server braucht. Das nach roles/system geklonte telerec-t-debian überträgt den SSH-Schlüssel für passwortloses Login, fügt den Benutzer der sudo-Gruppe hinzu, installiert mit apt Pakete wie vim, wget, curl und rsync (welche genau, steht in roles/system/vars/main.yml) und legt das Verzeichnis für Docker-Volumes an. Wir haben dafür /docker/ voreingestellt, Sie können es aber über die Variable in host_vars/ auch ändern. In diesem Verzeichnis landen alle Daten, die Sie regelmäßig sichern sollten.
Docker findet sich nicht in den voreingestellten Paketquellen. Die Role geerlingguy.docker richtet zuerst die Paketquelle mitsamt GPG-Schlüsseln ein und installiert dann Docker und das Compose-Plug-in. Da die Role von Ansible-Galaxy kommt, gibt es für sie keinen Unterordner in roles/. Beide Roles stehen aber im Playbook initial-setup.yml:
- hosts: server
become: true
roles:
- system
- geerlingguy.docker
Hinter hosts: steht der Name des Servers, den Sie in der Datei „hosts“ angegeben haben. become: trueverschafft dem Benutzer auf dem Server Root-Rechte und hinter roles: folgt die Liste der Verzeichnisse in roles/, die Ansible im Zuge des Playbooks abarbeitet oder die über Ansible-Galaxy geladenen Roles ohne Verzeichnis.
Playbooks ausführen
Ansible will sich standardmäßig ohne Passwort anmelden, was noch nicht geht, wenn der öffentliche SSH-Schlüssel noch nicht hinterlegt ist. Das Playbook initial-setup.yml kopiert den Schlüssel auf den Server; um es auszuführen, müssen Sie Ansible aber anweisen, einmalig nach dem SSH-Passwort und dem sudo-Passwort zu fragen:
pipenv run ansible-playbook initial-setup.yml -i hosts --ask-pass --ask-become-pass
Ansible dokumentiert mit farbigen Ausgaben auf der Kommandozeile die Ausführung aller Tasks. Grün deutet an, dass es nichts tun musste, bei Orange war der Task erfolgreich. Fehlermeldungen markiert Ansible rot.
Der Befehl zeigt, wie mächtig Ansible in der Praxis sein kann. Mit obigem Befehl sind Dutzende Pakete installiert und Schlüssel hinterlegt. Und nichts hält Sie davon ab, zusätzlich auch beliebige Skripte auszuführen, Software zu kompilieren oder Daten umherzuschieben. Alles mit nur einem Befehl, und wenn Sie das wünschen, sogar auf Dutzenden Servern gleichzeitig.
Erfahrungsgemäß werden Sie sich schon nach kurzer Zeit Playbooks für alle Routineaufgaben bei der Server-Administration erstellen. Oft ist die Aufgabe dann mit einem einzelnen Befehl erledigt und Sie können sich wieder interessanteren Tätigkeiten zuwenden. Außerdem dokumentieren Roles und Playbooks exakt, was Sie gemacht haben. So protokollieren Sie Ihre Arbeit nicht nur für Kollegen, sondern auch für sich selbst, und stellen sicher, dass Sie auch Monate später noch nachvollziehen können, welche Schritte Sie genau benutzt haben.
Ähnlich wie das Schreiben von Dokumentation verlangt ein Ansible-Setup aber auch Disziplin. Ändern Sie nicht einfach in einer SSH-Session Konfigurationsdateien auf dem Server. Für Telerec’t müssen sie das eigentlich auch gar nicht. Wenn Sie aber trotzdem einen Hotfix brauchen, denken Sie daran, dass vermutlich eines Ihrer Playbooks den Hotfix zu einem ungünstigen Zeitpunkt überschreiben wird. Fügen Sie der betroffenen Ansible-Role deswegen auch gleich Tasks hinzu, die die gleiche Änderung auch automatisch umsetzen können. Das geht beispielsweise mit Tasks, die mit regulären Ausdrücken Textdateien für die geänderte Konfiguration editieren.
Zugegeben: Wir haben einige Stunden gebraucht, bis wir mit Ansible vertraut genug waren, um die gewohnten Wartungsarbeiten am Server vorzunehmen. Inzwischen hat sich dieser Aufwand aber ausgezahlt: Neue Dienste setzen wir mit weniger Tipparbeit auf und sparen bei jedem etwas Zeit. Durch die gute Dokumentation sind wir deutlich konsequenter beim Einrichten der Dienste, was für uns selbst und auch andere die Übersicht verbessert. Vor allem aber ist das Gefühl verschwunden, dass wir einen früheren Schritt vergessen haben könnten und der neueste Befehl alles kaputtmachen könnte. Unser Setup ist in der Versionsverwaltung gesichert und wir können eine funktionierende alte Konfiguration mit einem Checkout und einem Ansible-Durchlauf ganz schnell wieder rekonstruieren. Administrationsaufgaben reproduzierbar ausführen zu können, gibt ein Gefühl der Sicherheit, das die Einarbeitungszeit schon bald rechtfertigt. Zeitersparnisse, weil Sie Befehle nur genau einmal eintippen müssen, und die Möglichkeit, mit anderen Admins Roles auszutauschen, sind der Zuckerguss auf dem Kuchen.
Im nächsten Artikel zu Telerec’t werden wir uns der Aufgabe widmen, vier essenzielle Docker-Container aufzusetzen und zu starten. (pmk@ct.de)
Repositories: ct.de/yu9e
Ordnung im Königreich
Bild: Thorsten Hübner
Die Grundausstattung für öffentliche Server – automatisiert mit Ansible
Mit einem eigenen Server gewinnen Sie die Datenhoheit zurück. Bevor Sie praktische Dienste installieren können, brauchen Sie aber Zertifikate, einen Reverse-Proxy, automatische Updates und ein Admin-Interface. Mit unserem Server-Baukasten „Telerec’t“ setzen Sie das alles weitgehend automatisch mithilfe von Ansible auf.
Einen Befehl auf der Kommandozeile ausführen, zurücklehnen und zuschauen, wie die eigenen Wünsche umgesetzt werden. Wer seinen Server mit Ansible administriert, kann sich wie ein König fühlen. Wie Sie die ersten Schlachten für die Eroberung Ihres Admin-Reichs schlagen, haben wir in [1] erklärt. Nun gilt es den Server gegen Angriffe abzusichern und einige Helfer für die Verwaltung einzusetzen. Als Monarch lassen Sie nämlich arbeiten und legen nur fest, was Ihre Untertanen zu tun haben.
Den Weg zur Macht beschreiten Sie auf Ihrem Server mit „Teile und Herrsche“. Die Technik dazu heißt „Container“ [2]. Ein Container ist eine auf das Wesentliche zusammengestutzte Linux-Umgebung, die nur einen Dienst ausführt, den aber mit größter Verlässlichkeit. Docker kümmert sich um diese Mini-Linuxe und vernetzt sie virtuell zu einem föderalen Staat. Das ist deutlich effizienter als Virtualisierung, weil alle Container den Kernel des Host mitbenutzen. Der größte Vorteil liegt aber darin, dass Ihr Reich auf den Schultern von Giganten steht: Andere Herrscher teilen bereits optimierte Container-Images über Container-Registries wie den Docker Hub und Sie profitieren von deren Updates.

Regierungsbildung
Damit Ihre Container keinen Staub ansetzen, sollte Ihr Ansible-Reich „Watchtower“ benutzen, das selbst im Container läuft. Es hält Ausschau nach veralteten Docker-Images und ersetzt sie automatisch durch neuere. Dazu gesellt sich „Autoheal“, das regelmäßig prüft, ob alle Container ihre Arbeit verrichten. Wenn nicht, hilft Autoheal ihnen wieder auf die Beine.
Als Außenminister empfehlen wir den Reverse-Proxy „Træfik“ (im Folgenden Traefik geschrieben), der verlässlich alle Anfragen befreundeter Rechner annimmt und an die Container weiterleitet, die dafür zuständig sind. Traefik regelt auch die Transportverschlüsselung der Kommunikation, indem es SSL-Zertifikate von Let’s Encrypt beschafft und automatisch vor Ablauf verlängert.
Vielleicht befürchten Sie, dass es unübersichtlich werden könnte, die vielen Container gleichzeitig im Auge zu behalten. Zum Glück schafft „Portainer“ mit seinem Dashboard Abhilfe. Die Webanwendung listet all Ihre Container auf, erleichtert deren Konfiguration und hilft mit Logs bei der Fehlersuche. Also keine Sorge, dass Sie zu diesem Zweck Ihr Reich zu Fuß bereisen oder mit Konsolenbefehlen hantieren müssten.
Das Repository als Verfassung
Ansible steht Ihnen stets als Protokollchef zur Seite, der den Kleinkram für Sie verwaltet. Wir gehen im Folgenden davon aus, dass Sie Ansible installiert und ein Repository für Ihr Setup angelegt haben. Falls Sie das schnell nachholen wollen, finden Sie alle Infos in [1]. Ein Ansible-Setup besteht immer aus drei Bereichen: Variablen, Roles und Playbooks.
Mit den Variablen legen Sie all die Werte fest, die für Ihr Setup individuell sind. Das sind Benutzernamen, Passwörter, Pfade und Abweichungen von der Standardkonfiguration. Falls eine Variable nur einen bestimmten Server betrifft, legen Sie diese im Ordner host_vars/ in einer YAML-Datei fest, deren Name dem Hostnamen oder der IP-Adresse des Servers entspricht. In unserem in [1] beschriebenen Basis-Setup haben wir so unter anderem den Namen des Standardbenutzers und die Zeitzone des Servers in host_vars/192.168.7.140.yml festgelegt.
Gelten Einstellungen für mehrere Server, sind sie in group_vars/all.yml besser aufgehoben. Dort haben wir Abschnitte für jeden unserer Serverdienste erstellt und dort beispielsweise alle Benutzernamen und Passwörter festgelegt. Da unser Basis-Repository privat ist, können wir damit Passwörter über mehrere Rechner, von denen wir den Server administrieren, synchron halten, beispielsweise der Desktop-PC im Büro und das private Notebook. Falls Ihnen das zu unsicher ist, müssen Sie die Passwortverwaltung in eigene Dienste auslagern. Damit dieser Artikel nicht zu lang wird, haben wir uns gegen diesen Weg entschieden und riskiert, dass GitHub unsere Passwörter kennt.
In unserem Vorlagen-Repository github.com/ct-Open-Source/telerec-t-base enthält die Datei schon Blöcke mit den Variablen der vier Container, die Sie immer brauchen. Die Passwörter müssen Sie aber unbedingt ändern! Der folgende Linux-Befehl erzeugt ein Passwort mit 25 Zeichen und nutzt dafür die besten Zufallszahlen, die der Kernel erzeugen kann:
cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c25; echo
Nutzen Sie diesen Befehl, um ein admin_password für portainer, ein http_token für watchtower und ein admin-Passwort für traefik zu erzeugen. Letzteres müssen Sie für die HTTP-Basic-Authentifizierung des Webinterfaces von Traefik noch hashen und mit dem Nutzernamen kombinieren. Der folgende Befehl macht das beispielhaft für den Benutzernamen admin und das Passwort Geheimnis:
htpasswd -nb admin Geheimnis | sed -e s/\\$/\\$\\$/g
Die Ausgabe dieses Befehls fügen Sie als Variable http_basic_users im Block traefik ein.
Sie können auch Ihren Passwortmanager benutzen, um sichere Passwörter für den Server zu erzeugen. Unter Windows und macOS würden wir von vornherein einen Passwortmanager wie KeePass [2] oder Bitwarden [3] zum Erzeugen der Passwörter empfehlen (beide laufen ebenfalls unter Linux). Da Sie die meisten Passwörter nie eingeben müssen, können Sie die extrem lang erzeugen lassen. Ausnahme: Das Passwort für BasicAuth brauchen Sie für jeden Login am Traefik-Dashboard.
Mit den Roles konfigurieren Sie, welche Dienste zu Ihrem Setup gehören. Dazu können Sie sich an unseren Vorlagen bedienen und Sub-Repositories in den Ordner roles/ klonen. Das geht mit git submodule add – ein vollständiges Beispiel hatten wir in [1] angegeben. Alternativ können Sie auch ohne Sub-Repository einfach Ordner in roles/ anlegen und selbst neue Dienste konfigurieren.
Die letzte Zutat sind Playbooks, die Sie im Wurzelverzeichnis Ihres Repository anlegen. Sie sind so kurz, dass es sich nicht lohnt, sie gesondert zu synchronisieren. Hier ein Beispiel für ein Playbook, das die Role für Traefik ausführt:
- hosts: server
become: true
roles:
- role: traefik
vars:
service_cfg: "{{ traefik }}"
Die ersten drei Zeilen sind immer gleich. Danach kommt die YAML-Liste aller Roles, die das Playbook ausführt. Die Angabe hinter vars: setzt die Variablen aus dem traefik-Block definiert in group_vars/all.yml. Die Zeile weist der Variable service_cfg den Inhalt des Blocks zu und die Compose Hull ergänzt danach diesen Block. Wir nutzen solche nahezu identischen Playbooks mit genau einer Role, um Dienste bei Bedarf einzeln neu starten zu können, beispielsweise nachdem wir eine Variable geändert haben. Ein Playbook, das gleich mehrere Dienste aufsetzt, sähe genauso aus mit einer Liste mit mehr Spiegelstrichen hinter roles:.
„Aufsetzen“ und „Neustarten“ nutzen wir bei Ansible übrigens synonym. Ansible-Playbooks dienen nämlich immer dazu, einen bestimmten Zustand auf dem Server herzustellen. War ein Dienst dort nicht installiert, müssen die im Playbook definierten Roles alle Dateien und Ordner anlegen und den Dienst starten. Lief der Dienst bereits mit einer minimalen Änderung an der Konfiguration, überprüft Ansible nur, dass die Ordner und Dateien existieren und startet mit der geänderten Konfiguration neu. Wenn Sie selbst Roles schreiben, müssen Sie daran denken, dass die auch genauso funktionieren und nicht jeder zusätzliche Durchlauf einen bereits korrekten Zustand auf dem Server noch mal verändert.
Ausführung gefällig?
Unsere Erklärung der Basisdienste kommt ohne Installationsbefehle aus, weil wir alle vier Dienste bereits als Submodules im Basis-Repository definiert haben. Zur Erinnerung: Im ersten Teil des Artikels haben Sie die Submodules mit folgendem Befehl auf den Rechner geholt:
git submodule update --init \ --recursive
Danach gibt es die Unterverzeichnisse in roles/ und Sie können die Roles in Playbooks benutzen. Die Playbooks für die vier Basisdienste haben wir im Basis-Repository schon vorbereitet und Sie können die vier Dienste nacheinander hochfahren:
pipenv run ansible-playbook traefik.yml -i hosts pipenv run ansible-playbook watchtower.yml -i hosts pipenv run ansible-playbook autoheal.yml -i hosts pipenv run ansible-playbook portainer.yml -i hosts
All das geht alternativ auch in einem einzigen Befehl:
pipenv run ansible-playbook server-setup.yml -i hosts --ask-pass --ask-become-pass
Ihr Reich hat jetzt eine einwandfreie Infrastruktur. Sie dient allerdings bislang nur dem Zweck, sich selbst zu betreiben. Mit dieser Basis können Sie nun jedoch mit gutem Gewissen die Dienste installieren, für die Sie den Server eigentlich haben wollten. Nützlich finden wir beispielsweise: Nextcloud für Dateisynchronisation, Wordpress für einen privaten Blog, Vaultwarden für die Passwortverwaltung, NodeRed und Mosquitto für das Smart Home oder Wekan für private Kanban-Boards. Wenn wir in den folgenden Ausgaben solche Dienste mit Ansible aufsetzen, verweisen wir immer auf diesen Artikel, weil die Basis immer gleich ist, egal für welche dieser Dienste Sie sich entscheiden.
Unsere Hoffnung ist, dass unser Beispiel-Setup Telerec’t Ihnen nicht nur Arbeit bei der Server-Administration abnimmt, sondern Sie auch Lust bekommen, das Setup mit eigenen Roles als Submodule zu erweitern. Von Ihren öffentlichen Repositories können dann auch andere profitieren und das Baukastensystem wächst und gedeiht. Wir verlinken Ihre Submodule-Repositories gern in der Readme-Datei unseres Basis-Repository. Schreiben Sie uns einfach eine kurze Nachricht mit dem Link an pmk@ct.de. (pmk@ct.de)
Repositories: ct.de/yruq
Schaltzentrale für den Server

Weboberfläche für Ansible mit Semaphore
Pakete aktualisieren, Docker installieren, Server neu starten: Das praktische Kommandozeilenwerkzeug Ansible hilft Admins dabei, nervige Aufgaben zu automatisieren und versetzt Serverflotten in einen reproduzierbaren Zustand. Semaphore erweitert Ansible um eine praktische Weboberfläche.
Ansible kümmert sich zuverlässig um Konfigurationsaufgaben, indem es To-do-Listen abarbeitet, die im Ansible-Jargon Playbooks heißen. Auf einem frischen Server installieren Admins so in Windeseile wichtige Pakete und versetzen das System in einen klar definierten Zustand. In [1] und [2] lesen Sie eine Einführung in Ansible und wie Sie mit unserem Ansible-Projekt telerec’t eine Reihe von containerisierten Diensten auf einem Heim- oder Mietserver einrichten, die sich bewährt haben.
Üblicherweise läuft Ansible auf dem lokalen System des Administrators (Control-Host). Es kann es sich aber lohnen, Ansible auf ein dediziertes System auszulagern. Beispielsweise, wenn man möchte, dass der Server (Ziel-Host) jede Nacht prüft, ob es Updates gibt und diese einspielt. Als dedizierter Control-Host reicht eine schmale VM oder ein älterer Raspberry Pi in Ihrem Heimnetzwerk.
Das Open-Source-Projekt Semaphore erweitert Ansible um eine Weboberfläche, die als Schaltzentrale für Ihre Serverflotte dient. Für Nutzer, die mit der Kommandozeile weniger vertraut sind, erleichtert Semaphore den Einstieg in die Automatisierung mit Ansible. In diesem Artikel erfahren Sie, wie Sie Semaphore in Betrieb nehmen und damit Aufgaben auf entfernten Systemen ausführen. Ein Grundverständnis für Ansible und SSH ist dafür hilfreich.
Installation
Am einfachsten installieren Sie Semaphore auf einem Ubuntu-Host als Snap-Paket:
sudo snap install semaphore
Wir haben für unsere Testläufe Ubuntu Server 22.04 LTS genutzt, das wir von einem anderen Rechner im Netz via SSH bedienen. Snap hat den Vorteil, dass Ansible, die Datenbank BoltDB und weitere Abhängigkeiten mit im Snap-Container stecken und nicht zusätzlich installiert werden müssen. Wenn Sie Snap meiden oder eine andere Linux-Distribution als Ubuntu vorziehen, beschreibt die Semaphore-Dokumentation (ct.de/y4au) weitere Installationswege, beispielsweise mittels Docker, oder Sie laden ein Debian- oder RPM-Paket herunter, installieren Semaphore und eine kompatible Datenbank manuell.
Nach der Installation müssen Sie Semaphore stoppen und ein Benutzerkonto für den Administrator anlegen:
sudo snap stop semaphore sudo semaphore user add --admin \ --login cttest \ --name=Testuser \ --email=cttest@example.com \ --password=geheim
Ersetzen Sie cttest durch einen eigenen Benutzernamen und cttest@example.com durch Ihre E-Mail-Adresse. Setzen Sie außerdem ein sicheres Passwort. Damit Ansible seine Arbeit verrichten kann, müssen Sie Zugangsdaten für die Ziel-Hosts in Semaphore hinterlegen. Eine ungeschützte Semaphore-Instanz dient Hackern als Generalschlüssel. Wir raten deswegen dazu, Semaphore nur im lokalen Netzwerk zu nutzen und nicht in das Internet zu hängen.
Starten Sie Semaphore mit sudo snap start semaphore, rufen in Ihrem Browser http://semaphore-host:3000 auf und melden sich dann mit den zuvor konfigurierten Zugangsdaten an. Ersetzen Sie semaphore-host durch den Hostnamen oder die IP-Adresse des Servers, der Semaphore ausführt.
Semaphore anfüttern
Als ersten Schritt legen Sie ein Projekt an und benennen es. Projekte dienen in Semaphore dazu, Automatisierungsaufgaben logisch zu trennen, beispielsweise eine Produktions- von einer Testumgebung. Sie können später beliebig viele weitere Projekte hinzufügen. Jetzt begrüßt Sie die Semaphore-Weboberfläche, die mit einigen Informationen gefüttert werden will, bevor Sie Ihr erstes Playbook ausführen.
Statten Sie zuerst dem Key Store einen Besuch ab, den Sie über die Seitenleiste auf der linken Seite des Fensters erreichen. Er verwaltet Zugangsdaten für Ziel-Hosts und Git-Repositories. Ansible führt Aufgaben auf Ziel-Hosts mittels SSH aus.
Dabei sollten Sie die Authentifizierung mittels SSH-Schlüsseln stets Passwörtern vorziehen. Damit das klappt, müssen Sie private SSH-Schlüssel in Semaphore hochladen, die als Gegenstück zu den öffentlichen Schlüsseln auf den Ziel-Hosts dienen. Schützen Sie Ihre Semaphore-Instanz gut. Eine kompromittierte Instanz ist eine Art Generalschlüssel für Angreifer.
Legen Sie einen Eintrag vom Typ „SSH Key“ an, fügen den privaten Schlüssel ein und geben ihm einen Namen. Wie Sie SSH-Schlüsselpaare erstellen und verwalten, lesen Sie in [3]. Die Anmeldung erfolgt standardmäßig mit dem Benutzer root. Wenn Ansible sich als ein anderer Benutzer anmelden soll, müssen Sie bei der Erstellung des Eintrags in Semaphore den korrekten Benutzer angeben.
Für Ziel-Hosts, auf die Sie via Passwort zugreifen, legen Sie einen Eintrag vom Typ „Login with password“ an und hinterlegen dann Nutzername und Passwort. Diese Zugangsdaten können auch als Passwort für sudo dienen, wenn Ansible sich als unprivilegierter Benutzer anmeldet und Aufgaben ausführen soll, die Systemverwalterrechte benötigen, beispielsweise bei der Installation von Paketen.
Legen Sie zuletzt noch einen Eintrag vom Typ „None“ an, dem Sie einen beliebigen Namen geben können. Semaphore verlangt, dass Sie jedem Git-Repository, aus dem es die Playbooks herunterlädt, ein Eintrag im Key Store zuordnen. Das gilt auch für öffentliche Repositories, wie unser Beispiel-Repository, das keine Zugangsdaten benötigt. Dafür brauchen Sie später den „None“-Schlüssel.
Wechseln Sie anschließend zum Menü namens Inventory. Hier erstellen Sie eine Liste der Ziel-Hosts. Die Liste entspricht der Inventory-Datei, die Ansible gewöhnlich in /etc/ansible/hosts sucht oder deren Pfad Sie auf der Kommandozeile mit dem Parameter -i übergeben. Eine Inventory-Datei vom Typ „Static“ im Ini-Stil sieht beispielsweise so aus:
[hosts] 192.168.1.101 192.168.1.102 192.168.1.103
Zusätzlich müssen Sie das Inventory benennen und eine der zuvor konfigurierten Authentifizierungsmethoden angeben.
Wechseln Sie jetzt in das Environment-Menü. Environments enthalten Variablen, mit denen Sie Playbooks weiter anpassen können. Für jeden Task, den Sie mit Semaphore ausführen, müssen Sie ein Environment angeben, auch wenn Sie keine Variablen definieren wollen. Also richten Sie ähnlich wie beim „None“-Schlüssel ein leeres Environment ein. Tragen Sie dafür beim Erstellen einfach {} in den Feldern „Extra variables“ und „Environment variables“ ein und vergeben einen Namen.
Als letzte Zutat braucht Semaphore noch eine Quelle an Playbooks. Um die zu versionieren und gemeinsam zu bearbeiten ist es üblich, sie in GitHub-Repositories abzulegen. Damit Sie sich mit Semaphore vertraut machen können, haben wir ein öffentliches GitHub-Repository mit einer Reihe simpler Beispiel-Playbooks erstellt. Das können Sie natürlich auch forken, um die Playbooks anzupassen.
Legen Sie in Semaphore im Menü namens Repositories einen neuen Eintrag an. Vergeben Sie einen Namen, beispielsweise ansible-examples, fügen Sie die URL des Repository (https://github.com/ndi-ct/ansible-examples) oder Ihres Forks ein und weisen Semaphore an, den Branch namens „main“ zu nutzen. Weil es ein öffentliches Repository ist, reicht der zuvor konfigurierte Access Key „None“. Wenn Sie später mit Semaphore private Git-Repositories anzapfen wollen, müssen Sie den entsprechenden SSH-Schlüssel im Key Store hinterlegen.
To-do-Liste abhaken
Semaphore hat jetzt alle nötigen Informationen, damit Ansible loslegen kann. Erstellen Sie im Menü namens Task Templates ein neues Template, indem Sie auf „Create Template“ klicken. Für einen Testlauf bietet sich das integrierte Ping-Modul ansible.builtin.ping von Ansible an. Ping prüft, ob Ansible eine SSH-Verbindung zu den Zielservern aufbauen kann und ob Python installiert ist. Wenn nicht, gibt Ping eine Fehlermeldung aus.
Das Playbook mit dem Namen ping.yml umfasst nur wenige Zeilen YAML-Code und steckt mit im Beispiel-Repository:
---
- hosts: all
tasks:
- ansible.builtin.ping:
Semaphore unterteilt Task Templates mit drei verschiedenen Labels. „Task“ bietet sich für Administrations- und Konfigurationsaufgaben an. „Build“ ist für die Integration von Semaphore in CI/CD-Pipelines und „Deploy“ eignet sich für komplexere Softwareinstallationen, beispielsweise für einen Verbund von Docker-Containern, wie im telerec’t-Projekt.
Der Ping-Testlauf passt am besten zu „Task“. Geben Sie dem Task Template einen Namen, beispielsweise „Testlauf“. Außerdem müssen Sie den Namen des Playbooks (ping.yml), sowie das Inventory, das Repository und das Environment so wie im Screenshot auf Seite 155 definieren. Die restlichen Angaben sind optional, beispielsweise Kommandozeilenparameter (CLI Args) wie --become, wenn die Tasks Systemverwalterrechte benötigen.
Klicken Sie in der Liste der Task Templates jetzt auf den Namen Ihres Templates und anschließend auf die Schaltfläche „Run“. Die zusätzlichen Optionen „Debug“, „Dry Run“ und „Diff“ können Sie erst mal ignorieren. Sie helfen bei Testläufen und der Fehlersuche, sollte ein Playbook mal nicht funktionieren.
Jetzt können Sie sich zurücklehnen und Ansible bei der Arbeit über die Schulter schauen. Zunächst fischt Ansible das Playbook ping.yml aus dem konfigurierten GitHub-Repository. Danach führt es den Task ansible.builtin.ping aus. Wurde der Task auf dem Zielserver erfolgreich abgeschlossen, quittiert Ansible das in der Ausgabe mit ok:
TASK [ansible.builtin.ping] *** ok: [192.168.1.101] ok: [192.168.1.102] ok: [192.168.1.103]
Wiederkehrende Aufgaben
Eine Semaphore-Instanz eignet sich besonders gut, um wiederkehrende Aufgaben auf Ziel-Hosts zu automatisieren. Um das zu zeigen, nutzen wir ein simples Playbook namens updates.yml, das mit dem Paketmanager apt prüft, ob neue Updates vorliegen, diese installiert und nicht mehr benötigte Pakete entfernt:
---
- name: Update packages via apt
hosts: all
gather_facts: true
tasks:
- name: Update package cache
apt:
update_cache: yes
- name: Upgrade packages
apt:
upgrade: dist
autoclean: yes
Erstellen Sie ein weiteres Task Template nach dem Vorbild des Ping-Beispiels, aber tragen Sie diesmal bei „Playbook Filename“ den Namen updates.yml ein. Um die Aufgabe zu terminieren, müssen Sie einen Zeitpunkt bei „Cron“ eintragen, beispielsweise 03***, um die Aufgabe jeden Tag um 3 Uhr auszuführen. Wenn Sie Schwierigkeiten haben, Ihren Wunschzeitpunkt in eine Cron-Expression zu übersetzen, hilft das Onlinetool crontab guru, das wir unter ct.de/y4au verlinkt haben. Semaphore führt das Playbook ab jetzt stets zum konfigurierten Zeitpunkt aus.
Um Administratoren über den Status von Aufgaben zu informieren, kann Semaphore Nachrichten an Kanäle im Messenger Telegram verschicken. Das hat bei unserem Testlauf mit der Snap-Variante aber nicht funktioniert. Wenn Sie trotzdem auf dem Laufenden bleiben wollen, können Sie stattdessen eines der integrierten Benachrichtigungsmodule von Ansible nutzen (siehe ct.de/y4au).
Das Playbook mit dem Namen update-notification.yml in unserem Beispiel-Respository enthält eine Vorlage, um mittels Webhook eine Nachricht an einen Discord-Server zu schicken:
- name: Discord-Notification
community.general.discord:
webhook_id: "id"
webhook_token: "token"
content: "Software update complete on {{ ansible_hostname }}."
Sie müssen lediglich die Platzhalter id und token durch die ID und das Token Ihres Discord-Webhook ersetzen. Die URL, die beide Werte enthält, zeigt Discord an, wenn Sie in den Kanaleinstellungen im Menü „Integration“ einen neuen Webhook erstellen. Die ID und das Token folgen auf webhooks/ und werden durch /getrennt. Die Variable {{ ansible_hostname }} befüllt Ansible automatisch mit dem Hostnamen.
Fazit
Semaphore erweitert Ansible um eine grafische Benutzeroberfläche, mit der Sie sich im Handumdrehen eine Serverschaltzentrale einrichten. Das erleichtert den Einstieg in die Automatisierung mit Ansible und hilft dabei, die eigene Serverflotte zuverlässig in den gewünschten Zustand zu bringen. Wer eine Ansible-GUI möchte und wem Funktionen von Semaphore nicht mehr ausreichen, sollte einen Blick auf AWX werfen, das als Grundlage für die Red Hat Ansible Automation Platform dient, aber vorrangig für den Betrieb in einem Kubernetes-Cluster gedacht ist. (ndi@ct.de)
GitHub-Repository ansible-examples, Semaphore-Dokumentation: ct.de/y4au