- Ansible für automatisierte Roll-Outs – der Einstieg
- Ansible für automatisierte Roll-Outs – das erste Playbook
In einer beweglichen und agilen Cloud Umgebung wird es immer wichtiger, dass Server Systeme vollautomatisch ausgerollt werden, um den Erwartungen der Nutzer gerecht zu werden. Noch vor wenigen Jahren war es völlig normal, dass man auf einen neuen Server mehrere Wochen warten musste, aber in der heutigen Welt, wo man sich innerhalb von 5 Minuten einen virtuellen Server bei Amazon klicken kann, haben sich die Ansprüche verschoben. Selbst wenige Tage Wartezeit auf einen Server werden schon als inakzeptabel empfunden.
Daher möchte ich in dieser Serie meine Erfahrungen mit Ansible schildern und aufzeigen, welche Wege funktionieren und was man vielleicht vermeiden sollte. Alles hier Dargestellte beruht auf meinen Erfahrungen und stellt nicht „die absolute Wahrheit“ dar, sondern zeigt den Status Quo auf, an dem ich nach einigem Hin und Her angekommen bin. Wer alternative Ideen, Anmerkungen oder Verbesserungsvorschläge hat, ist herzlich eingeladen diese hier in den Kommentaren abzuladen!
Um die Systeme in der gewünschten Geschwindigkeit bereitzustellen muss man diese vollautomatisiert ausrollen können. In dieser Serie gehe ich davon aus, dass das „nackte“ Betriebssystem bereits ausgerollt ist, was durch ein Template in der Cloud Plattform oder auch durch Tools wie Foreman geschehen kann. Dieser Server hat einen SSH Zugang installiert, der das Login mit einem SSH-Key erlaubt, außerdem ist Python verfügbar. Ich konzentriere mich hier auf Linux Systeme, Ansible kann allerdings auch mit Windows Systemen umgehen.
Setup
Ansible greift per ganz normalem SSH auf die zu konfigurierenden Nodes zu. Die sogenannten Playbooks können also vom lokalen Arbeitsplatzrechner oder von einem dedizierten Host gestartet werden. In größeren Umgebungen bietet sich die Variante mit dem zentralen Ansible Host an, für die Entwicklung oder kleinere Umgebungen kann man auch vom lokalen Arbeitsplatz aus arbeiten.
Um sicherzustellen, dass bei verteilter Arbeitsweise alle auf die gleichen Playbooks zurückgreifen, sollte ein zentraler Git Server (Versionskontrollsystem) bereitgestellt werden, auf dem die Playbooks verwaltet werden.
Der Zugriff auf die Hosts per SSH hat zwei große Vorteile:
- kein Agent auf den Hosts erforderlich – nur Python und ein SSH Zugang
- durch die Verwendung der SSH Config kann man hier auch Hosts, die nur indirekt (über einen Bastion- oder Jump-Host) erreichbar sind direkt ansprechen.
Der Demo Setup
In dieser Serie wird erst einmal von einer Workstation aus gearbeitet (in meinem Fall ein Mac, Linux geht aber auch, Windows ist leider etwas komplizierter – siehe Ansible Windows Support). Die zu verwaltende Umgebung ist ein Labor Setup, der teilweise über einen Jumphost und teilweise direkt erreichbar ist.
Im direkt erreichbaren Teil der Infrastruktur lautet die DNS Domain der Hosts front.example.com
und das nicht direkt erreichbare Netz hat die Domain lab.example.com
. Auf dem Jumphost ist die Namensauflösung so konfiguriert, dass per Default in lab.example.com
gesucht wird. Wenn auf dem Jumphost als srv01
angefragt wird, so wird dies zu srv01.lab.example.com
, sodass die Kommunikation ab diesem Punkt standardmäßig über das Lab Netz geht.
Der Jumphost ist bereits fertig konfiguriert und erfordert keine weitere Einrichtung (mein Adminaccount pd
ist angelegt und mein SSH Key ist installiert. Die weiteren Hosts haben zwar eine Netzwerkconfig (IP Adressen etc.) und einen Default Login Account (deploy
), der auch sudo Berechtigungen (ohne Passwort!) hat, aber noch keine weitere Config. Der Default Login Account ist auch mit einem SSH Key ausgestattet, der in die authorized_keys von SSH eingetragen ist und das Passwort ist deaktiviert (d.h.: Login mit einem Passwort ist nicht möglich).
Los geht’s
Workstation (Mac) vorbereiten
Wir benötigen Homebrew um etwas Software zu installieren und die Pakete zu managen. Einfach der Anleitung im Link folgen.
Anschließend nutzen wir Homebrew, um python (und hier insbesondere PIP) zu installieren:
1 2 3 4 5 6 7 8 9 |
ghost:~ pd$ brew install python ==> Installing dependencies for python: sqlite, openssl ==> Installing python dependency: sqlite ==> Downloading https://homebrew.bintray.com/bottles/sqlite-3.20.0.sierra.bottle.tar.gz [...] ==> Summary /usr/local/Cellar/python/2.7.13_1: 6,339 files, 86.9MB, built in 2 minutes 12 seconds |
Jetzt installieren wir noch Ansible aus den offizielle Python PIP Repositories (dies ist im Allgemeinen die aktuellste stabile Version):
1 2 3 4 |
ghost:~ pd$ pip2 install ansible [...] Installing collected packages: ansible Successfully installed ansible-2.3.1.0 |
Inventar und SSH Verbindungen einrichten
In diesem Teil der Serie richten wir uns erst einmal häuslich ein, erstellen ein Grundgerüst für unser Playbook und stellen die benötigte SSH Konnektivität her. Im weiteren Verlauf dieser Serie bauen wir darauf auf und erstellen nach und nach ein komplexeres Playbook, welches uns die Grundeinrichtung von Linux Hosts abnimmt bzw. diese ganz erheblich beschleunigt.
Verzeichnisstruktur
Um unser Playbook auch für spätere Erweiterungen sauber zu halten, verwende ich die hier gezeigte Verzeichnisstruktur, die wir im späteren Verlauf nach Bedarf erweitern werden:
1 2 3 4 5 6 7 8 |
MeinPlaybook/ ├── README.md ├── group_vars ├── host_vars ├── inventory ├── site.yml ├── templates └── vars |
Die einzelnen Einträge in diesem Verzeichnis sind:
README.md
– eine klassische README Datei, die den Zweck und die Verwendung des Playbooks erklärt (im Markdown Format)group_vars
– ein Verzeichnis, in das Variablen und Parameter für spezifische Gruppen von Hosts abgelegt werdenhost_vars
– analog zugroup_vars
aber nur für einzelne Hostsinventory
– ein Verzeichnis für Inventarlisten. Auf Basis dieser Listen wendet Ansible die Playbooks auf die darin genannten Hosts ansite.yml
– das Haupt-Playbook File. Der name ist beliebig und es ist durchaus gebräuchlich mehrere dieser Files für unterschiedliche Aufgaben zu habentemplates
– ein Verzeichnis für Template Dateien, welche auf die Hosts ausgerollt werden sollen. Diese Templates verwenden Jinja2 als Templating Sprachevars
– ein Verzeichnis für sonstige Variablen und Parameter, die nicht pro Host oder pro Gruppe verwendet werden.
Diese Struktur habe ich in ein leeres Verzeichnis angelegt, sodass ich für neue Projekte einfach die leere Hierarchie kopieren kann, um einen Startpunkt zu haben.
Das Inventory
Als erstes erstellen wir mal ein kleines Inventory file, welches alle beteiligten Hosts enthält, auf denen etwas gemacht werden soll. Im ersten Anlauf halten wir das schön einfach: keine Gruppen, keine Spezialparameter, nur die Hosts. Inventory Files orientieren sich am INI-File Format. Hier ist also mein erstes Inventory File:
1 2 3 4 |
portal01 # direkt erreichbar control01 # nicht direkt erreichbar, nur über jumphost compute01 # nicht direkt erreichbar, nur über jumphost compute02 # nicht direkt erreichbar, nur über jumphost |
Dieses File wird einfach mit einem Texteditor erstellt und unter einem beliebigen (sprechenden!) Namen im inventory
Verzeichnis abgelegt. In meinem Fall heißt das File lab-environment
.
Alles ab einem #
in einer Zeile wird als Kommentar ignoriert. Auf jeder Zeile steht ein Hostname, was uns jetzt ein Problem bereitet: die nicht direkt erreichbaren Hosts. Um mit diesen Hosts zu kommunizieren müssen wir uns zuerst auf dem Jumphost einloggen und dann von da aus weiter verbinden:
1 2 3 4 5 6 7 8 |
ghost:Playbooks pd$ ssh compute01 ssh: Could not resolve hostname compute01: nodename nor servname provided, or not known ghost:Playbooks pd$ ssh jumphost Last login: Fri Aug 4 08:32:45 2017 from 172.21.29.15 pd@jumphost:~$ ssh deploy@compute01 Last login: Tue Aug 1 17:31:34 2017 from 10.10.4.8 deploy@compute01:~$ |
OpenSSH bietet für diesen Zweck die hilfreiche ProxyJump
Option an. Wir legen uns also einen entsprechende Eintrag in der Datei .ssh/config
in unserem Home Verzeichnis an:
1 2 3 4 5 6 |
Host jumphost ForwardAgent yes Host control?? compute?? StrictHostKeyChecking no ProxyJump pd@jumphost |
Für den Jumphost aktivieren wir per default das Agent Forwarding, was uns das Herumhantieren mit SSH Keys erspart und unsere lokalen Keys von der Workstation über den Jumphost auch an die Rechner dahinter weiterreicht.
Der Eintrag StrictHostKeyChecking no
für die Hosts hinter dem Jumphost ist der Tatsache geschuldet, dass wir in einem Ansible Run keine Möglichkeit haben, eventuelle Rückfragen von SSH bezüglich des HostKeys zu beantworten. Alternativ kann man auch einmal die entsprechende Verbindung mit dem Host manuell per ssh
herstellen, so dass der Hostkey bereits lokal vorliegt und ein Login ohne Rückfragen möglich ist.
Tests
Jetzt sollte es möglich sein, direkt per ssh deploy@compute01
zum Beispiel auf den compute01
Host zu verbinden. Die Verbindung wird automatisch durch den Jumphost aufgebaut und unsere SSH Keys werden direkt weitergereicht.
1 2 3 |
ghost:~ pd$ ssh deploy@compute01 Last login: Mon Aug 7 13:47:59 2017 from 10.10.0.1 deploy@compute01:~$ |
Wir können jetzt unseren ersten Ansible Test starten, für den wir noch kein eigentliches Playbook benötigen:
1 |
ghost:MeinPlaybook pd$ ansible all -i inventory/lab-environment -m setup -u deploy -b |
Diese eine Zeile stellt die Verbindung zu jedem einzelnen Host her und führt dort das Ansible setup
Modul aus, welches die sogenannten „Facts“ auf den Hosts einsammelt. Dieser Aufruf sollte keine Fehlermeldungen produzieren und eine Menge Output zu jedem Host ausgeben, auf den man dann später in den Playbooks zurückgreifen kann.
Im nächsten Teil dieser Serie werden wir dann ein tatsächliches Playbook aufbauen und dieses im weiteren Verlauf mit mehr Funktionen und Features ausstatten.