Aus Linux-Magazin 10/2015

Automatische Desktopkonfiguration mit Ansible und Perl

© Rainer Plendl, 123RF

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.

Abbildung 1: Auf Github steht die maßgeschneiderte ».vimrc«-Datei des Autors.

Abbildung 1: Auf Github steht die maßgeschneiderte ».vimrc«-Datei des Autors.

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.

Abbildung 2: Ansible lädt eine ».vimrc«-Datei aus einem Github-Repository und installiert sie lokal.

Abbildung 2: Ansible lädt eine ».vimrc«-Datei aus einem Github-Repository und installiert sie lokal.

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.

Abbildung 3: In der Vagrant-VM läuft dank Ansible »mod_perl2« auf einem Apache-2-Server mit dem Testskript aus <a href="#article_l5" class="listing" srcset=

Listing 5.” width=”300″ height=”151″ /> Abbildung 3: In der Vagrant-VM läuft dank Ansible »mod_perl2« auf einem Apache-2-Server mit dem Testskript aus Listing 5.

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

  1. Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2015/10/Perl
  2. Lorin Hochstein, “Ansible: Up and Running”: O’Reilly, 2015
  3. Ansible: https://github.com/ansible/ansible
  4. Michael Schilli, “Entspannt nach draußen”: Linux-Magazin 05/13, S. 94, https://www.linux-magazin.de/Ausgaben/2013/05/Perl-Snapshot

Der Autor

Michael Schilli arbeitet als Software Engineer in der San Francisco Bay Area in Kalifornien. In seiner seit 1997 laufenden Kolumne forscht er jeden Monat nach praktischen Anwendungen der Skriptsprache Perl. Unter mailto:mschilli@perlmeister.com beantwortet er gerne Fragen.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 4 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben