Ansible
Last updated
Was this helpful?
Last updated
Was this helpful?
Ansible wird zum automatisierten Provisioning von Systemen eingesetzt.
In einem Multimachine-Vagrant-Projekt, das aus 5 verschiedenen Images bestand und dessen Provisioning eine knappe Stunde dauerte, haben wir Shellscripting zum Provisioning eingesetzt. Das hatte einige Vorteile, aber auch einen ganz entscheidenden Nachteil: Fehlende Idempotenz.
Idempotenz erlaubt es ein Script mehrfach hintereinander ablaufen zu lassen, ohne daà sich das Ergebnis verändert. Einige Praxisbeispiele:
Beispiel 1:
Skripte entwickeln sich weiter und neu entwickelte Teile sollen auch sofort genutzt werden kÜnnen. Ohne Idempotenz darf/kann ich bereits ausgefßhrte Schritte nicht einfach wiederholen, sondern muà sie auskommentieren (o. ä. ... was evtl. gar nicht so einfach ist).
enthält ein Skript mkdir /tmp/test
, so ist das nicht idempotent. Bei der zweiten Ausfßhrung schlägt das Kommando fehl, weil das Verzeichnis schon existiert. Ohne Idempotenz-Support muà man dann mßhselig (und fehlerträchtig) entsprechende if
-clauses einbauen oder andere Befehle nutzen.
Beispiel 2:
Bricht das Provisioning nach 20 Minuten Laufzeit mit einem Fehler ab, dann bedeutet das nach Beheben des Fehlers oftmals (wenn man nicht mehrere Minuten in das manuelle Anpassen der Scripte investieren will), daĂ das Image komplett platt gemacht und neu aufgebaut wird ... also wieder 20 Minuten warten bis man Ăźberhaupt wieder so weit ist wie man schon mal war.
Fehlende Idempotenz hat zumeist zur Folge, daĂ die Skripte nicht regelmäĂig ausgefĂźhrt werden (weil: zu umständlich) und das wiederum hat zur Folge, daĂ man bei jeder AusfĂźhrung Schlimmstes befĂźrchten muĂ.
Ansible unterstĂźtzt Idempotenz (state=present
):
http://docs.ansible.com/ansible/intro_adhoc.html
AdHoc-Kommandos werden per ansible
(--args
aka -a
)
abgeschickt und sind so komfortabel wie Shell-Kommandos ... nur eben mit der Mächtigkeit sehr viele Hosts mit der Abarbeitung des Kommandos zu beauftragen.
Ein Playbook hingegen ist eher fĂźr umfangreichere Aktionen gedacht:
Hierzu wird eine Playbook-Datei benĂśtigt.
Während man es bei Shellscripting und Vagrantscripting gewohnt ist auf EINER Maschine zu arbeiten, ist Ansible dazu gedacht Kommandos auf vielen Maschinen (evtl. Tausende) gleichzeitig auszufßhren. Das erleichtert insbesondere die Wartung von Serverfarmen. Hierzu lassen sich Maschinen ßber das Inventory-File (/etc/ansible/hosts
) kategorisieren, so daĂ per
alle als Webserver kategorisierte Maschinen ihre Plattenbelegung ausgeben.
Ansible unterscheidet zwischen
Local-Verbindung: hierbei wird das Ansible-Skript/Kommando nicht per ssh zum Zielrechner (= localhost) gebracht ... insofern muĂ die ssh-Infrastruktur auch nicht existieren. Allerdings funktionieren dann Properties wie remote_user
nicht ... stattdessen wird become
verwendet, um in eine andere Rolle zu schlĂźpfen (beispielsweise als root
zu agieren)
Remote-Verbindung: hierbei wird das Kommando auf dem Zielrechner per ssh -c ...
ausgefĂźhrt. Das kann auch passieren, wenn der Zielrechner localhost
ist.
Wenn nicht explizit angegeben wurde, ob Ansible local oder remote verwenden soll, dann versucht Ansible ein Auto-Detection. Man kann Ansible aber auch unter die Arme greifen, um die Connection-Variante explizit zu bestimmen (/etc/ansible/hosts
):
Das Scripting unterscheidet sich bei den AusfĂźhrungsformen nicht. Allerdings benĂśtigt die Remote-AusfĂźhrung ein erweitertes Setup (ssh-Infrastrutur).
ACHTUNG: fßrs Entwickeln der Skripte ist der Local-Mode sicherlich ganz brauchbar, aber integrativ sollte man dann tatsächlich auch im Remote-Mode testen
Ăber den Parameter -vvvvvv
kann man die einzelnen Schritte erkennen, die Ansible fĂźr die Remote-AusfĂźhrung macht - hier am Beispiel ansible ansible-target --args "/bin/echo hello" -vvvvvv
:
Man sieht hier sehr schÜn wie die Kommunikation under-the-hood abläuft:
ssh -c
, um ein Zielverzeichnis (~/.ansibe/tmp/ansible-tmp-foo
( fĂźr das auszufĂźhrende Kommando anzulegen
sftp
, um das Kommando zum Target-Host zu transportieren
kann man von SFTP auf SSH umkonfigurieren (Stichwort scp_if_ssh=True
)
ACHTUNG: das Kommando-Verzeichnis wird i. a. am Ende wieder gelĂśscht (es sei denn man verwendet export ANSIBLE_KEEP_REMOTE_FILES=1
)
ssh-Connection Sharing Ăźber ControlPath
erfolgt
Das CLI ansible-playbook
hat ein paar interessante Parameter:
Diese Parameter sind insbes. fĂźr die Entwicklung von Ansible-Skripten durchaus hilfreich.
Ăber -v
wird das LogLevel definiert ... je mehr v's desto geschwätziger:
Die Konfiguration erfolgt in ansible.cfg
. Hier werden verschiedene Locations herangezogen:
im aktuellen Verzeichnis
/etc/ansible/ansible.cfg
In der Default-Einstellung wird SFTP verwendet, um das Ansible-Play vom Ansible-Controller zum Target-Host zu transportieren.
Ansible bietet zur Spezifikation der Playbooks eine DSL an, aus der zur AusfĂźhrungszeit intern in Python-Code generiert wird. Im Ansible-Remote-Modus wird ein Python-Script (selbst im Fall eines einfachen ansible ansible-target -a "/bin/echo hello"
ist dieses Script 90 Kilobyte groĂ) vom Ansible-Controller zum Target-Server transportiert und in ~/.ansible/tmp/ansible-tmp-foo/command
gespeichert.
Bei der AusfĂźhrung wird eine Result in JSON-Form ausgegben (hier im Falle eines erfolgreichen echo hello
)
das der Ansible-Controller erhält.
Nach der AusfĂźhrung wird das Command-Skript per default gelĂśscht. Mit der Environment-Variable export ANSIBLE_KEEP_REMOTE_FILES=1
kann das LĂśschen verhindert werden ... bei der Fehlersuche sehr hilfreich, denn dann kann man mal einen Blick in das generierte Python-Skript werfen!!!
Natßrlich muà man seine Konsole nicht dauerhaft verändern - bei folgendem Aufruf wird die Command-datei nur fßr diesen einen Aufruf behalten:
Beispiele: https://github.com/ansible/ansible-examples
alle Module: http://docs.ansible.com/ansible/list_of_all_modules.html
https://liquidat.wordpress.com/2016/01/26/howto-introduction-to-ansible-variables/
ACHTUNG: yaml ist bei den EinrĂźckungen sehr strikt und Fehler in diesem Bereich sind schwer zu finden
Ansible DSL baut auf Modulen auf: http://docs.ansible.com/ansible/modules_by_category.html
Die sog. Core-Modules werden vom offiziellen Ansible-Team bereitgestellt und immer mit Ansible ausgeliefert. Drittanbieter kĂśnnen eigene Module bereitstellen/verkaufen, die dann aber extra installiert werden mĂźssen.
Ansible sammelt (sog. Gather Facts) zu den Hosts Informationen (z. B. CPU, OS), die als Variablen zur VerfĂźgung stehen. Diese Variablen kĂśnnen per
Statt localhost
kann jeder registrierte Host eingetragen werden (siehe /etc/ansible/hosts
). Auf diese Weise findet man beispielsweise heraus, daĂ es eine Variable ansible_kernel
gibt, die die die Kernel-Version enthält (anstatt uname -r
).
Umgebungsvariablen kĂśnnen Ăźber Gather Facts Informationen
oder Ăźber
abgefragt werden.
Ăber lookup
lassen sich Inhalte von Dateien auslesen. Beispielsweise wird der Public-Key (in .ssh/id_rsa.pub
) ausgelesen und in die authorized_keys
des Users vagrant
auf einer anderen Maschine Ăźbertragen:
Im Inventory-File (konfigurierbar Ăźber --inventory-file=/tmp/myinventory
) werden die Zielsysteme definiert, gruppiert und der Zugriff konfiguriert, u. a.
Host und Port fĂźr den ssh-Zugriff
Private-Keys
...
http://docs.ansible.com/ansible/playbooks_roles.html
Ăber Rollen läĂt sich in Ansible Modularisierung abbilden, um Wiederverwendung zu erreichen. Wiederverwendung ist sicherlich erst dann erforderlich, wenn man mehr macht als nur ein einziges Playbook zusammenzubasteln.
Verwendet man Playbook fĂźr verschiedene Use-Cases, dann ergibt sich allerdings sehr schnell der Wunsch nach einer Modularisierung (Beispiel: Sicherstellung einer Java-JDK-Installation).
Modularisierung Ăźber Includes läĂt sich auf allen abstrakten Ebenen verwenden:
Playbooks verwenden weitere Playbooks
Tasks verwenden weitere Tasks
Handlers verwenden weitere Handlers
Da der Use-Case dem zu verwendenden Modul nicht bekannt ist, mĂźssen die Module entsprechend parametrisiert werden. Damit lassen sich einfache Anpassungen vornehmen (auf der Ebene eines Attributwertes).
Beispiel: Ensure-Java
Die Sicherstellung einer passenden Java-Installation kĂśnnte ein wiederverwendbarer Baustein sein, der in vielen Playbooks benĂśtigt wird. In manchen Situationen wird Java 7 und in anderen Java 8 benĂśtigt. Das Modul ensureJdkInstallation.yml
erhält hierzu einen Parameter version
:
In java.yml
wird der Parameter per {{ version }}
erferenziert.
Mit Conditionals kÜnnen beispielsweise Include-Anweisungen bestßckt werden, um auf diese Weise betriebssystemabhängige Weichen einzubauen:
If roles/x/tasks/main.yml
exists, tasks listed therein will be added to the play
If roles/x/handlers/main.yml
exists, handlers listed therein will be added to the play
If roles/x/vars/main.yml
exists, variables listed therein will be added to the play
If roles/x/meta/main.yml
exists, any role dependencies listed therein will be added to the list of roles (1.3 and later)
Any copy, script, template or include tasks (in the role) can reference files in roles/x/{files,templates,tasks}/
(dir depends on task) without having to path them relatively or absolutely
Es besteht allerdings weiterhin die MĂśglichkeit, Tasks, Handlers, Variablen explizit in Playbooks zu verwenden.
Das Playbook (site.yml
) sieht dann beispielsweise so aus:
Darin werden Rollen referenziert, wodurch der Play grĂśĂer wird. In Rollen selbst kĂśnnen Role Dependencies (s. u.) definiert werden, die den Play ihrerseits erweitern.
Ansible wird dadurch sehr mächtig ... aber vielleicht auch sehr komplex.
Eine Rolle kann in <role>/meta/main.yml
Abhängigkeiten auf weitere Rollen definieren ... diese Abhängigkeiten kÜnnen parametrisiert werden.
Rollen kÜnnen in Abhängigkeit bestimmter Zustände (z. B. Betriebssystem) gezogen werden.
Auf diese Weise lassen sich beispielsweise betriebssystemabhängige Weichen einbauen wie diese:
... ist ein Repository fĂźr Community-Developed Ansible Roles ...
In meinem ersten Ansible-Projekt stand ich vor der Aufgabe Java 8 auf mindestens CentOS zu installieren.
Die oben vorgestellte Wiederverwendung Ăźber Roles funktioniert gut, wenn das gesamte Projekt unter der eigenen Kontrolle ist. Wenn nun aber drei getrennte Projekte auf eine gemeinsame Scripting-Basis (im wesentlichen Roles) zugreifen sollen, dann hat Ansible derzeit noch keine passende Antwort.
Ansible Galaxy scheint in diese Richtung zu gehen ...
Offizielle Best-Practices: http://docs.ansible.com/ansible/playbooks_best_practices.html
Code-Beispiele: https://github.com/ansible/ansible-examples
Besser ist, sich als normaler User zu verbinden und dann gezielt fĂźr einzelne Kommandos per become
in die Rolle des root
zu schlĂźpfen.
Um Ansible-Skripte komplett automatisiert (also nicht interaktiv) ausfĂźhren zu lassen, muĂ der SSH-Agent mit dem/den Private-Key(s) gefĂźttert werden, denn ansonsten muĂ der Benutzer die Passphrase (wenn vorhanden - RECOMMENDED!!!) interaktiv eingeben.
http://docs.ansible.com/ansible/playbooks_roles.html
Ăber Rollen läĂt sich in Ansible Modularisierung abbilden, um Wiederverwendung zu erreichen. Wiederverwendung ist sicherlich erst dann erforderlich, wenn man mehr macht als nur ein einziges Playbook zusammenzubasteln.
weitere Details: siehe oben
Man kommt tatsächlich sehr schnell (wenn man mal die Ansible-Infrastruktur mit ssh, sudo aufgebaut hat) zu brauchbaren Ergebnissen. Häufig braucht man gar nicht so viele Module ... es sind 5-10 Module, die wirklich ständig im Einsatz sind.
Ich arbeite an einer Infrastruktur bestehend aus Controller und verschiedenen Target-Hosts. Mir ist es schon häufiger passiert, daĂ
ich vom Target-Host das Ansible-Skript starten wollte ... dort ist aber gar kein Ansible installiert
ich vom Controller-System Remote-Konfigurationen angestoĂen habe und die Ănderungen lokal gesucht habe
IDEMPOTENZ ist leichter mĂśglich als bei Shellscripts (der Entwickler muĂ die Skripte aber auch idempotent gestalten!!!)
gute Dokumentation
Remote-Installationen mĂśglich
flache Lernkurve (s. o.), Komplexität ßberschaubar
keine Agenten (Puppet) notwendig, die auf den Zielsystemen installiert sein mĂźssen ... ssh + sudo + python
die Reihenfolge der Taskausfßhrung ist bei Ansible klar definiert (nämlich genau die aus dem Playbook)
nur Push, kein Pull (kann auch als Nachteil gesehen werden)
kein interner Dependency Graph und den damit verbundenen Problemen (unsynchronisierte Redundanzen)
gute Lesbarkeit
insbes. fĂźr Gelegenheitsanwender (z. B. Entwickler) interessant
angeblich gute Fehlermeldungen (noch keine eigene Erfahrung damit gemacht)
guter Support fĂźr On-Premise- und Cloud-Deployments
gute Vagrant-Integration
Ansible basiert auf Python ... fĂźr mich als nicht Ruby (Puppet) Entwickler kĂśnnte das Vorteile haben
Templating basiert auf dem Jinja2-Templating (Subset von Django), mit dem viele Entwickler vertrauter sind
die Fehlermeldungen sind meistens ganz brauchbar
Ausgabe bei der Abarbeitung der Playbooks sehr Ăźbersichtlich:
pfh@workbench ~/windows_de.cachaca.workbench (git)-[master] % ansible-playbook playbook.yml
PLAY ***************************************************************************
TASK [setup] ******************************************************************* ok: [localhost]
TASK [system | update package meta-data] *************************************** ok: [localhost]
TASK [system | install aptitude ... needed for succeeding "upgrade system"] *** ok: [localhost]
TASK [system | upgrade system] ************************************************* ok: [localhost]
TASK [devcon | create directory strcuture for user pfh] ************************ ok: [localhost] => (item=/bin) ok: [localhost] => (item=/src) ok: [localhost] => (item=/temp) ok: [localhost] => (item=/zipfiles)
manchmal (u. a. fehlende schlieĂende GänsefĂźĂchen, falsche EinrĂźckungen im Ansible-Playbook) sind die Fehlermeldungen wenig hilfreich
Tatsächlich scheint mir die Performance nicht so berauschend. Fßr meinen Anwendungsfall (wenige Tasks auf wenigen Knoten) ist es aber ok.
Vielleicht helfen die Einstellungen pipelining = True
und host_key_checking = False
in ansible.cfg
.
https://dantehranian.wordpress.com/2015/01/20/ansible-vs-puppet-overview/
https://dantehranian.wordpress.com/2015/01/20/ansible-vs-puppet-hands-on-with-ansible/
ein sehr ausfĂźhrlicher Vergleich zwischen Ansible und Saltstack: http://ryandlane.com/blog/2014/08/04/moving-away-from-puppet-saltstack-or-ansible/
"After three years of using Puppet at VMware and Virtual Instruments, the thought of not continuing to use the market leader in configuration management tools seemed like a radical idea when it was first suggested to me. After spending several weeks researching Ansible and using it hands-on, I came to the conclusion that Ansible is a perfectly viable alternative to Puppet. I tend to agree with Lyftâs conclusion that if you have a centralized Ops team in change of deployments then they can own a Puppet codebase. On the other hand if you want more wide-spread ownership of your configuration management scripts, a tool with a shallower learning curve like Ansible is a better choice." (Dan Tehranian's Blog, https://dantehranian.wordpress.com/2015/01/20/ansible-vs-puppet-hands-on-with-ansible/)
ganz dicht am manuellen Aufsetzen - klar Scripting bedeutet dann oftmals auch, daĂ eine gewisse Art von Konfigurierbarkeit in die Scripte eingebaut wird, um sie an bestimmte Umgebungen anzupassen. Verzichtet man aber auf die Konfigurierbarkeit, dann genĂźgt es, die fĂźr eine manuelle Installation/Konfiguration eines System erforderlichen Befehle, in eine Shelldatei zu packen. Viola :-)
kein weiteres Layer zwischen den Befehlen und dem System ... ich bin mir aber nicht sicher, ob das wirklich ein Vorteil ist, denn Fehlersuche in Shellscripten ist wirklich alles andere als eine Freude ... insbes. aufgrund der fehlenden Idempotenz
Ich habe ein komplexes Setup Ăźber Vagrant und Shellscripting-Provisioning umgesetzt (Laufzeit 50 Minuten). Erst danach habe ich Ansible kennengelernt.
Mein Fazit: Ansible ist der klare Sieger
Variablen kĂśnnen auf sehr vielen Ebenen definiert werden. Vom Betriebssystem bis zum Playbook. Wenn sich die Variablen Ăźberschreiben (was durchaus gewollt sein kann), - dieses Feature sollte also mit Bedacht eingesetzt werden.
Roles bauen einen Convention-over-Configuration-Mechanismus um Includes auf. Auf Basis einer vorgegebenen Ordnerstruktur erfolgt der Aufbau eines Plays (= Playbook zur Laufzeit) nahezu automatisch. Durch Verzicht auf explizite Konfiguration sind Skripte leichter erweiterbar/anpassbar. Das erinnert sehr stark an den Ansatz, den .
Zentrales Element der Konvention ist die :
Ausgehend davon werden sog. Plays (Playbooks zur Laufzeit) nach folgenden Regeln dynamisch zusammengebaut ():
An diese Denkweise muĂ man sich erst einmal gewĂśhnen. Insbesonder, wenn man - wie ich - im privaten Gebrauch ausschlieĂlich lokal arbeitet () und im geschäftlichen Umfeld ausschlieĂlich remote.
Ganz ohne Frage ... Shellscripting hat ein paar Vorteile (die Nachteile habe ich umfassend in einem aufgelistet: