Das überlichtgeschwinde Provisionierungstool Ansible eignet sich nicht nur für Konfigurations- und Releasemanagement mittelgroßer Serverfarmen, sondern auch für den Hausgebrauch, zum Restaurieren von Anpassungen auf dem Linux-Desktop daheim.
Online PLUS
Im Screencast demonstriert Michael Schilli das Beispiel: https://www.linux-magazin.de/Ausgaben/2015/10/plus
Etwa alle fünf Jahre ist es in den Perlmeister-Labs an der Zeit, eine neue Linux-Distribution zu installieren. Nur alle fünf Jahre deshalb, weil es meiner Erfahrung nach mit einem Wahnsinnsaufwand verbunden ist, nach dem »do-release-upgrade« all die kleinen Cronjobs und Webapplikationen wieder zum Laufen zu bringen, die ein solches Upgrade wegen neuer Versionen von Perl, Apache und diverser Libs anscheinend zwangsläufig mit sich bringt.
Viele Open-Source-Entwickler – sowohl in der Distributionsszene (Hallo, Ubuntu!) als auch im Tool-Bereich (Hallo, Open SSL!) – scheren sich einen feuchten Kehricht um Rückwärtskompatibilität und ich darf mir jedes Mal ein paar Tage Urlaub nehmen, um fluchend an meinem Linux-Desktop zu sitzen und Konfigurationen zu ändern oder Skripte anzupassen.
Oft erinnere ich mich dann auch nur noch vage an all die vielen kleinen von Hand eingefügten Patches, die notwendig waren, um das eine oder andere Tool endlich zur Zufriedenheit zum Laufen zu bringen. Auch Wochen nach dem Upgrade ist es nicht ungewöhnlich, etwas zu finden, das noch nicht ordnungsgemäß funktioniert, und ich zermartere mir das Hirn, um herauszufinden, wie ich das vor Jahren gelöst hatte.
Handbetrieb lachhaft
Server von Hand einrichten, das löst ja bei Profis schon seit vielen Jahren Lachkrämpfe aus. Werkzeuge wie Puppet oder Chef erledigen dort Konfiguration und neue Releases automatisch und zuverlässig. Wer seine Änderungen in die versionierten Konfigurationsdateien dieser Tools einträgt, hat zudem noch den Vorteil, dass alles jederzeit fehlerfrei reproduzierbar ist.
Leider geht jedes dieser Tools mit einem religiösem Kult Hand in Hand, dessen Jünger sich anscheinend lebenslang verpflichten müssen, kostenpflichtige Trainings zu absolvieren, Zertifizierungen abzulegen und ja nicht von der verbotenen Frucht zu naschen, nach deren Verzehr der Abtrünnige sich dann vielleicht fragen würde, ob es tatsächlich sinnvoll ist, sich vom kreativen Skripthacker zum willfährigen Anpasser von Konfigurationsdateien nach einem vorgegebenen Schema umschulen zu lassen.
Deswegen freute es mich während der Lektüre des neuen und sehr empfehlenswerten Ansible-Buches [2] zu erfahren, dass dieses freie Provisionierungswerkzeug [3] nicht nur keinen Agenten auf dem Zielsystem erfordert, weil es sich einfach mittels SSH dorthin verbindet, sondern auch nach unten skaliert, also auch für die rechenzentrumslose Oma Meume ohne viel Brimborium einen einzigen Desktop verwalten kann.
Ursprung Science-Fiction
Der Name Ansible kommt aus der Science-Fiction-Literatur und bezeichnet ein Gerät, mit dem man in Überlichtgeschwindigkeit über Galaxie-Entfernungen hinweg kommunizieren kann. Wikipedia kommentiert lapidar, unsere Wissenschaftler wüssten zurzeit noch nicht, wie ein solches Gerät zu bauen wäre. Wer will, kann der Ansible-Religion beitreten und seine Anweisungen im Yaml-Format schreiben, handgestrickte Perl-Skripte funktionieren aber auch.
Modernen Linux-Distributionen liegt Ansible als Paket bei. Unter Ubuntu installiert es sich zum Beispiel mit:
sudo apt-get install ansible
Mit dem Ping-Modul und dem heimischen Desktop »localhost« als Ziel aufgerufen bestätigt es, dass alles in Ordnung ist und nichts verändert wurde:
$ ansible localhost -m ping
localhost | success >> {
"changed": false,
"ping": "pong"
}
Nun zur Praxis: Eine einfache, aber ständig wiederkehrende Aufgabe nach der Neuinstallation meines Systems zuhause ist es zum Beispiel, eine maßgeschneiderte ».vimrc« -Datei ins Homeverzeichnis zu platzieren. Glücklicherweise liegt diese öffentlich zugänglich in einem meiner Repositories auf Github (Abbildung 1), sodass Ansible jetzt das Herunterladen automatisieren und der Datei nach der Installation auch gleich noch die voreingestellten Nutzerrechte geben kann. Da die Datei, die den Sollzustand des Systems beschreibt, zusammen mit vielen anderen Ansible-Skripten in einem Verzeichnis liegt, ist die Wiederherstellung der Vim-Konfiguration nun fester Bestandteil der Systemrestauration, den garantiert kein schussliger Admin vergisst.
Spiel mit
Seine Instruktionen bezieht Ansible aus so genannten Playbooks, die im Yaml-Format beschreiben, welche Kommandos auszuführen sind und welche Dateien die Kommandos modifizieren, sodass später feststellbar ist, ob eine Anweisung schon vorher abgearbeitet wurde und nun keiner neuen Anstrengung mehr bedarf. Rufe ich Ansible also zweimal hintereinander auf, sollte der zweite Aufruf ganz schnell ohne Änderungen zurückkehren. Diese Idempotenz ist sehr hilfreich.
Listing 1
vimrc.yml
01 --- 02 - name: Dev Tools Setup 03 hosts: localhost 04 tasks: 05 - name: Vimrc from Github dotfiles 06 get_url: > 07 url=https://raw.githubusercontent.com/mschilli/dotfiles/master/.vimrc 08 mode=0644 09 validate_certs=no 10 dest=~/.vimrc
Um zum Beispiel die besagte Datei ».vimrc« zu installieren, nutzt das Playbook in Listing 1 das standardmäßig mitausgelieferte Ansible-Modul »get_url« , das laut Dokumentation mindestens folgende Parameter erwartet:
- »url« : Download-URL einer Datei,
- »dest« : Name der lokal installierten Datei und
- »mode« : Unix-Nutzerrechte.
Dieses Playbook läuft auf allen Linux-Distributionen und Plattformen, die Ansible unterstützt, hilft also auch unterwegs auf meinem Macbook.
Das Modul »get_url« wird bei einem zweiten Lauf mit der Standardeinstellung keinen erneuten Download einleiten, weil die lokale Datei schon existiert. Wer sie stetig auffrischen möchte, weist Ansible mit dem zusätzlichen Parameter »force=yes« dazu an, die Datei bei jedem Aufruf neu aus dem Internet zu laden und die lokale Version dann zu ersetzen, falls sie von der frisch eingetroffenen abweicht.
Weiter führt Listing 1 mit »localhost« den Zielhost auf, und die Yaml-Datei hat noch viel Platz für mehrere solcher “Plays”, die jeweils aus verschiedenen “Tasks” bestehen, denen jeweils mit »name« ein Name zugeordnet ist. Dieser Name gibt bei der Ausführung mit »ansible-playbook vimrc.yml« – wie in Abbildung 2 gezeigt – darüber Auskunft, was gerade passiert. Im Fehlerfall hilft er bei der Ursachensuche. Der Ausgabe ist zu entnehmen, dass Ansible ».vimrc« ohne Probleme heruntergeladen und installiert hat und dass sich deswegen der Status des Linux-Desktopcomputers verändert hat.
Die längliche Github-URL zum direkten Laden der neuesten Version der dort eingecheckten Datei fand ich übrigens im Browser auf Github.com unter dem »Raw« -Button.
In neuem Gewand
Als Nächstes steht beim Anpassen einer Linux-Distribution typischerweise ein Lauf meines Skripts »binlinks« an, das seinerseits in einem lokal geklonten Git-Repository liegt und eine Vielzahl weiterer praktischer Perl-Skripte, die ihrerseits in Unterverzeichnissen desselben Repository liegen, mit Einträgen im »bin« -Verzeichnis unter meinem Homedirectory verlinkt.
Mein Perl-Skript-Template-Generator »tmpl« , mit dem ich beinahe jedes Projekt starte, ist so zum Beispiel einfach von der Kommandozeile als »tmpl« verfügbar, da mein Kommandopfad »$PATH« das Verzeichnis »bin« in meinem Homeverzeichnis mit einschließt.
Listing 2
binlinks
01 #!/usr/local/bin/perl -w
02 use strict;
03 use File::Basename;
04 use Sysadm::Install qw(mkd);
05
06 my($home) = glob "~";
07 my $home_bin = "$home/bin";
08
09 while(<DATA>) {
10 chomp;
11
12 my($linkbase, $src) = split ' ', $_;
13
14 $src = "$home/$src";
15 my $binpath = "$home_bin/$linkbase";
16
17 if(-l $binpath) {
18 warn "$binpath already exists";
19 next;
20 } elsif (-e $binpath) {
21 warn "$binpath already exists, " .
22 "but not a link!";
23 next;
24 }
25
26 if(! -d dirname($binpath)) {
27 mkd dirname($binpath);
28 }
29
30 symlink $src, $binpath or
31 die "Cannot link $binpath->$src ($!)";
32 }
33
34 __DATA__
35 binlinks git/myrepo/binlinks/binlinks
36 tmpl git/myrepo/tmpl/tmpl
Wie Listing 2 zeigt, erzeugt das Skript einfach für jedes unterhalb des »__DATA__« -Bereichs am unteren Ende aufgelistete Skript einen Symlink, der auf die Position des Originals im Repository zeigt. Der Aufruf »ansible-playbook binlinks.yml« lässt das Playbook in Listing 3 mit dem Ansible beiliegenden »command« -Modul ablaufen, das wiederum das Skript »binlinks« aufruft und mit einer Erfolgsmeldung zurückkehrt, falls das Skript seinerseits mit dem Returncode »0« abschließt.
Listing 3
binlinks.yml
01 --- 02 - name: Tools setup part 2 03 hosts: localhost 04 tasks: 05 - name: binlinks script 06 command: ./binlinks
Mit einer Kombination aus dem von Ansible unterstützten Template-Mechanismus zum Modifizieren von Dateien und weiteren Skripten kann ich nun auch kompliziertere Abläufe wiederholen, wie zum Beispiel die Reparatur der bislang bei jedem Upgrade zerstörten Nagios-Konfiguration. Obwohl selbst Ansible nicht hellsehen kann und nur die anno dazumal ausgeführten Schritte wiederholt, hilft es doch ungemein, wenigstens zu wissen, welche Eingriffe zum Installieren notwendig waren, damit ich sie später, wenn auch leicht angepasst, mit Ansible wiederholen kann.
Virtuelle Welten
Eine weitere Konfigurationsorgie steht immer dann an, wenn ich zum Beispiel ausprobieren möchte, ob ein gerade in der Entwicklungsumgebung fertiggestelltes Perl-Modul auch auf einem Produktionssystem mit Apache-2-Server und »mod_perl2« -Beschleuniger fehlerfrei läuft. Am besten eignet sich dazu ein Reinraum, der ausgehend von einer blanken Linux-Distribution in einer Vagrant-VM [4] nur die absolut notwendigen Pakete installiert, den Server entsprechend konfiguriert und das Perl-Modul ebenfalls hineinpflanzt.
Damit sich der Entwickler bei diesem Verfahren nicht die Finger wund tippt, liegen die Instruktionen hierfür maschinenlesbar im Source-Repository, Ansible führt sie bei jeder neu erzeugten VM genau gleich aus. Das Vagrant-File in Listing 4 setzt auf einer aktuellen Ubuntu-Distribution auf, die sich Vagrant bei Bedarf aus dem Internet holt. Weiter sorgt die Direktive »forwarded_port« dafür, dass der innerhalb der VM auf Port 80 lauschende Apache-Server dies auf dem Hostsystem unter Port 8080 tut.
Listing 4
Vagrantfile
1 VAGRANTFILE_API_VERSION = "2" 2 3 Vagrant::configure(VAGRANTFILE_API_VERSION) do |config| 4 5 # 32-bit Ubuntu Box 6 config.vm.box = "ubuntu/trusty64" 7 config.vm.network "forwarded_port", guest: 80, host: 8080 8 end
Zunächst muss Ansible allerdings erst einmal wissen, unter welchem SSH-Port und mit welchem privaten Schlüssel es sich mit der frisch durch »vagrant up« hochgefahrenen VM verbinden kann. Der Aufruf von »vagrant ssh-config« offenbart, dass die Ubuntu-VM über SSH-Port 2222 auf dem lokalen Host erreichbar ist und unter welchem Pfad der private SSH-Schlüssel zur Identifizierung auf der Festplatte liegt. Die unten angeführte Ansible-Konfigurationsdatei setzt diese Werte für Ansible und stellt damit sicher, dass das Tool sich in der neu erzeugten VM einloggen und dort die vordefinierten Arbeiten verrichten kann.
Wird »ansible-playbook« aus dem gleichen Verzeichnis aufgerufen, stellt es zum Verbindungsaufbau die folgenden Werte ein:
[defaults] hostfile = hosts remote_user = vagrant private_key_file = /home/mschilli/.vagrant.d/insecure_private_key host_key_checking = False
Es fährt gut damit. Die Datei, die diese Angaben enthält, heißt »ansible.cfg« .
Stetige Kontrolle
Bei neu geschriebenen oder aufgrund von Bugfixes leicht modifizierten Softwareprojekten stellt sich immer die Frage, ob das System nach dem Fix auch noch funktioniert. Eine Unit-Testsuite räumt schon mal besonders peinliche Fehler aus dem Weg, aber letztendlich Klarheit verschafft nur der Test in einer Produktionsumgebung.
Listing 5
ModPerlTest.pm
01 package ModPerlTest;
02 use strict;
03 use warnings;
04
05 use Apache2::RequestRec ();
06 use Apache2::RequestIO ();
07 use Apache2::Const -compile => qw( OK );
08
09 sub handler {
10 my( $r ) = @_;
11
12 $r->content_type( 'text/html' );
13 print "<H1>Ansible did it!</H1>\n";
14
15 return Apache2::Const::OK;
16 }
17
18 1;
Ob der »mod_perl« -Handler in Listing 5 zum Beispiel funktioniert, ist mit Sicherheit nur unter einem Apache-Server mit installiertem »mod_perl« -Modul zu sagen. Der Entwickler muss dazu nicht nur die entsprechenden Distributionspakete wie »apache2« und »mod-perl2« installieren, sondern anschließend auch noch den Webserver unter dem Pfad »/modperltest« mittels einer Konfigurationsdatei – wie in Listing 6 gezeigt – auf »mod_perl« einnorden.
Listing 6
mod-perl-test.conf
01 <Location /modperltest> 02 SetHandler perl-script 03 PerlResponseHandler ModPerlTest 04 </Location>
Ansible hilft auch hier. Listing 7 zeigt das Playbook für einen Apache-2-Server mit installiertem »mod_perl2« -Modul und der Testapplikation (Listing 5), die das Playbook beim Aufruf von »ansible-playbook mod-perl.yml« in der VM installiert, die per »vagrant up« hochgefahrenen wurde. Der Parameter »apt« gibt mit den Schlüsseln »name« die Namen der Ubuntu-Pakete an, die es zu installieren gilt.
Listing 7
mod-perl.yml
01 --- 02 - name: mod_perl Test Application 03 hosts: testserver 04 tasks: 05 - name: Apache and mod_perl 06 apt: name=apache2,libapache2-mod-perl2 update_cache=yes 07 sudo: True 08 - name: Test script 09 copy: src=ModPerlTest.pm dest=/usr/lib/perl5/ModPerlTest.pm 10 sudo: True 11 notify: restart apache 12 - name: mod_perl conf 13 copy: > 14 src=mod-perl-test.conf 15 dest=/etc/apache2/sites-enabled/mod-perl-test.conf 16 sudo: True 17 handlers: 18 - name: restart apache 19 command: sudo apachectl restart
Der Parameter »update_cache=yes« veranlasst Ubuntu vorher dazu, die neuesten Indexdateien der Repositories einzuholen, was zwar sehr viel Zeit kostet, aber bei einer frischen Installation unumgänglich ist, da der Cache einer frischen Ubuntu-VM immer veraltete Informationen birgt, die dazu führen, dass ein nachfolgendes »apt-get install« oft die benötigten Pakete nicht findet.
Da Ansible normalerweise nicht als »root« läuft, das Kommando »apt-get install« aber Rootrechte benötigt, aktiviert Zeile 7 in Listing 7 den Sudo-Modus. So kann jede Task ihre Rechte feinjustieren und nichts läuft unnötigerweise mit gefährlich viel Feuerkraft. Außerdem kopiert das Playbook in Listing 7 die Apache-Konfigurationsdatei nach »/etc/apache2/conf« . Dank des Aufrufs von »http://localhost:80/modperltest« innerhalb der VM oder dank des Port-Mappings von Listing 4 auf Port 8080 auf dem Hostrechner springt die Mod-Perl-Applikation an, die die Ausgabe erzeugt, die in Abbildung 3 zu sehen ist.
Zudem sorgt Ansible dafür, dass der Apache-Server neu startet, falls sich seine Konfiguration oder der Sourcecode der Applikation geändert hat. Die Direktive »notify: restart apache« aus Zeile 11 löst immer dann den ab Zeile 18 definierten Restart-Mechanismus via »apachectl« aus, wenn der Entwickler wieder eine neue Version der Applikation eingespielt hat.
Fit für 2020
Mit diesen Skripten wird das Upgrade auf Ubuntu 21.04 im Jahre 2020 zweifelsohne wie am Schnürchen klappen. Da die Ansible-Instruktionen in einem Git-Repository liegen, kann ich sie über die kommenden Jahre ständig pflegen und erweitern, beim nächsten Upgrade wird auch garantiert nichts versehentlich ausgelassen. Voraussetzung ist natürlich, dass Ansible in der 2020 vermutlich aktuellen Release 7.42 noch immer die Syntax der Version 1.5.3 von anno 2015 beherrscht, was ja auch nicht selbstverständlich ist!
Infos
- Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2015/10/Perl
- Lorin Hochstein, “Ansible: Up and Running”: O’Reilly, 2015
- Ansible: https://github.com/ansible/ansible
- Michael Schilli, “Entspannt nach draußen”: Linux-Magazin 05/13, S. 94, https://www.linux-magazin.de/Ausgaben/2013/05/Perl-Snapshot










