Ansible
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.
Wichtige Konzepte
Idempotenz
Idempotenz erlaubt es ein Script mehrfach hintereinander ablaufen zu lassen, ohne daß sich das Ergebnis verändert. Einige Praxisbeispiele:
- Beispiel 1: für meinen Workbench-Anwendnungsfall besonders wichtig
- 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) entsprechendeif
-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
):
lineinfile:
dest=~/.zshrc
state=present
line='ZSH_THEME=robbyrussell'
AdHoc vs. Playbook
AdHoc-Kommandos werden per ansible
(--args
aka -a
)
ansible all --args "/bin/echo hello"
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:
ansible-playbook myplaybook.yml
Hierzu wird eine Playbook-Datei benötigt.
Multimachine
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
ansible webservers -a "/bin/df -h"
alle als Webserver kategorisierte Maschinen ihre Plattenbelegung ausgeben.
Local vs. Remote
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 wirdbecome
verwendet, um in eine andere Rolle zu schlüpfen (beispielsweise alsroot
zu agieren) - Remote-Verbindung: hierbei wird das Kommando auf dem Zielrechner per
ssh -c ...
ausgeführt. Das kann auch passieren, wenn der Zielrechnerlocalhost
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
):
localhost ansible_connection=local
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
Remote-Mode
Ü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
:
╭─pfh@workbench ~/.ssh
╰─➤ ansible ansible-target --args "/bin/echo hello" -vvvvvv
Using /etc/ansible/ansible.cfg as config file
Loaded callback minimal of type stdout, v2.0
<ansible-target> ESTABLISH SSH CONNECTION FOR USER: admin
<ansible-target> SSH: ansible.cfg set ssh_args:
(-o)(ControlMaster=auto)
(-o)(ControlPersist=60s)
<ansible-target> SSH: ansible_password/ansible_ssh_pass not set:
(-o)(KbdInteractiveAuthentication=no)
(-o)(PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey)
(-o)(PasswordAuthentication=no)
<ansible-target> SSH: ANSIBLE_REMOTE_USER/remote_user/ansible_user/user/-u set: (-o)(User=admin)
<ansible-target> SSH: ANSIBLE_TIMEOUT/timeout set: (-o)(ConnectTimeout=10)
<ansible-target> SSH: PlayContext set ssh_common_args: ()
<ansible-target> SSH: PlayContext set ssh_extra_args: ()
<ansible-target> SSH: found only ControlPersist; added ControlPath:
(-o)(ControlPath=/home/pfh/.ansible/cp/ansible-ssh-%h-%p-%r)
<ansible-target> SSH: EXEC ssh -C -vvv
-o ControlMaster=auto
-o ControlPersist=60s
-o KbdInteractiveAuthentication=no
-o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey
-o PasswordAuthentication=no
-o User=admin
-o ConnectTimeout=10
-o ControlPath=/home/pfh/.ansible/cp/ansible-ssh-%h-%p-%r -tt ansible-target
'( umask 22 && mkdir -p "$( echo $HOME/.ansible/tmp/ansible-tmp-1490084266.37-148070795341843 )"
&& echo "$( echo $HOME/.ansible/tmp/ansible-tmp-1490084266.37-148070795341843 )" )'
<ansible-target> PUT /home/pfh/temp/tmpY_WJOP TO
/home/admin/.ansible/tmp/ansible-tmp-1490084266.37-148070795341843/command
<ansible-target> SSH: ansible.cfg set ssh_args:
(-o)(ControlMaster=auto)
(-o)(ControlPersist=60s)
<ansible-target> SSH: ansible_password/ansible_ssh_pass not set:
(-o)(KbdInteractiveAuthentication=no)
(-o)(PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey)
(-o)(PasswordAuthentication=no)
<ansible-target> SSH: ANSIBLE_REMOTE_USER/remote_user/ansible_user/user/-u set: (-o)(User=admin)
<ansible-target> SSH: ANSIBLE_TIMEOUT/timeout set: (-o)(ConnectTimeout=10)
<ansible-target> SSH: PlayContext set ssh_common_args: ()
<ansible-target> SSH: PlayContext set sftp_extra_args: ()
<ansible-target> SSH: found only ControlPersist; added ControlPath:
(-o)(ControlPath=/home/pfh/.ansible/cp/ansible-ssh-%h-%p-%r)
<ansible-target> SSH: EXEC sftp -b - -C -vvv
-o ControlMaster=auto
-o ControlPersist=60s
-o KbdInteractiveAuthentication=no
-o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey
-o PasswordAuthentication=no
-o User=admin
-o ConnectTimeout=10
-o ControlPath=/home/pfh/.ansible/cp/ansible-ssh-%h-%p-%r '[ansible-target]'
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 anzulegensftp
, 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
)
- kann man von SFTP auf SSH umkonfigurieren (Stichwort
- ssh-Connection Sharing über
ControlPath
erfolgt
Getting Started
Ansible Runtime Engine für Playbooks
Das CLI ansible-playbook
hat ein paar interessante Parameter:
--inventory-file
--limit
-vvvv
Diese Parameter sind insbes. für die Entwicklung von Ansible-Skripten durchaus hilfreich.
LogLevel
Über -v
wird das LogLevel definiert ... je mehr v's desto geschwätziger:
ansible-playbook myplaybook.yml -vvvv
Konfiguration
Die Konfiguration erfolgt in ansible.cfg
. Hier werden verschiedene Locations herangezogen:
- im aktuellen Verzeichnis
/etc/ansible/ansible.cfg
Wichtige Parameter
scp_if_ssh
In der Default-Einstellung wird SFTP verwendet, um das Ansible-Play vom Ansible-Controller zum Target-Host zu transportieren.
ANSIBLE_KEEP_REMOTE_FILES=1
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
)
```json
{
"changed": true,
"end": "2017-03-21 09:48:19.398800",
"stdout": "hello",
"cmd": ["/bin/echo", "hello"],
"rc": 0,
"start": "2017-03-21 09:48:19.397007",
"stderr": "",
"delta": "0:00:00.001793",
"invocation": {
"module_args": {
"warn": true,
"executable": null,
"chdir": null,
"_raw_params": "/bin/echo hello",
"removes": null,
"creates": null,
"_uses_shell": false
}
},
"warnings": []
}
```
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:
```bash
ANSIBLE_KEEP_REMOTE_FILES=1 ansible ansible-target -a "/bin/echo hello"
```
Ansible Playbook DSL
- 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/
YAML-Syntax
ACHTUNG: yaml ist bei den Einrückungen sehr strikt und Fehler in diesem Bereich sind schwer zu finden
Module
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.
Variablen
Variablen können auf sehr vielen Ebenen definiert werden. Vom Betriebssystem bis zum Playbook. Wenn sich die Variablen überschreiben (was durchaus gewollt sein kann), wird es schnell mal schwierig - dieses Feature sollte also mit Bedacht eingesetzt werden.
Gather Facts
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
ansible -m setup localhost
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
Umgebungsvariablen können über Gather Facts Informationen
vars:
executingUser: "{{ ansible_env.HOME') }}"
oder über
vars:
executingUser: "{{ lookup('env','USER') }}"
abgefragt werden.
Lookups
Ü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:
tasks:
- set_fact:
current_user_default_public_ssh_key: "{{ ansible_env.HOME }}/.ssh/id_rsa.pub"
- authorized_key:
user: vagrant
key: "{{ lookup('file', current_user_default_public_ssh_key) }}/.ssh/id_rsa.pub"
Inventory
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
- ...
Wiederverwendung
Ü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).
Includes
Modularisierung über Includes läßt sich auf allen abstrakten Ebenen verwenden:
- Playbooks verwenden weitere Playbooks
- name: this is a playbook
hosts: all
tasks:
- name: greetings
shell: echo "hello world"
- include: otherPlaybook1.yml
- include: otherPlaybook2.yml
- Tasks verwenden weitere Tasks
tasks:
- include: java.yml
vars:
version: 8
provider: oracle
- Handlers verwenden weitere Handlers
handlers:
- include: handlers/myhandler.yml
Parametrisierung
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
:
tasks:
- include: ensureJdkInstallation.yml
vars:
version: 8
provider: oracle
In java.yml
wird der Parameter per {{ version }}
erferenziert.
Conditionals
Mit Conditionals können beispielsweise Include-Anweisungen bestückt werden, um auf diese Weise betriebssystemabhängige Weichen einzubauen:
---
- include: centos7.yml
when: ansible_distribution == "CentOS"
and ansible_distribution_major_version == "7"
- include: ubuntu.yml
when: ansible_distribution == "Ubuntu"
Roles
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 Spring-Boot mit der annotationsbasierten ApplicationContext-Aufbau geht.
Convention: Ordnerstruktur
Zentrales Element der Konvention ist die Filesystem-Struktur:
site.yml
webservers.yml
fooservers.yml
roles/
myRole/
files/
templates/
tasks/
handlers/
vars/
defaults/
meta/
Ausgehend davon werden sog. Plays (Playbooks zur Laufzeit) nach folgenden Regeln dynamisch zusammengebaut (kopiert von hier):
- 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:
---
- hosts: webservers
roles:
- common
- webservers
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.
Role Dependencies
Eine Rolle kann in <role>/meta/main.yml
Abhängigkeiten auf weitere Rollen definieren ... diese Abhängigkeiten können parametrisiert werden.
Conditionals
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:
---
- include: centos7.yml
when: ansible_distribution == "CentOS"
and ansible_distribution_major_version == "7"
- include: ubuntu.yml
when: ansible_distribution == "Ubuntu"
Ansible Galaxy
... ist ein Repository für Community-Developed Ansible Roles ...
Role Java
In meinem ersten Ansible-Projekt stand ich vor der Aufgabe Java 8 auf mindestens CentOS zu installieren.
Projektübergreifende Wiederverwendung
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 ...
Best Practices
- Offizielle Best-Practices: http://docs.ansible.com/ansible/playbooks_best_practices.html
- Code-Beispiele: https://github.com/ansible/ansible-examples
Nicht verwenden "remote_user: root"
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.
SSH-Agent starten und konfigurieren
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.
Verwende Roles
Ü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
Bewertung
Lernkurve
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
An diese Denkweise muß man sich erst einmal gewöhnen. Insbesonder, wenn man - wie ich - im privaten Gebrauch ausschließlich lokal arbeitet (um die Workbench aufzubauen) und im geschäftlichen Umfeld ausschließlich remote.
Pro
- 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)
Contra
- manchmal (u. a. fehlende schließende Gänsefüßchen, falsche Einrückungen im Ansible-Playbook) sind die Fehlermeldungen wenig hilfreich
Performance
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
.
Alternativen
- 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/)
Alternative Shellscripting
Ganz ohne Frage ... Shellscripting hat ein paar Vorteile (die Nachteile habe ich umfassend in einem eigenen Abschnitt aufgelistet:
- 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