Ansible – Was ich am Ad-hoc-Modus schätze

Schon seit einiger Zeit hilft mir Ansible1 fast täglich dabei, meine Arbeit leichter zu gestalten. Heute möchte ich euch ganz kurz erzählen, was ich am Ad-hoc-Modus schätze.

Der Ad-hoc-Modus bietet die Möglichkeit, einfache Kommandos parallel auf einer Gruppe von Nodes ausführen zu lassen, ohne zuvor ein Playbook erstellen zu müssen. Ein Ad-hoc-Befehl besitzt z.B. den folgenden Aufbau:

ansible [-m module_name] [-a args] [options]

Ein einfaches Beispiel aus der Ansible-Dokumentation2 soll die Anwendung verdeutlichen:

# ansible all -m ping -i staging --limit=e-stage
host01.example.com | SUCCESS => {
"changed": false,
"ping": "pong"
}
host02.example.com | SUCCESS => {
"changed": false,
"ping": "pong"
}
host03.example.com | SUCCESS => {
"changed": false,
"ping": "pong"
}

Das Schlüsselwort all gibt an, dass das Kommando auf allen Nodes ausgeführt werden soll, welche in der Inventar-Datei enthalten sind. Mit -m ping wird das zu verwendende Ansible-Modul spezifiziert. Da das verwendete Modul keine weiteren Argumente besitzt, findet -a in diesem Beispiel keine Anwendung. Mit der Option -i kann die zu verwendende Inventar-Datei angegeben werden. Lässt man diese Option weg, wird die Standard-Inventar-Datei /etc/ansible/hosts verwendet. Mit der Option --limit=e-stage wird die Ausführung noch weiter eingeschränkt. So wird in diesem Fall das Modul ping nur auf den Nodes der Gruppe e-stage ausgeführt. Das in diesem Beispiel verwendete Inventar besitzt den folgenden Aufbau:

[e-stage]
host01.example.com
host02.example.com
host03.example.com
host06.example.com
host07.example.com

[i-stage]
host04.example.com

[p-stage]
host05.example.com

Verknüpfung mit weiteren Kommandos

Selbstverständlich lassen sich Ansible-Ad-hoc-Kommandos auf der Kommandozeile auch weiter verknüpfen. Dies soll an zwei kleinen Beispielen verdeutlicht werden.

Status eines Dienstes prüfen

In diesem ersten Beispiel soll der Status des Dienstes chronyd überprüft werden, ohne den aktuellen Status zu ändern. Dabei soll das Kommando systemctl status chronyd.service via Ansible parallel auf den Nodes ausgeführt werden.

Zuvor habe ich mir auf einem Node angesehen, wie die Ansible-Ausgabe in Abhängigkeit vom Dienststatus aussieht (Ausgabe gekürzt):

# Der Dienst auf dem Node ist gestartet
root@ansible-control-machine>ansible all -m command -a'/usr/bin/systemctl status chronyd.service' -i staging -l host01.example.com
host01.example.com | SUCCESS | rc=0 >>
* chronyd.service - NTP client/server
Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2016-12-15 14:52:02 CET; 19h ago

# Der Dienst auf dem Node ist gestoppt
root@ansible-control-machine>ansible all -m command -a’/usr/bin/systemctl status chronyd.service‘ -i staging -l host01.example.com
host01.example.com | FAILED | rc=3 >>
* chronyd.service – NTP client/server
Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; vendor preset: enabled)
Active: inactive (dead) since Fri 2016-12-16 10:04:34 CET; 4s ago

# Das Paket, welches den Dienst enthaelt ist nicht installiert
root@ansible-control-machine>ansible all -m command -a’/usr/bin/systemctl status chronyd.service‘ -i staging -l host01.example.com
host01.example.com | FAILED | rc=4 >>
Unit chronyd.service could not be found.

Anhand der Ausgaben ist zu erkennen, dass Ansible den Task als „| SUCCESS |“ markiert, wenn der Dienst läuft und als „| FAILED |“, wenn der Dienst gestoppt bzw. gar nicht installiert ist. Durch Verknüpfung des Kommandos mit grep kann man sich nun schnell einen Überblick über den Dienststatus auf seinen Rechnern verschaffen:

root@ansible-control-machine> ansible all -m command -a'/usr/bin/systemctl status chronyd.service' -i staging --limit=e-stage | grep '| SUCCESS |\|| FAILED |'
host01.example.com | SUCCESS | rc=0 >>
host02.example.com | SUCCESS | rc=0 >>
host03.example.com | FAILED | rc=3 >>
host06.example.com | SUCCESS | rc=0 >>
host07.example.com | SUCCESS | rc=0 >>

Anhand der Ausgabe ist leicht zu erkennen, dass der Dienst chronyd auf host03 nicht läuft. Anhand des Return-Codes rc=3 lässt sich weiterhin erkennen, dass das notwendige Paket offensichtlich installiert ist, der Dienst jedoch nicht gestartet wurde. Dies kann nun jedoch schnell durch folgenden Befehl korrigiert werden (Ausgabe gekürzt):

root@ansible-control-machine>ansible host03.example.com -m systemd -a'name=chronyd state=started' -i staging
host03.example.com | SUCCESS => {
"changed": true,
"name": "chronyd",
"state": "started",
"status": {...}
}

Eine erneute Ausführung des ersten Kommandos bestätigt, dass der Dienst nun auch auf dem Node host03 ausgeführt wird.

root@ansible-control-machine> ansible all -m command -a'/usr/bin/systemctl status chronyd.service' -i staging --limit=e-stage | grep '| SUCCESS |\|| FAILED |'
host01.example.com | SUCCESS | rc=0 >>
host02.example.com | SUCCESS | rc=0 >>
host03.example.com | SUCCESS | rc=0 >>
host06.example.com | SUCCESS | rc=0 >>
host07.example.com | SUCCESS | rc=0 >>

Paketversion überprüfen

In diesem Beispiel möchte ich die installierte Version des Pakets tzdata abfragen. Dies geschieht auf einem einzelnen Host mit dem Kommando rpm -qi :

# rpm -qi tzdata
Name : tzdata
Version : 2016i
Release : 1.el7
Architecture: noarch
Install Date: Wed Nov 9 08:47:03 2016
Group : System Environment/Base
Size : 1783642
License : Public Domain
Signature : RSA/SHA256, Fri Nov 4 17:21:59 2016, Key ID 199e2f91fd431d51
Source RPM : tzdata-2016i-1.el7.src.rpm
Build Date : Thu Nov 3 12:46:39 2016
Build Host : ppc-045.build.eng.bos.redhat.com
Relocations : (not relocatable)
Packager : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>
Vendor : Red Hat, Inc.
URL : https://www.iana.org/time-zones
Summary : Timezone data
Description :
This package contains data files with rules for various timezones around
the world.

Mich interessiert lediglich die zweite Zeile, welche die Version des Pakets enthält. Die frage ich nun wie folgt ab:

root@ansible-control-machine> ansible all -m command -a'/usr/bin/rpm -qi tzdata' -i staging --limit=e-stage | grep 'SUCCESS\|Version'
host01.example.com | SUCCESS | rc=0 >>
Version : 2016f
host02.example.com | SUCCESS | rc=0 >>
Version : 2016g
host03.example.com | SUCCESS | rc=0 >>
Version : 2016i
host06.example.com | SUCCESS | rc=0 >>
Version : 2016i
host07.example.com | SUCCESS | rc=0 >>
Version : 2016i

Ohne Ansible hätte ich diese Aufgaben entweder mit Iteration in einem kurzen Shell-Skript lösen müssen, oder zuerst ein Kochbuch, Manifest, etc. schreiben, welches dann anschließend ausgeführt werden kann. So wurde mir hingegen einiges an Zeit gespart, die ich für andere Dinge verwenden konnte.

Quellen und weiterführende Links

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.