Seite wählen

Ansible

Kapitel 1 Ansible Playbook

Im Kapitel: Ansible Schritt für Schritt wurde unser Plan beschrieben. Dieses Kapitel 1 beginnt nun von Grund auf. Hier wird nun Schritt für Schritt Ansible erklärt, eingerichtet und ein  1. Playbook erstellt. 

 

Hinweis

Beispiele und Code auf: Gitlab

Bitte beachten:

In dieser Dokumentenserie  gehen wir von Zielumgebung Ubuntu 22.04.2 und Ansible 2.10 aus. Vereinzelt gehen wir auf Opensuse Leap 15.4 ein. Bei anderen Distributionen und Versionsstände können nachfolgende Beispiele u.U. nicht funktionieren.

Die Installation:

Um mit Ansible zu beginnen muss zunächst die notwendigen Software installiert werden. Unser Rechner basiert auf Ubuntu 22.04.2. Sie benötigen somit einen Rechner mit Ubuntu 22.04.2.
Bitte auf der Console als Benutzer root anmelden.

Bestätigen mit Y und die Installation beginnt.

apt install ansible

Das Ergebnis:

Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  ieee-data python-babel-localedata python3-argcomplete python3-babel python3-certifi python3-dnspython
  python3-jinja2 python3-jmespath python3-kerberos python3-libcloud python3-lockfile python3-markupsafe
  python3-netaddr python3-ntlm-auth python3-packaging python3-pycryptodome python3-requests
  python3-requests-kerberos python3-requests-ntlm python3-requests-toolbelt python3-selinux python3-simplejson
  python3-tz python3-urllib3 python3-winrm python3-xmltodict
Suggested packages:
  cowsay sshpass python3-sniffio python3-trio python-jinja2-doc python-lockfile-doc ipython3 python-netaddr-docs
  python3-socks python-requests-doc
The following NEW packages will be installed:
  ansible ieee-data python-babel-localedata python3-argcomplete python3-babel python3-certifi python3-dnspython
  python3-jinja2 python3-jmespath python3-kerberos python3-libcloud python3-lockfile python3-markupsafe
  python3-netaddr python3-ntlm-auth python3-packaging python3-pycryptodome python3-requests
  python3-requests-kerberos python3-requests-ntlm python3-requests-toolbelt python3-selinux python3-simplejson
  python3-tz python3-urllib3 python3-winrm python3-xmltodict
0 upgraded, 27 newly installed, 0 to remove and 2 not upgraded.
Need to get 28.4 MB of archives.
After this operation, 272 MB of additional disk space will be used.
Do you want to continue? [Y/n] Y

Damit ist die Installation von Ansbible abgeschlossen. Kompliziert war das nicht.

Ansible Basics

Was ist ein inventory?

Das Inventory in Ansible ist eine Datei oder eine Gruppe von Dateien, die Informationen über die Hosts enthält, auf denen Ansible-Playbooks ausgeführt werden sollen. Diese Informationen beinhalten die IP-Adressen oder Hostnamen der zu verwaltenden Hosts sowie zusätzliche Attribute wie z.B. Gruppenzugehörigkeit, Variablenwerte oder SSH-Verbindungsoptionen.

YAML verwendet eine einfache Syntax, die durch Einrückungen und Leerzeichen gekennzeichnet ist. Ein YAML-Dokument besteht aus einer Reihe von Schlüssel-Wert-Paaren, welche durch Doppelpunkte getrennt sind. Jeder Schlüssel-Wert-Paar beginnt mit dem Schlüssel, gefolgt von einem Doppelpunkt und dem Wert. Der Wert kann ein String, eine Zahl, ein Array, ein Objekt oder eine andere komplexe Struktur sein.

Oder um es ganz kurz zu sagen: Yaml ist eine „Liste“, welche weitere Listen und Attribute beinhaltet. 

Hier ein kleines Beispiel als Host: (btw. das werden wir so nicht verwenden)!

host: mx5 
system: Ubuntu
version: 22.04.2 
network: 
  eth0: 
    ipv4:
      - 123.123.123.5/24
- 123.123.124.5/24 ipv6:

- fe80::4827:43ff:fe91:d516/64 filesystem: root:
size: 64G
type: ext
home:
size: 10G
type: xfs

In diesem Beispiel gibt es 15 Schlüssel-Wert-Paare.

  • Der erste Schlüssel ist „host“ und der Wert ist „mx5“.
  • Der zweite Schlüssel ist „system“ und der Wert ist „Ubuntu“.
  • Der dritte Schlüssel ist „version“ und der Wert ist „22.04.2“
  • Der vierte Schlüssel „network“ ist ein Objekt, das drei weitere Schlüssel-Wert-Paare enthält.
  • Der achte Schlüssel „filesystem“ ist ebenfalls wieder ein Objekt mit 6 weiteren Schlüssel-Werte Paaren.

Jedes dieser Schlüssel-Wert-Paare ist durch Einrückung vom Elternschlüssel getrennt. Eine Liste an Werten kann man wie bei Schlüssel ipv4, mit „- „eingeleitet anfügen. 

YAML kann auch verwendet werden, um Listen und komplexe Strukturen zu definieren. Zum Beispiel könnte eine Liste von Objektern (hier Hosts) wie folgt aussehen:

 

- host: mx1 
  system: openSuSe
  version: 15.4 
  network: 
   eth0: 
     ipv4:
       - 10.10.10.1/24
       - 10.120.10.1/24
     ipv6:
        - fe80::1827:43ff:fe90:d516/64
  filesystem: 
     root:
       size: 64G
       type: ext
     home:
       size: 10G
       type: xfs
- host: mx5  
  system: Ubuntu 
  version: 22.04.2  
  network:  
    eth0: 
      ipv4: 
        - 10.10.10.5/24
        - 10.120.10.5/24 
      ipv6:
        - fe80::4827:43ff:fe91:d516/64  
  filesystem:  
    root:
      size: 64G
      type: ext
    home:
      size: 10G
      type: xfs

In diesem Beispiel gibt es eine Liste von zwei Objekten, die jeweils die Informationen eines Servers enthalten.

YAML ist sehr flexibel und kann auf unterschiedliche Weise verwendet werden, einschließlich der Definition von Konfigurationen, der Definition von Datenstrukturen und der Definition von Testspezifikationen. YAML-Dateien können auch von anderen Anwendungen gelesen und geschrieben werden, was sie zu einem nützlichen Austauschformat macht.

Das Inventory kann in verschiedenen Formaten vorliegen, darunter INI-Dateien, YAML-Dateien oder dynamische Inventarquellen wie Cloud-Provider-APIs oder CMDBs. Die INI-Formatierung hat dabei eine lange Historie und ist ein einfaches textbasiertes Format, welches in älteren Ansible-Versionen bevorzugt wurde. Das YAML-Format ist hingegen moderner und flexibler, da es ein leicht lesbares und strukturiertes Datenformat ist.

Mithilfe von Gruppenzugehörigkeiten und Variablenzuweisungen können Hosts innerhalb des Inventars in logische Gruppen unterteilt werden, um sie entsprechend zu konfigurieren. Das erleichtert insbesondere die Wartung und Skalierung von großen Infrastrukturen, da die gewünschten Konfigurationen über eine Gruppenzuweisung auf eine Vielzahl von Hosts angewendet werden können.

Das Inventory wird üblicherweise als Eingabe für Playbooks und Ad-hoc-Befehle verwendet, um eine gezielte Konfiguration von Hosts oder Gruppen von Hosts zu ermöglichen. Es ist wichtig, dass das Inventory immer auf dem neuesten Stand gehalten wird, um die korrekte Verwaltung der Hosts zu gewährleisten.

Ansible Basics

Was ist ein Playbook

Ansible Playbooks sind eine zentrale Komponente von Ansible, mit der Automatisierung von IT-Infrastruktur durchgeführt wird. Ein Playbook ist eine Datei, die eine Abfolge von Schritten definiert, die von Ansible auf den verwalteten Hosts ausgeführt werden sollen. Diese Schritte werden als Aufgaben (tasks) bezeichnet und können von einfachen Shell-Befehlen bis hin zu komplexen Deployment- oder Konfigurationsaufgaben reichen.

Ein Playbook besteht aus einer YAML-Datei und enthält eine oder mehrere Plays. Ein Play definiert eine Gruppe von Aufgaben, die auf einer bestimmten Gruppe von Hosts ausgeführt werden sollen. Jede Aufgabe enthält eine spezifische Aktion, die von Ansible auf dem Host ausgeführt werden soll, sowie zusätzliche Optionen wie zum Beispiel die Nutzung von Variablen.

Playbooks ermöglichen es, komplexe Infrastrukturkonfigurationen einfach und reproduzierbar zu automatisieren, da sie eine einzige Quelle der Wahrheit für die gesamte Konfiguration bereitstellen. Mit Hilfe von Playbooks kann man also sehr schnell eine Vielzahl von Konfigurationsänderungen und Updates auf vielen Hosts durchführen. Zusätzlich ermöglichen Playbooks eine automatisierte und dokumentierte Wartung von Systemen, was insbesondere in größeren Infrastrukturen eine enorme Erleichterung darstellt.

Zusätzlich zu den Aufgaben, die in einem Playbook definiert sind, können Playbooks auch Variablen, Schleifen, Bedingungen und andere Funktionen enthalten, die eine komplexe Logik für die Automatisierung bereitstellen. Insgesamt bieten Playbooks eine leistungsstarke Möglichkeit, um komplexe Aufgaben in einer einheitlichen und einfach zu verwaltenden Weise zu automatisieren.

Ansible Basics

Was sind Variablen und worin unterscheiden sich globale, Inventar, Faken, Benutzer und Rollen Variablen?

Variablen in Ansible sind eine Möglichkeit, Werte zu speichern und in verschiedenen Teilen des Playbooks wiederzuverwenden. Es gibt verschiedene Arten von Variablen in Ansible, einschließlich globaler Variablen, Inventarvariablen, Faktoren, Benutzervariablen und Rollenvariablen. Diese Variablen können verwendet werden, um Konfigurationsparameter zu definieren, Host-spezifische Informationen zu speichern oder um Entscheidungen in Playbooks basierend auf bestimmten Bedingungen zu treffen..

    Globale Variablen sind vordefinierte Variablen in Ansible, die von jeder Rolle oder jedem Playbook verwendet werden können. Beispiele dafür sind ansible_user, ansible_ssh_private_key_file oder ansible_python_interpreter.

    Inventarvariablen sind Variablen, die direkt im Inventar definiert werden und auf den jeweiligen Hosts verwendet werden können. Hierbei wird meist eine INI- oder YAML-Datei verwendet, um das Inventar zu definieren.

    Faktoren sind Informationen, die Ansible über das System des verwalteten Hosts sammelt und als Variablen bereitstellt. Beispiele hierfür sind die Architektur, Betriebssystem, Speicherplatz oder Netzwerkschnittstellen.

    Benutzervariablen werden in den Playbooks definiert und sind auf die Ebene des jeweiligen Plays oder der jeweiligen Rolle beschränkt. Diese können auf spezifische Hosts oder Hostgruppen angewendet werden und ermöglichen somit eine granulare Steuerung der Konfiguration.

    Rollenvariablen sind spezielle Variablen, die in Ansible-Rollen definiert werden und zur Konfiguration von Rollen verwendet werden können. Hierbei wird typischerweise eine defaults/main.yml-Datei in der Rollenstruktur verwendet, um Variablen zu definieren, die von allen Plays in der Rolle verwendet werden.

Insgesamt bieten Variablen in Ansible eine flexible Möglichkeit, um Informationen zu speichern und wiederzuverwenden, um komplexe Automatisierungsaufgaben durchzuführen. Variablen ermöglichen es, Konfigurationsparameter einfach und zentral zu verwalten und somit eine Wartung der Infrastruktur zu vereinfachen.

   Gruppen lassen sich Hosts in Gruppen zusammen Fassen. Ein gutes Beispiel wäre z.B. VM versus physische Server oder konkret. System mit und Systeme ohne vlan Konfiguration. Über Gruppen können verschachtelt werden, so kann z.B. ein Gruppe speziell für eine Anwendung z.B. WordPress als Child (Untermenge) von Webserver sein. Dies ist aber nicht die einzige Möglichkeit solche Unterscheidungen zu treffen. Gerade wenn Anwendungsfälle sehr ähnlich sind, kann eine Unterscheidung / Fallunterscheidung im Tasks selbst sinnvoll sein. 

In diesem Dokument wird genau der Fall eintreten, dass die bisherige Struktur und Planung nochmals angepasst wird.

Unser erstes Playbook

Nach der ganzen Theorie geht es nun an die Praxis. 

😉 Falls euch jetzt schon der Kopf raucht…. keine Sorge in den folgenden Kapiteln gehen wir das alles nochmals Schritt für Schritt durch. Der Umstieg auf Ansible ist nicht ganz einfach, aber es lohnt sich. 

Was benötigen wir also um los zu legen?

Theoretisch nur eine Datei: das Playbook. Wir trennen es aber von Anfang an und erstellen zunächst eine Inventar Liste. Eine Liste unsere Zielsysteme. Zudem ein Playbook mit den Anweisungen. Später trennen wir es auf und führen bereits Rollen ein.

Das 1. Playbook wird einfach. Wir verbinden uns per SSH auf das Zielsystem und geben eine Debug Meldung aus.

Das Inventory

Fürs Erste reicht uns ein Zielsystem. In diesem Beispiel wird ein host: mx5.weiss-system.de festgelegt.

Kurzname mx5 FQDN mx5.domain.tld. In dieser Beitragserie verwenden wir konsquent das yaml Format. Das Inventory könnte auch als im ini Format angelegt werden.

domain.tld oder allgemein der Wert von ansible_host: Muss natürlich an die Zielumgebung angepasst werden. IP Adresse geht natürlich auch. Diese Inventory Datei heisst: hosts

all:
  vars:
    ansible_user=’root‘
    ansible_become=yes
    ansible_python_interpreter=’/usr/bin/env python3′
  hosts:
    mx5:
      ansible_host: mx5.domain.tld

Unter

  • all:

Wird zunächst alles zusammen gefasst. Es ist die oberste ebene.

     

    • vars

    Hier können allgemeingültige Variablen stehen. In unserem Beispiel, unter welchem Benutzer es ausgeführt werden soll. Sprich mit welchem Benutzer sich ansible ans Zielsystem verbindet. Python Interpreter welcher genutzt werden soll.

    • hosts:

    Danach folgt eine Liste der Hosts. In unserem Beispiel führen wir noch keine Rollen ein. Entsprechend steht bei der Ansible Ausgabe weiter unten „ungrouped“.

    hosts: definiert also, welche Art von Objekte als Liste folgen. (Hier könnte auch eine Gruppe stehen, dazu später mehr)

    • mx5:

    Hier folgt nun der wirkliche Rechner. Der Host mx5. Dieser wiederum hat das Attribut ansbible_host: erhalten, worüber sich ansible zum Zielsystem verbindet.

    -> Würden wir auf die Angabe ansible_host: verzichten, müsste „mx5“ auf dem System aufgelöst werden können.

    Sprich, ein

    ssh root@mx5

    sollte euch zum Zielsystem verbinden.

    Hier kann nun geprüft werden, ob ansible mit unserem Inventory einverstanden ist.

    ansible-inventory --graph -i host

    Ergebnis:

    @all:
    |--@ungrouped:
    | |--mx5

    Das Playbook

    Das erste Playbook enthält nun sowohl den Play Teil, welcher zunächst die Hosts definiert* und enthält zusätzlich die Tasks zur Ausführung der Aufgabe: Gib eine Debug Meldung aus.

    *Aus der Menge der Zielsysteme in hosts wird selektiert, welche host angesprochen / verarbeitet werden sollen. 

    Wie legen die Datei main.yml an. Zunächst wird über:

    - host: mx5

    Der Host mx5 festgelegt. Statt mx5 könnte eine Rolle oder all für alle Hosts stehen. Rollen haben wir noch nicht definiert: Siehe Ausgabe ansible-inventory oben. „ungrouped“

    Die definierten Werte:

    become: "true"
    become_user: root
    

    Sind allgemeingültig, gelten also für alle Hosts, welche in diesem Playbook verarbeitet werden. Danach folgt unser 1. Tasks. -name gibt beim Ausführen des Playbooks an, welcher Tasks gerade bearbeitet wird. Es folgt die Aufgabe:

    debug:
      msg: "Hallo Welt."
    ---
    - hosts: mx5
      become: "true"
      become_user: root
      tasks:
        - name: Unser erste Aufgabe
          debug:
            msg: 'Hallo Welt'

    Die Ausführung:

    ansible-playbook -i hosts main.yml

    Das Egebnis:

     

    PLAY [mx5] ****************************************************************************************************************************************************************************************************

     

    TASK [Gathering Facts] ****************************************************************************************************************************************************************************************
    fatal: [mx5]: UNREACHABLE! => {„changed“: false, „msg“: „Failed to connect to the host via ssh: ssh: Could not resolve hostname mx5.domain.tld: Name or service not known“, „unreachable“: true}

    PLAY RECAP ****************************************************************************************************************************************************************************************************

    mx5 : ok=0 changed=0 unreachable=1 failed=0 skipped=0 rescued=0 ignored=0

     

    failed? Was ging schief.

    Der Hostname mx5 und auch nicht der definierte ansible_hosts: mx5.domain.tld kann aufgelöst werden.

    in der /etc/hosts trage ich also die IP Adresse und Hostname ein:

    192.168.115.1 mx5.dmain.tld

    Mit dem erneuten ausführen obigen Befehls folgt nun diese Ausgabe: 

    ansible-playbook -i hosts main.yml
    PLAY [mx5] ****************************************************************************************************************************************************************************************************
    TASK [Gathering Facts] ****************************************************************************************************************************************************************************************
    ok: [mx5]
    TASK [Unser erste Aufgabe] ************************************************************************************************************************************************************************************
    ok: [mx5] => {
    "msg": "Hallo Welt"
    }
    PLAY RECAP ****************************************************************************************************************************************************************************************************
    mx5 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

    Was passiert hier?

    Als erster wird der Play ausgeführt. Hier wurde der Host mx5 definiert und nur dieser wird verarbeitet.

    PLAY [mx5]

    Play also fasst die Systeme zusammen wfür welche die nachfolgenden Tasks ausgeführt werden sollen.

    TASK [Gathering Facts]

    Dieser Taks wird immer ausgeführt. Dieser baut eine Verbindung zum
    Zielsystem per ssh auf und ermittelt Informationen zum System. Diese können in den Playbooks / Tasks verwendet werden.

    TASK [Unser erste Aufgabe]

    Das ist nun unser Task mit der Debug Meldung.
    Diese Meldung wird auf dem Zielsystem, hier mx5, erzeugt und im JSON Format zurück gegeben.

    PLAY RECAP
    

    Hier gibt Ansible eine Übersicht der ausgeführten Tasks, Erfolge, Änderungen , Erreichbarkeit, Fehlschläge, Übersprungene Tasks, etc. aus.

    Das war das 1. Playbook mit einer einfachen Debug Meldung.
    Die zugehörigen Dateien sind im Gitlab: Tag: Ansible-Kapitel-01