Kapitel 4. Inventarisierung: Beschreibe deine Server

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

Bis jetzt haben wir nur mit einem Server (oder Host, wie Ansible ihn nennt) gearbeitet. Die einfachste Bestandsaufnahme ist eine kommagetrennte Liste von Hostnamen, die du auch ohne einen Server erstellen kannst:

$ ansible all -i 'localhost,' -a date

In der Realität wirst du mehrere Hosts verwalten. Die Sammlung von Hosts, die Ansible kennt, wird als Inventar bezeichnet. In diesem Kapitel lernst du, wie du eine Reihe von Hosts als Ansible-Inventar beschreibst, indem du ein Inventar erstellst, das mehrere Maschinen enthält.

Deine ansible.cfg-Datei sollte wie Beispiel 4-1 aussehen, in dem alle Inventar-Plug-ins explizit aktiviert sind.

Beispiel 4-1. ansible.cfg
[defaults]
inventory = inventory

[inventory]
enable_plugins = host_list, script, auto, yaml, ini, toml

In diesem Kapitel werden wir ein Verzeichnis namens inventory für die Inventarbeispiele verwenden. Das Ansible-Inventar ist ein sehr flexibles Objekt: Es kann eine Datei (in verschiedenen Formaten), ein Verzeichnis oder eine ausführbare Datei sein, und einige ausführbare Dateien sind als Plug-ins gebündelt. Inventar-Plug-ins ermöglichen es uns, auf Datenquellen wie deinen Cloud-Provider zu verweisen, um das Inventar zu erstellen. Ein Inventar kann getrennt von deinen Playbooks gespeichert werden. Das bedeutet, dass du ein Inventarverzeichnis erstellen kannst, das du mit Ansible auf der Kommandozeile, mit Hosts, die in Vagrant, Amazon EC2, Google Cloud Platform oder Microsoft Azure laufen, oder wo immer du willst, verwenden kannst!

Hinweis

Serge van Ginderachter ist die kompetenteste Person, die man zum Thema Ansible-Inventar lesen kann. In seinem Blog findest du ausführliche Details.

Inventar/Hosts-Dateien

Die Standardmethode, um deine Hosts in Ansible zu beschreiben, ist die Auflistung in Textdateien, den sogenannten Inventory-Hosts-Dateien. Die einfachste Form ist eine Liste von Hostnamen in einer Datei namens hosts, wie in Beispiel 4-2 gezeigt.

Beispiel 4-2. Eine sehr einfache Bestandsdatei
frankfurt.example.com
helsinki.example.com
hongkong.example.com
johannesburg.example.com
london.example.com
newyork.example.com
seoul.example.com
sydney.example.com

Ansible fügt standardmäßig einen Host zum Inventar hinzu: localhost. Es versteht, dass localhost sich auf deinen lokalen Rechner bezieht, mit dem es direkt interagiert, anstatt sich über SSH zu verbinden.

Vorbereitungen: Mehrere Vagrant-Maschinen

Um über das Inventar zu sprechen, musst du mit mehreren Hosts interagieren. Konfigurieren wir Vagrant so, dass er drei Hosts aufruft. Wir nennen sie einfallslos vagrant1, vagrant2, und vagrant3.

Bevor du eine neue Vagrantdatei für dieses Kapitel erstellst, stelle sicher, dass du deine bestehende(n) virtuelle(n) Maschine(n) zerstörst, indem du Folgendes ausführst:

$ vagrant destroy --force

Wenn du die Option --force nicht angibst, wird Vagrant dich auffordern, zu bestätigen, dass du jede virtuelle Maschine, die in der Vagrantdatei aufgeführt ist, zerstören willst.

Als nächstes erstellst du ein neues Vagrantfile, das wie Beispiel 4-3 aussieht.

Beispiel 4-3. Vagrantfile mit drei Servern
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  # Use the same key for each machine
  config.ssh.insert_key = false

  config.vm.define "vagrant1" do |vagrant1|
    vagrant1.vm.box = "ubuntu/focal64"
    vagrant1.vm.network "forwarded_port", guest: 80, host: 8080
    vagrant1.vm.network "forwarded_port", guest: 443, host: 8443
  end
  config.vm.define "vagrant2" do |vagrant2|
    vagrant2.vm.box = "ubuntu/focal64"
    vagrant2.vm.network "forwarded_port", guest: 80, host: 8081
    vagrant2.vm.network "forwarded_port", guest: 443, host: 8444
  end
  config.vm.define "vagrant3" do |vagrant3|
    vagrant3.vm.box = "centos/stream8"
    vagrant3.vm.network "forwarded_port", guest: 80, host: 8082
    vagrant3.vm.network "forwarded_port", guest: 443, host: 8445
  end
end

Vagrant verwendet ab Version 1.7 standardmäßig einen anderen SSH-Schlüssel für jeden Host. Beispiel 4-3 enthält die Zeile, mit der das frühere Verhalten, den gleichen SSH-Schlüssel für jeden Host zu verwenden, wiederhergestellt wird:

config.ssh.insert_key = false

Die Verwendung desselben Schlüssels auf jedem Host vereinfacht unser Ansible-Setup, da wir einen einzigen SSH-Schlüssel in der Konfiguration angeben können.

Nehmen wir an, dass jeder dieser Server ein Webserver sein kann. Beispiel 4-3 ordnet die Ports 80 und 443 in jedem Vagrant-Rechner einem Port auf dem lokalen Rechner zu.

Wir sollten in der Lage sein, die virtuellen Maschinen zu starten, indem wir Folgendes ausführen:

$ vagrant up

Wenn alles gut geht, sollte die Ausgabe ungefähr so aussehen:

Bringing machine 'vagrant1' up with 'virtualbox' provider...
Bringing machine 'vagrant2' up with 'virtualbox' provider...
Bringing machine 'vagrant3' up with 'virtualbox' provider...
...
    vagrant1: 80 (guest) => 8080 (host) (adapter 1)
    vagrant1: 443 (guest) => 8443 (host) (adapter 1)
    vagrant1: 22 (guest) => 2222 (host) (adapter 1)
==> vagrant1: Running 'pre-boot' VM customizations...
==> vagrant1: Booting VM...
==> vagrant1: Waiting for machine to boot. This may take a few minutes...
    vagrant1: SSH address: 127.0.0.1:2222
    vagrant1: SSH username: vagrant
    vagrant1: SSH auth method: private key
==> vagrant1: Machine booted and ready!
==> vagrant1: Checking for guest additions in VM...
==> vagrant1: Mounting shared folders...
    vagrant1: /vagrant => /Users/bas/code/ansible/ansiblebook/ansiblebook/ch03

Als Nächstes müssen wir wissen, welche Ports auf dem lokalen Rechner auf den SSH-Port (22) innerhalb jeder VM abgebildet sind. Wir können diese Informationen erhalten, indem wir Folgendes ausführen:

$ vagrant ssh-config

Die Ausgabe sollte in etwa so aussehen:

Host vagrant1
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/lorin/.vagrant.d/insecure_private_key
  IdentitiesOnly yes
  LogLevel FATAL
Host vagrant2
  HostName 127.0.0.1
  User vagrant
  Port 2200
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/lorin/.vagrant.d/insecure_private_key
  IdentitiesOnly yes
  LogLevel FATAL
Host vagrant3
  HostName 127.0.0.1
  User vagrant
  Port 2201
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/lorin/.vagrant.d/insecure_private_key
  IdentitiesOnly yes
  LogLevel FATAL

Viele der Informationen auf ssh-config wiederholen sich und können reduziert werden. Die Informationen, die sich pro Host unterscheiden, sind, dass vagrant1 den Port 2222, vagrant2 den Port 2200 und vagrant3 den Port 2201 verwendet.

Ansible verwendet standardmäßig deinen lokalen SSH-Client, was bedeutet, dass es alle Aliase versteht, die du in deiner SSH-Konfigurationsdatei einrichtest. Deshalb verwenden wir einen Platzhalter-Alias in der Datei ~/.ssh/config:

Host vagrant*
  Hostname 127.0.0.1
  User vagrant
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile ~/.vagrant.d/insecure_private_key
  IdentitiesOnly yes
  LogLevel FATAL

Ändere deine Inventar-/Hosts-Datei so, dass sie wie folgt aussieht:

vagrant1 ansible_port=2222
vagrant2 ansible_port=2200
vagrant3 ansible_port=2201

Stelle nun sicher, dass du auf diese Rechner zugreifen kannst. Um zum Beispiel Informationen über die Netzwerkschnittstelle von vagrant2 zu erhalten, führe Folgendes aus:

$ ansible vagrant2 -a "ip addr show dev enp0s3"

Deine Ausgabe sollte in etwa so aussehen:

vagrant2 | CHANGED | rc=0 >>
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP 
group default qlen 1000
    link/ether 02:1e:de:45:2c:c8 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
       valid_lft 86178sec preferred_lft 86178sec
    inet6 fe80::1e:deff:fe45:2cc8/64 scope link
       valid_lft forever preferred_lft forever

Parameter des Verhaltensinventars

Um unsere Vagrant-Maschinen in der Ansible-Inventardatei zu beschreiben, mussten wir explizit den Port (2222, 2200 oder 2201) angeben, mit dem sich der SSH-Client von Ansible verbinden sollte. Ansible nennt solche Variablen Verhaltensinventarisierungsparameter, und es gibt mehrere von ihnen, die du verwenden kannst, wenn du die Ansible-Standardeinstellungen für einen Host überschreiben willst (siehe Tabelle 4-1).

Tabelle 4-1. Parameter des Verhaltensinventars
Name Standard Beschreibung
ansible_host Name des Gastgebers Hostname oder IP-Adresse für die SSH-Verbindung
ansible_port 22 Port zu SSH zu
ansible_user $BENUTZER Benutzer zu SSH als
ansible_password (Keine) Passwort für die SSH-Authentifizierung
ansible_connection smart Wie Ansible eine Verbindung zum Host herstellt (siehe folgender Abschnitt)
ansible_ssh_private_key_file (Keine) Privater SSH-Schlüssel, der für die SSH-Authentifizierung verwendet wird
ansible_shell_type sh Shell, die für Befehle verwendet werden soll (siehe folgender Abschnitt)
ansible_python_interpreter /usr/bin/python Python-Interpreter auf dem Host (siehe den folgenden Abschnitt)
ansible_*_interpreter (Keine) Wie ansible_python_interpreter für andere Sprachen (siehe den folgenden Abschnitt)

Bei einigen dieser Optionen ist die Bedeutung aus dem Namen ersichtlich, aber andere erfordern mehr Erklärung:

ansible_connection

Ansible unterstützt mehrere Transporte, d.h. Mechanismen, die Ansible verwendet, um sich mit dem Host zu verbinden. Der Standardtransport, smart, prüft, ob der lokal installierte SSH-Client eine Funktion namens ControlPersist unterstützt. Wenn der SSH-Client ControlPersist unterstützt, wird Ansible den lokalen SSH-Client verwenden. Ist dies nicht der Fall, greift der intelligente Transport auf eine Python-basierte SSH-Client-Bibliothek namens Paramiko zurück.

ansible_shell_type

Ansible funktioniert, indem es SSH-Verbindungen zu entfernten Rechnern herstellt und dann Skripte aufruft. Standardmäßig geht Ansible davon aus, dass die entfernte Shell die Bourne-Shell ist, die sich unter /bin/sh befindet, und generiert die entsprechenden Befehlszeilenparameter, die mit dieser Shell funktionieren. Ansible legt temporäre Verzeichnisse an, um diese Skripte zu speichern.

Ansible akzeptiert auch csh, fish und (unter Windows) powershell als gültige Werte für diesen Parameter. Ansible funktioniert nicht mit eingeschränkten Shells.

ansible_python_interpreter

Ansible muss wissen, wo sich der Python-Interpreter auf dem entfernten Rechner befindet. Vielleicht möchtest du das ändern und eine Version wählen, die für dich geeignet ist. Der einfachste Weg, Ansible unter Python 3 laufen zu lassen, ist, es mit pip3 zu installieren und dies einzustellen:

ansible_python_interpreter="/usr/bin/env python3"
ansible_*_interpreter

Wenn du ein benutzerdefiniertes Modul verwendest, das nicht in Python geschrieben ist, kannst du mit diesem Parameter den Ort des Interpreters angeben (z. B. /usr/bin/ruby). Wir werden dies in Kapitel 12 behandeln.

Verhaltensparameter-Standardwerte ändern

Du kannst einige der Standardwerte der Verhaltensparameter in der Inventardatei oder im Abschnitt defaults der Datei ansible.cfg überschreiben(Tabelle 4-2). Überlege dir, wo du diese Parameter änderst. Sind die Änderungen eine persönliche Entscheidung oder gelten sie für dein ganzes Team? Braucht ein Teil deines Inventars eine andere Einstellung? Denke daran, dass du die SSH-Einstellungen in der Datei ~/.ssh/config konfigurieren kannst.

Tabelle 4-2. Standardeinstellungen, die in ansible.cfg überschrieben werden können
Parameter des Verhaltensinventars ansible.cfg Option
ansible_port remote_port
ansible_user remote_user
ansible_ssh_private_key_file ssh_private_key_file
ansible_shell_type executable (siehe den folgenden Absatz)

Die Option ansible.cfg executable config ist nicht genau das Gleiche wie der Parameter ansible_shell_type behavioral inventory. Die ausführbare Datei gibt den vollständigen Pfad der Shell an, die auf dem entfernten Rechner verwendet werden soll (zum Beispiel /usr/local/bin/fish). Ansible schaut sich den Basisnamen dieses Pfads an (in diesem Fall fish) und verwendet diesen als Standardwert für ansible_shell_type.

Gruppen und Gruppen und Gruppen

Normalerweise wollen wir die Konfiguration für Gruppen von Hosts durchführen und nicht für einen einzelnen Host. Ansible definiert automatisch eine Gruppe namens all (oder *), die alle Hosts im Inventar umfasst. Wir können zum Beispiel überprüfen, ob die Uhren auf den Rechnern ungefähr synchronisiert sind, indem wir Folgendes ausführen:

$ ansible all -a "date"

oder

$ ansible '*' -a "date"

Die Ausgabe auf Bas' System sieht folgendermaßen aus:

vagrant2 | CHANGED | rc=0 >>
Wed 12 May 2021 01:37:47 PM UTC
vagrant1 | CHANGED | rc=0 >>
Wed 12 May 2021 01:37:47 PM UTC
vagrant3 | CHANGED | rc=0 >>
Wed 12 May 2021 01:37:47 PM UTC

Wir können unsere eigenen Gruppen in der Inventar-Hosts-Datei definieren. Ansible verwendet das .ini-Dateiformat für Inventar-Hosts-Dateien; es gruppiert Konfigurationswerte in Abschnitte.

So gibst du an, dass unsere Vagrant-Hosts in einer Gruppe namens vagrant sind, zusammen mit den anderen Beispielhosts, die am Anfang des Kapitels erwähnt wurden:

frankfurt.example.com
helsinki.example.com
hongkong.example.com
johannesburg.example.com
london.example.com
newyork.example.com
seoul.example.com
sydney.example.com

[vagrant]
vagrant1 ansible_port=2222
vagrant2 ansible_port=2200
vagrant3 ansible_port=2201

Wir könnten die Vagrant-Hosts abwechselnd oben und dann auch in einer Gruppe auflisten, etwa so:

frankfurt.example.com
helsinki.example.com
hongkong.example.com
johannesburg.example.com
london.example.com
newyork.example.com
seoul.example.com
sydney.example.com
vagrant1 ansible_port=2222
vagrant2 ansible_port=2200
vagrant3 ansible_port=2201

[vagrant]
vagrant1
vagrant2
vagrant3

Du kannst Gruppen so verwenden, wie es dir passt: Sie können sich überschneiden oder ineinander verschachtelt sein, wie du willst. Die Reihenfolge spielt keine Rolle, außer für die Lesbarkeit.

Beispiel: Bereitstellen einer Django App

Stell dir vor, du bist für die Bereitstellung einer Django-basierten Webanwendung verantwortlich, die langlaufende Aufträge verarbeitet. Die Anwendung muss die folgenden Dienste unterstützen:

  • Die eigentliche Django-Webanwendung, die von einem Gunicorn HTTP-Server ausgeführt wird

  • Ein NGINX-Webserver, der vor Gunicorn sitzt und statische Inhalte bereitstellt

  • Eine Celery-Warteschlange, die langlaufende Aufträge im Namen der Webanwendung ausführt

  • Eine RabbitMQ-Nachrichtenwarteschlange, die als Backend für Celery dient

  • Eine Postgres-Datenbank, die als persistenter Speicher dient

In späteren Kapiteln werden wir ein detailliertes Beispiel für die Bereitstellung dieser Art von Django-basierter Anwendung durcharbeiten, obwohl unser Beispiel weder Celery noch RabbitMQ verwenden wird. Für den Moment müssen wir diese Anwendung in drei verschiedenen Umgebungen bereitstellen: Production (die echte Umgebung), Staging (zum Testen auf Hosts, auf die unser Team gemeinsamen Zugriff hat) und Vagrant (für lokale Tests).

Wenn wir in die Produktion gehen, wollen wir, dass das gesamte System schnell und zuverlässig reagiert, also tun wir Folgendes:

  • Lass die Webanwendung auf mehreren Hosts laufen, um die Leistung zu verbessern, und schalte einen Load Balancer davor.

  • Betreibe Task-Queue-Server auf mehreren Hosts für bessere Leistung

  • Lege Gunicorn, Celery, RabbitMQ und Postgres auf separate Server

  • Verwende zwei Postgres-Hosts: einen primären und einen Replikat

Angenommen, wir haben einen Load Balancer, drei Webserver, drei Task-Queues, einen RabbitMQ-Server und zwei Datenbankserver, dann sind das 10 Hosts, mit denen wir umgehen müssen(Abbildung 4-1).

Django application deployment
Abbildung 4-1. Zehn Hosts für die Bereitstellung einer Django-App

Für unsere Staging-Umgebung wollen wir weniger Hosts als in der Produktion verwenden, um Kosten zu sparen, da hier viel weniger Aktivität stattfinden wird als in der Produktion. Nehmen wir an, wir entscheiden uns, nur zwei Hosts für das Staging zu verwenden. Wir stellen den Webserver und die Task Queue auf einen Staging-Host und RabbitMQ und Postgres auf den anderen.

Für unsere lokale Vagrant-Umgebung entscheiden wir uns, drei Server zu verwenden: einen für die Web-App, einen für eine Task-Queue und einen, der RabbitMQ und Postgres enthält.

Beispiel 4-4 zeigt eine Beispielinventardatei, in der die Server nach Umgebung (Produktion, Staging, Vagrant) und Funktion (Webserver, Task Queue usw.) gruppiert sind.

Beispiel 4-4. Inventarisierungsdatei für die Bereitstellung einer Django-App
[production]
frankfurt.example.com
helsinki.example.com
hongkong.example.com
johannesburg.example.com
london.example.com
newyork.example.com
seoul.example.com
sydney.example.com
tokyo.example.com
toronto.example.com

[staging]
amsterdam.example.com
chicago.example.com

[lb]
helsinki.example.com

[web]
amsterdam.example.com
seoul.example.com
sydney.example.com
toronto.example.com
vagrant1

[task]
amsterdam.example.com
hongkong.example.com
johannesburg.example.com
newyork.example.com
vagrant2

[rabbitmq]
chicago.example.com
tokyo.example.com
vagrant3

[db]
chicago.example.com
frankfurt.example.com
london.example.com
vagrant3

Wir hätten zuerst alle Server am Anfang der Inventardatei auflisten können, ohne eine Gruppe anzugeben, aber das ist nicht nötig und hätte diese Datei noch länger gemacht.

Beachte, dass wir die Verhaltensinventarisierungsparameter für die Vagrant-Instanzen nur einmal angeben müssen.

Aliase und Ports

Wir haben unsere Vagrant Hosts wie folgt beschrieben:

[vagrant]
vagrant1 ansible_port=2222
vagrant2 ansible_port=2200
vagrant3 ansible_port=2201

Die Namen vagrant1, vagrant2 und vagrant3 sind Aliasnamen. Sie sind nicht die echten Hostnamen, sondern nur nützliche Namen, um auf diese Hosts zu verweisen. Ansible löst Hostnamen mithilfe des Inventars, deiner SSH-Konfigurationsdatei, /etc/hosts und DNS auf. Diese Flexibilität ist bei der Entwicklung nützlich, kann aber auch zu Verwirrung führen.

Ansible unterstützt auch die <hostname>:<port> Syntax bei der Angabe von Hosts, so dass wir die Zeile, die vagrant1 enthält, durch 127.0.0.1:2222 ersetzen können(Beispiel 4-5).

Beispiel 4-5. Das funktioniert nicht
[vagrant]
127.0.0.1:2222
127.0.0.1:2200
127.0.0.1:2201

Das, was du in Beispiel 4-5 siehst, können wir jedoch nicht wirklich ausführen. Der Grund dafür ist, dass das Inventar von Ansible nur einen einzigen Host mit 127.0.0.1 verknüpfen kann, sodass die Vagrant-Gruppe nur einen statt drei Hosts enthalten würde.

Gruppen von Gruppen

Mit Ansible kannst du auch Gruppen definieren, die sich aus anderen Gruppen zusammensetzen. Da zum Beispiel sowohl die Webserver als auch die Task Queue Server Django und seine Abhängigkeiten benötigen, könnte es sinnvoll sein, eine Gruppe django zu definieren, die beide enthält. Diese fügst du der Inventardatei hinzu:

[django:children]
web
task

Beachte, dass sich die Syntax ändert, wenn du eine Gruppe von Gruppen angibst, im Gegensatz zu einer Gruppe von Hosts. So kann Ansible web und task als Gruppen und nicht als Hosts interpretieren.

Nummerierte Wirte (Haustiere vs. Rinder)

Die Inventardatei, die du in Beispiel 4-4 gesehen hast, sieht komplex aus. Sie beschreibt 15 Hosts, was in dieser cloudigen, skalierbaren Welt nicht nach einer großen Zahl klingt. Allerdings kann der Umgang mit 15 Hosts in der Inventardatei mühsam sein, weil jeder Host einen völlig anderen Hostnamen hat.

Bill Baker von Microsoft hat den Unterschied zwischen der Behandlung von Servern als Haustiere und der Behandlung von Servern als Vieh herausgearbeitet.1 Wir geben Haustieren eindeutige Namen und behandeln und pflegen sie als Individuen; bei Rindern hingegen geben wir ihnen eine Identifikationsnummer und behandeln sie wie Vieh.

Der "Rinder"-Ansatz für Server ist viel skalierbarer, und Ansible unterstützt ihn gut, indem es numerische Muster unterstützt. Wenn deine 20 Server zum Beispiel web1.example.com, web2.example.com und so weiter heißen, kannst du sie in der Inventardatei wie folgt angeben:

[web]
web[1:20].example.com

Wenn du es vorziehst, dass eine führende Null hat (wie z.B. web01.example.com), gibst du das im Bereich wie folgt an:

[web]
web[01:20].example.com

Ansible unterstützt auch die Verwendung von alphabetischen Zeichen, um Bereiche anzugeben. Wenn du die Konvention web-a.example.com, web-b.example.com usw. für deine 20 Server verwenden möchtest, kannst du das unter tun:

[web]
web-[a:t].example.com

Hosts und Gruppenvariablen: Innerhalb des Inventars

Erinnere dich daran, wie wir verhaltensbezogenen Inventar Parameter für Vagrant-Hosts festlegen können:

vagrant1 ansible_host=127.0.0.1 ansible_port=2222
vagrant2 ansible_host=127.0.0.1 ansible_port=2200
vagrant3 ansible_host=127.0.0.1 ansible_port=2201

Diese Parameter sind Variablen, die für Ansible eine besondere Bedeutung haben. Wir können auch beliebige Variablennamen und zugehörige Werte für Hosts definieren. Wir könnten zum Beispiel eine Variable mit dem Namen color definieren und sie für jeden Server auf einen Wert setzen:

amsterdam.example.com color=red
seoul.example.com color=green
sydney.example.com color=blue
toronto.example.com color=purple

Wir könnten diese Variable dann wie jede andere Variable in einem Playbook verwenden. Deine Autoren verknüpfen Variablen nicht oft mit bestimmten Hosts. Andererseits verknüpfen wir Variablen oft mit Gruppen.

Um auf unser Django-Beispiel zurückzukommen, müssen die Webanwendung und der Task-Queue-Dienst mit RabbitMQ und Postgres kommunizieren. Wir gehen davon aus, dass der Zugriff auf die Postgres-Datenbank sowohl auf der Netzwerkebene (so dass nur die Webanwendung und die Task-Queue auf die Datenbank zugreifen können) als auch durch Benutzernamen und Passwort gesichert ist. RabbitMQ ist nur über die Netzwerkebene gesichert.

Um alles einzurichten, kannst du:

  • Konfiguriere die Webserver mit dem Hostnamen, Port, Benutzernamen und Passwort des primären Postgres-Servers und dem Namen der Datenbank.

  • Konfiguriere die Aufgabenwarteschlangen mit dem Hostnamen, dem Port, dem Benutzernamen und dem Passwort des primären Postgres-Servers und dem Namen der Datenbank.

  • Konfiguriere die Webserver mit dem Hostnamen und Port des RabbitMQ-Servers.

  • Konfiguriere die Task-Warteschlangen mit dem Hostnamen und Port des RabbitMQ-Servers.

  • Konfiguriere den primären Postgres-Server mit dem Hostnamen, dem Port sowie dem Benutzernamen und dem Passwort des Replikat-Postgres-Servers (nur Produktion).

Diese Konfigurationsinformationen variieren je nach Umgebung, daher ist es sinnvoll, sie als Gruppenvariablen für die Produktions-, Staging- und Vagrant-Gruppen zu definieren. Beispiel 4-6 zeigt eine Möglichkeit, dies in der Inventardatei zu tun. (Eine bessere Möglichkeit, Passwörter zu speichern, wird in Kapitel 8 besprochen).

Beispiel 4-6. Festlegen von Gruppenvariablen im Bestand
[all:vars]
ntp_server=ntp.ubuntu.com
[production:vars]
db_primary_host=frankfurt.example.com
db_primary_port=5432
db_replica_host=london.example.com
db_name=widget_production
db_user=widgetuser
db_password=pFmMxcyD;Fc6)6
rabbitmq_host=johannesburg.example.com
rabbitmq_port=5672
[staging:vars]
db_primary_host=chicago.example.com
db_primary_port=5432
db_name=widget_staging
db_user=widgetuser
db_password=L@4Ryz8cRUXedj
rabbitmq_host=chicago.example.com
rabbitmq_port=5672
[vagrant:vars]
db_primary_host=vagrant3
db_primary_port=5432
db_name=widget_vagrant
db_user=widgetuser
db_password=password
rabbitmq_host=vagrant3
rabbitmq_port=5672

Beachte, wie die Gruppenvariablen in Abschnitten mit dem Namen [<group name>:vars] organisiert sind. Außerdem haben wir uns die Gruppe all zunutze gemacht (die, wie du dich erinnern wirst, von Ansible automatisch erstellt wird), um Variablen festzulegen, die sich nicht zwischen den Hosts ändern.

Host- und Gruppenvariablen: In ihren eigenen Dateien

Die Inventardatei ist ein guter Ort, um Host- und Gruppenvariablen zu speichern, wenn du nicht zu viele Hosts hast. Aber je größer dein Inventar wird, desto schwieriger wird es, die Variablen auf diese Weise zu verwalten. Auch wenn Ansible-Variablen Boolesche Werte, Strings, Listen und Wörterbücher enthalten können, kannst du in einer Inventardatei nur Boolesche Werte und Strings angeben.

Ansible bietet einen skalierbareren Ansatz, um Host- und Gruppenvariablen zu verwalten: Du kannst für jeden Host und jede Gruppe eine eigene Variablendatei erstellen. Ansible erwartet, dass diese Variablendateien im YAML-Format vorliegen.

Es sucht nach Host-Variablendateien in einem Verzeichnis namens host_vars und nach Gruppenvariablendateien in einem Verzeichnis namens group_vars. Ansible erwartet, dass sich diese Verzeichnisse entweder in dem Verzeichnis befinden, das deine Playbooks enthält, oder in dem Verzeichnis neben deiner Inventardatei. Wenn du beide Verzeichnisse hast, hat das erste (das Playbook-Verzeichnis) Vorrang.

Wenn Lorin zum Beispiel ein Verzeichnis mit seinen Playbooks unter /home/lorin/playbooks/ und ein Inventarverzeichnis und eine Hosts-Datei unter /home/lorin/inventory/hosts hat, sollte er die Variablen für den Host amsterdam.example.com in die Datei /home/lorin/inventory/host_vars/amsterdam.example.com und die Variablen für die Produktionsgruppe in die Datei /home/lorin/inventory/group_vars/production legen (siehe Beispiel 4-7).

Beispiel 4-7. group_vars/production
---
db_primary_host: frankfurt.example.com
db_primary_port: 5432
db_replica_host: london.example.com
db_name: widget_production
db_user: widgetuser
db_password: 'pFmMxcyD;Fc6)6'
rabbitmq_host: johannesburg.example.com
rabbitmq_port: 5672
...

Wir können auch YAML-Wörterbücher verwenden, um diese Werte darzustellen, wie in Beispiel 4-8 gezeigt.

Beispiel 4-8. group_vars/production, mit Wörterbüchern
---
db:
  user: widgetuser
  password: 'pFmMxcyD;Fc6)6'
  name: widget_production
  primary:
    host: frankfurt.example.com
    port: 5432
  replica:
    host: london.example.com
    port: 5432
rabbitmq:
  host: johannesburg.example.com
  port: 5672
...

Wenn wir uns für YAML-Wörterbücher entscheiden, greifen wir auf die Variablen mit der Punktnotation wie folgt zu:

"{{ db.primary.host }}"

Wir können auf die Variablen im Wörterbuch auch wie folgt zugreifen:

"{{ db['primary']['host'] }}"

Das steht im Gegensatz dazu, wie wir sonst auf sie zugreifen würden:

"{{ db_primary_host }}"

Wenn wir die Dinge noch weiter aufbrechen wollen, können wir mit Ansible group_vars/production als Verzeichnis statt als Datei definieren. Darin können wir mehrere YAML-Dateien mit Variablendefinitionen ablegen. Wir könnten zum Beispiel datenbankbezogene Variablen in eine Datei und die RabbitMQ-bezogenen Variablen in eine andere Datei packen, wie in den Beispielen 4-9 und 4-10 gezeigt.

Beispiel 4-9. group_vars/production/db
---
db:
  user: widgetuser
  password: 'pFmMxcyD;Fc6)6'
  name: widget_production
  primary:
    host: frankfurt.example.com
    port: 5432
  replica:
    host: london.example.com
    port: 5432
...
Beispiel 4-10. group_vars/production/rabbitmq
---
rabbitmq:
  host: johannesburg.example.com
  port: 6379
...

Es ist oft besser, einfach anzufangen, als die Variablen auf zu viele Dateien aufzuteilen. In größeren Teams und Projekten steigt der Wert von separaten Dateien, da viele Personen gleichzeitig auf Dateien zugreifen und darin arbeiten müssen.

Dynamisches Inventar

Bis zu diesem Punkt haben wir alle unsere Hosts explizit in unserer Hosts-Inventarisierungsdatei angegeben. Vielleicht hast du aber auch ein System außerhalb von Ansible, das deine Hosts im Auge behält. Wenn deine Hosts zum Beispiel auf Amazon EC2 laufen, verfolgt EC2 die Informationen über deine Hosts für dich. Du kannst diese Informationen über die EC2-Weboberfläche, die Query-API oder über Befehlszeilen-Tools wie awscli abrufen. Andere Cloud-Provider haben ähnliche Schnittstellen.

Wenn du deine eigenen Server mit einem automatischen Bereitstellungssystem wie Cobbler oder Ubuntu Metal as a Service (MAAS) verwaltest, dann behält dein System bereits den Überblick über deine Server. Vielleicht hast du aber auch eine dieser schicken Konfigurationsmanagement-Datenbanken (CMDBs), in denen all diese Informationen gespeichert sind.

Du willst diese Informationen nicht manuell in deiner Hosts-Datei duplizieren, denn dann stimmt diese Datei nicht mehr mit deinem externen System überein, das die eigentliche Quelle der Informationen über deine Hosts ist. Ansible unterstützt eine Funktion namens dynamische Inventarisierung, mit der du diese Duplizierung vermeiden kannst.

Wenn die Inventardatei als ausführbar markiert ist, nimmt Ansible an, dass es sich um ein dynamisches Inventarskript handelt und führt die Datei aus, anstatt sie zu lesen.

Hinweis

Um eine Datei als ausführbar zu markieren, verwende den Befehl chmod +x. Zum Beispiel:

$ chmod +x vagrant.py

Inventarisierungs-Plug-ins

Ansible wird mit verschiedenen Plug-ins ausgeliefert, die sich mit verschiedenen Cloud-Systemen verbinden können, sofern du die Voraussetzungen installierst und die Authentifizierung einrichtest. Diese Plug-ins benötigen normalerweise eine YAML-Konfigurationsdatei im Inventarverzeichnis sowie einige Umgebungsvariablen oder Authentifizierungsdateien.

Um die Liste der verfügbaren Plug-ins zu sehen:

$ ansible-doc -t inventory -l

Hier findest du die Plug-in-spezifische Dokumentation und Beispiele:

$ ansible-doc -t inventory <plugin name>

Amazon EC2

Wenn du Amazon EC2 verwendest, installiere die Anforderungen:

$ pip3 install boto3 botocore

Erstelle eine Datei inventory/aws_ec2.yml, die mindestens Folgendes enthält:

plugin: aws_ec2

Azure Resource Manager

Installiere diese Anforderungen in einer Python Virtualenv mit Ansible 2.9.xx:

$ pip3 install msrest msrestazure

Erstelle eine Datei inventory/azure_rm.yml, die mindestens Folgendes enthält:

plugin: azure_rm
platform: azure_rm
auth_source: auto
plain_host_names: true

Die Schnittstelle für ein dynamisches Inventarisierungsskript

Ein dynamisches Ansible-Inventarskript muss zwei Befehlszeilenflags unterstützen:

  • --host=<hostname> zur Anzeige von Gastgeberdetails

  • --list für Listengruppen

Außerdem sollte es eine Ausgabe im JSON-Format mit einer bestimmten Struktur zurückgeben, die Ansible interpretieren kann.

Gastgeberdetails anzeigen

Um die Details des einzelnen Hosts zu erhalten, ruft Ansible ein Inventarskript mit dem Argument --host= auf:

$ ansible-inventory -i inventory/hosts --host=vagrant2
Hinweis

Ansible enthält ein Skript, das als dynamisches Inventarisierungsskript für das statische Inventar fungiert, das mit dem Kommandozeilenargument -i bereitgestellt wird: ansible-inventory.

Die Ausgabe sollte alle hostspezifischen Variablen enthalten, einschließlich der Verhaltensparameter, wie hier:

{
    "ansible_host": "127.0.0.1",
    "ansible_port": 2200,
    "ansible_ssh_private_key_file": "~/.vagrant.d/insecure_private_key",
    "ansible_user": "vagrant"
} 

Die Ausgabe ist ein einzelnes JSON-Objekt; die Namen sind die Variablennamen und die Werte sind die Variablenwerte.

Gruppen auflisten

Dynamische Inventarskripte müssen in der Lage sein, alle Gruppen und Details zu den einzelnen Hosts aufzulisten. Im GitHub-Repository, das diesem Buch beiliegt, gibt es ein Inventarisierungsskript für die Vagrant-Hosts namens vagrant.py. Ansible ruft es wie folgt auf, um eine Liste aller Gruppen zu erhalten:

$ ./vagrant.py --list

In der einfachsten Form könnte die Ausgabe wie folgt aussehen:

{"vagrant": ["vagrant1", "vagrant2", "vagrant3"]}

Diese Ausgabe ist ein einzelnes JSON-Objekt; die Namen sind Ansible-Gruppennamen und die Werte sind Arrays von Hostnamen.

Als Optimierung von kann der Befehl --list die Werte der Host-Variablen für alle Hosts enthalten, was Ansible die Mühe erspart, einen separaten --host -Aufruf zu machen, um die Variablen für die einzelnen Hosts abzurufen.

Um diese Optimierung zu nutzen, sollte der Befehl --list einen Schlüssel namens _meta zurückgeben, der die Variablen für jeden Host in dieser Form enthält:

"_meta": {
    "hostvars": {
      "vagrant1": {
        "ansible_user": "vagrant",
        "ansible_host": "127.0.0.1",
        "ansible_ssh_private_key_file": "~/.vagrant.d/insecure_private_key",
        "ansible_port": "2222"
      },
      "vagrant2": {
        "ansible_user": "vagrant",
        "ansible_host": "127.0.0.1",
        "ansible_ssh_private_key_file": "~/.vagrant.d/insecure_private_key",
        "ansible_port": "2200"
      },
      "vagrant3": {
        "ansible_user": "vagrant",
        "ansible_host": "127.0.0.1",
        "ansible_ssh_private_key_file": "~/.vagrant.d/insecure_private_key",
        "ansible_port": "2201"
      }
    }

Ein dynamisches Inventarisierungsskript schreiben

Eine der praktischen Funktionen von Vagrant ist, dass du mit dem Befehl vagrant status sehen kannst, welche Maschinen gerade laufen. Angenommen, wir haben eine Vagrant-Datei, die wie Beispiel 4-3 aussieht. Wenn wir vagrant status ausführen, würde die Ausgabe wie in Beispiel 4-11 aussehen.

Beispiel 4-11. Ausgabe von vagrant status
$ vagrant status
Current machine states:

vagrant1                 running (virtualbox)
vagrant2                 running (virtualbox)
vagrant3                 running (virtualbox)

This environment represents multiple VMs. The VMs are all listed
above with their current state. For more information about a specific
VM, run 'vagrant status NAME'.

Da Vagrant die Maschinen bereits für uns im Auge behält, müssen wir sie nicht in einer Ansible-Inventardatei auflisten. Stattdessen können wir ein dynamisches Inventarisierungsskript schreiben, das Vagrant abfragt, welche Maschinen laufen. Sobald wir ein dynamisches Inventarisierungsskript für Vagrant eingerichtet haben, müssen wir die Ansible-Inventarisierungsdatei nicht mehr bearbeiten, selbst wenn wir unsere Vagrant-Datei ändern, um eine andere Anzahl von Vagrant-Maschinen zu betreiben.

Gehen wir ein Beispiel für die Erstellung eines dynamischen Inventarisierungsskripts durch, das die Details über die Hosts von Vagrant abruft. Unser dynamisches Inventarskript muss den Befehl vagrant status aufrufen. Die in Beispiel 4-11 gezeigte Ausgabe ist für Menschen lesbar. Mit dem Flag --machine-readable können wir eine Liste der laufenden Hosts in einem Format erhalten, das von Computern leichter zu analysieren ist, etwa so:

$ vagrant status --machine-readable

Die Ausgabe sieht wie folgt aus:

1620831617,vagrant1,metadata,provider,virtualbox
1620831617,vagrant2,metadata,provider,virtualbox
1620831618,vagrant3,metadata,provider,virtualbox
1620831619,vagrant1,provider-name,virtualbox
1620831619,vagrant1,state,running
1620831619,vagrant1,state-human-short,running
1620831619,vagrant1,state-human-long,The VM is running. To stop this 
VM%!(VAGRANT_COMMA) you can run `vagrant halt` to\nshut it down 
forcefully%!(VAGRANT_COMMA) or you can run `vagrant suspend` to 
simply\nsuspend the virtual machine. In either case%!(VAGRANT_COMMA) 
to restart it again%!(VAGRANT_COMMA)\nsimply run `vagrant up`.
1620831619,vagrant2,provider-name,virtualbox
1620831619,vagrant2,state,running
1620831619,vagrant2,state-human-short,running
1620831619,vagrant2,state-human-long,The VM is running. To stop this 
VM%!(VAGRANT_COMMA) you can run `vagrant halt` to\nshut it down 
forcefully%!(VAGRANT_COMMA) or you can run `vagrant suspend` to 
simply\nsuspend the virtual machine. In either case%!(VAGRANT_COMMA) 
to restart it again%!(VAGRANT_COMMA)\nsimply run `vagrant up`.
1620831620,vagrant3,provider-name,virtualbox
1620831620,vagrant3,state,running
1620831620,vagrant3,state-human-short,running
1620831620,vagrant3,state-human-long,The VM is running. To stop this 
VM%!(VAGRANT_COMMA) you can run `vagrant halt` to\nshut it down 
forcefully%!(VAGRANT_COMMA) or you can run `vagrant suspend` to 
simply\nsuspend the virtual machine. In either case%!(VAGRANT_COMMA) 
to restart it again%!(VAGRANT_COMMA)\nsimply run `vagrant up`.
1620831620,,ui,info,Current machine states:\n\nvagrant1                  
running (virtualbox)\nvagrant2         running (virtualbox)\nvagrant3
running (virtualbox)\n\nThis environment represents multiple VMs. The VMs 
are all listed\nabove with their current state. For more information about 
a specific\nVM%!(VAGRANT_COMMA) run `vagrant status NAME`

Um Details über eine bestimmte Vagrant-Maschine, z.B. vagrant2, zu erhalten, würden wir Folgendes ausführen:

$ vagrant ssh-config vagrant2

Die Ausgabe sieht wie folgt aus:

Host vagrant2
  HostName 127.0.0.1
  User vagrant
  Port 2200
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/lorin/.vagrant.d/insecure_private_key
  IdentitiesOnly yes
  LogLevel FATAL

Unser dynamisches Inventarisierungsskript muss diese Befehle aufrufen, die Ausgaben parsen und das entsprechende JSON ausgeben. Wir können die Paramiko-Bibliothek verwenden, um die Ausgabe von vagrant ssh-config zu parsen. Installiere zunächst die Python Paramiko-Bibliothek mit pip:

$ pip3 install --user paramiko

Hier ist eine interaktive Python-Sitzung, die zeigt, wie man die Paramiko-Bibliothek dafür verwendet:

$ python3
>>> import io
>>> import subprocess
>>> import paramiko
>>> cmd = ["vagrant", "ssh-config", "vagrant2"]
>>> ssh_config = subprocess.check_output(cmd).decode("utf-8")
>>> config = paramiko.SSHConfig()
>>> config.parse(io.StringIO(ssh_config))
>>> host_config = config.lookup("vagrant2")
>>> print (host_config)
{'hostname': '127.0.0.1', 'user': 'vagrant', 'port': '2200', 'userknownhostsfile': 
'/dev/null', 'stricthostkeychecking': 'no', 'passwordauthentication': 'no', 
'identityfile': ['/Users/bas/.vagrant.d/insecure_private_key'], 'identitiesonly': 
'yes', 'loglevel': 'FATAL'}

Beispiel 4-12 zeigt unser komplettes vagrant.py-Skript.

Beispiel 4-12. vagrant.py
#!/usr/bin/env python3
""" Vagrant inventory script """
# Adapted from Mark Mandel's implementation
# https://github.com/markmandel/vagrant_ansible_example

import argparse
import io
import json
import subprocess
import sys

import paramiko


def parse_args():
    """command-line options"""
    parser = argparse.ArgumentParser(description="Vagrant inventory script")
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('--list', action='store_true')
    group.add_argument('--host')
    return parser.parse_args()


def list_running_hosts():
    """vagrant.py --list function"""
    cmd = ["vagrant", "status", "--machine-readable"]
    status = subprocess.check_output(cmd).rstrip().decode("utf-8")
    hosts = []
    for line in status.splitlines():
        (_, host, key, value) = line.split(',')[:4]
        if key == 'state' and value == 'running':
            hosts.append(host)
    return hosts


def get_host_details(host):
    """vagrant.py --host <hostname> function"""
    cmd = ["vagrant", "ssh-config", host]
    ssh_config = subprocess.check_output(cmd).decode("utf-8")
    config = paramiko.SSHConfig()
    config.parse(io.StringIO(ssh_config))
    host_config = config.lookup(host)
    return {'ansible_host': host_config['hostname'],
            'ansible_port': host_config['port'],
            'ansible_user': host_config['user'],
            'ansible_private_key_file': host_config['identityfile'][0]}


def main():
    """main"""
    args = parse_args()
    if args.list:
        hosts = list_running_hosts()
        json.dump({'vagrant': hosts}, sys.stdout)
    else:
        details = get_host_details(args.host)
        json.dump(details, sys.stdout)


if __name__ == '__main__':
    main()

Aufteilung des Inventars in mehrere Dateien

Wenn du sowohl eine reguläre Inventarisierungsdatei als auch ein dynamisches Inventarisierungsskript (oder eine beliebige Kombination aus statischen und dynamischen Inventarisierungsdateien) haben möchtest, legst du sie einfach unter im selben Verzeichnis ab und konfigurierst Ansible so, dass es dieses Verzeichnis als Inventar verwendet. Das kannst du mit dem Parameter inventory in der Datei ansible.cfg oder mit dem Flag -i in der Befehlszeile tun. Ansible wird alle Dateien verarbeiten und die Ergebnisse in einem einzigen Inventar zusammenführen.

Das bedeutet, dass du ein einziges Inventarverzeichnis erstellen kannst, das du mit Ansible auf der Kommandozeile mit Hosts verwenden kannst, die in Vagrant, Amazon EC2, Google Cloud Platform, Microsoft Azure oder wo immer du sie brauchst, laufen.

Die Verzeichnisstruktur von Bas sieht zum Beispiel so aus:

  • inventory/aws_ec2.yml
  • inventory/azure_rm.yml
  • inventory/group_vars/vagrant
  • inventory/group_vars/staging
  • inventory/group_vars/production
  • Inventar/Hosts
  • inventory/vagrant.py

Hinzufügen von Einträgen zur Laufzeit mit add_host und group_by

Mit Ansible kannst du während der Ausführung eines Playbooks Hosts und Gruppen zum Inventar hinzufügen. Das ist nützlich, wenn du dynamische Cluster wie Redis Sentinel verwaltest.

add_host

Das Modul add_host fügt einen Host zum Inventar hinzu. Dies ist nützlich, wenn du Ansible zur Bereitstellung neuer virtueller Maschineninstanzen in einer Infrastructure-as-a-Service-Cloud verwendest.

Der Aufruf des Moduls sieht folgendermaßen aus:

- name: Add the host
  add_host
    name: hostname
    groups: web,staging
    myvar: myval

Die Angabe der Liste von Gruppen und zusätzlichen Variablen ist optional.

Hier siehst du den Befehl add_host in Aktion, mit dem du eine neue Vagrant-Maschine startest und dann konfigurierst:

---
- name: Provision a Vagrant machine
  hosts: localhost
  vars:
    box: centos/stream8

  tasks:
    - name: Create a Vagrantfile
      command: "vagrant init {{ box }}"
      args:
        creates: Vagrantfile

    - name: Bring up the vagrant machine
      command: vagrant up
      args:
        creates: .vagrant/machines/default/virtualbox/box_meta

    - name: Add the vagrant machine to the inventory
      add_host:
        name: default
        ansible_host: 127.0.0.1
        ansible_port: 2222
        ansible_user: vagrant
        ansible_private_key_file: >
          .vagrant/machines/default/virtualbox/private_key

- name: Do something to the vagrant machine
  hosts: default
  tasks:
    # The list of tasks would go here
    - name: ping
      ping:
...
Hinweis

Das Modul add_host fügt den Host nur für die Dauer der Ausführung des Playbooks hinzu. Es ändert deine Bestandsdatei nicht.

Wenn wir die Bereitstellung in unseren Playbooks vornehmen, teilen wir sie gerne in zwei Schritte auf. Das erste Spiel läuft gegen localhost und stellt die Hosts bereit, das zweite Spiel konfiguriert die Hosts.

Beachte, dass in dieser Aufgabe das Argument creates: Vagrantfile verwendet:

- name: Create a Vagrantfile
  command: "vagrant init {{ box }}"
  args:
    creates: Vagrantfile

Damit wird Ansible mitgeteilt, dass der Befehl nicht erneut ausgeführt werden muss, wenn die Vagrantfile-Datei vorhanden ist. Indem du sicherstellst, dass der (potenziell nicht idempotente) Befehl nur einmal ausgeführt wird, erreichst du Idempotenz in einem Playbook, das das Modul command aufruft. Das Gleiche gilt für das vagrant up Befehlsmodul.

group_by

Mit dem Modul group_by von Ansible kannst du neue Gruppen erstellen, während ein Playbook ausgeführt wird. Jede Gruppe, die du erstellst, basiert auf dem Wert einer Variable, die auf jedem Host gesetzt wurde und die Ansible als Fakt bezeichnet.(In Kapitel 5 werden Fakten ausführlicher behandelt.)

Wenn das Ansible Fact Gathering aktiviert ist, ordnet Ansible eine Reihe von Variablen einem Host zu. Zum Beispiel wird die Variable ansible_machine für 32-Bit x86-Maschinen zu i386 und für 64-Bit x86-Maschinen zu x86_64. Wenn Ansible mit einer Mischung aus solchen Hosts interagiert, können wir mit der Aufgabe die Gruppen i386 und x86_64 erstellen.

Wenn wir unsere Hosts lieber nach Linux-Distributionen gruppieren möchten (z.B. Ubuntu oder CentOS), können wir die ansible_fact.distribution Tatsache nutzen:

- name: Create groups based on Linux distribution
  group_by:
    key: "{{ ansible_facts.distribution }}"

In Beispiel 4-13 verwenden wir group_by, um getrennte Gruppen für unsere Ubuntu- und CentOS-Hosts zu erstellen. Dann verwenden wir das Modul apt, um Pakete auf Ubuntu zu installieren und das Modul yum, um Pakete auf CentOS zu installieren.

Beispiel 4-13. Ad-hoc-Gruppen basierend auf der Linux-Distribution erstellen
---

- name: Group hosts by distribution
  hosts: all
  gather_facts: true
  tasks:
    - name: Create groups based on distro
      group_by:
        key: "{{ ansible_facts.distribution }}"

- name: Do something to Ubuntu hosts
  hosts: Ubuntu
  become: true
  tasks:
    - name: Install jdk and jre
      apt:
        update_cache: true
        name:
          - openjdk-11-jdk-headless
          - openjdk-11-jre-headless

- name: Do something else to CentOS hosts
  hosts: CentOS
  become: true
  tasks:
    - name: Install jdk
      yum:
        name:
          - java-11-openjdk-headless
          - java-11-openjdk-devel

Fazit

Das war's auch schon mit dem Inventar von Ansible. Es ist ein sehr flexibles Objekt, das dabei hilft, deine Infrastruktur und die Art und Weise, wie du sie nutzen willst, zu beschreiben. Das Inventar kann so einfach wie eine Textdatei oder so komplex sein, wie du es dir vorstellen kannst.

Im nächsten Kapitel geht es darum, wie man Variablen verwendet.

1 Dieser Begriff wurde von Randy Bias von Cloudscaling populär gemacht.

Get Ansible: Up and Running, 3. Auflage now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.