Ansible Playbook-Ausführung auf RHEL steuern

Red Hat Enterprise LinuxRed Hat Enterprise LinuxBeginner
Jetzt üben

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einleitung

In diesem Lab lernen Sie, wie Sie den Ausführungsfluss von Ansible Playbooks auf einem Red Hat Enterprise Linux (RHEL) System steuern. Sie beginnen mit dem Schreiben eines Playbooks, das grundlegende Kontrollstrukturen verwendet, einschließlich Schleifen (loops) zur effizienten Wiederholung von Aufgaben und Bedingungen (conditionals), um Aufgaben nur auszuführen, wenn bestimmte Kriterien erfüllt sind. Sie werden auch Handler implementieren, um Aktionen wie Neustarts von Diensten nur dann auszulösen, wenn eine Änderung auftritt, was Ihre Automatisierung intelligenter und effizienter macht.

Aufbauend auf diesen grundlegenden Fähigkeiten werden Sie fortgeschrittenere Techniken zur Verwaltung der Playbook-Ausführung erkunden. Dazu gehört die Verwendung von block und rescue Anweisungen zur eleganten Behandlung von Aufgabenfehlern sowie die Anwendung von changed_when und failed_when zur feingranularen Steuerung des Aufgabenstatus. Zum Abschluss des Labs wenden Sie alle diese Konzepte in einer praktischen Übung zur Bereitstellung eines sicheren Webservers an, um Ihre Fähigkeit zur Erstellung robuster und zuverlässiger Ansible-Automatisierung zu festigen.

Ein Playbook mit Schleifen und Bedingungen schreiben

In diesem Schritt lernen Sie zwei grundlegende Konzepte in Ansible zur Steuerung der Aufgabenausführung kennen: Schleifen (loops) und Bedingungen (conditionals). Schleifen ermöglichen es Ihnen, eine Aufgabe mehrmals mit unterschiedlichen Werten zu wiederholen, was für Aufgaben wie die Installation mehrerer Pakete oder die Erstellung mehrerer Benutzer äußerst effizient ist. Bedingungen, die das Schlüsselwort when verwenden, erlauben es Ihnen, eine Aufgabe nur dann auszuführen, wenn bestimmte Kriterien erfüllt sind, z. B. wenn das Betriebssystem eine bestimmte Version hat oder eine Datei bereits existiert.

Stellen Sie zunächst sicher, dass Ansible auf Ihrer LabEx VM installiert ist. Wir werden dafür den DNF-Paketmanager verwenden.

sudo dnf install -y ansible-core

Sie sollten eine Ausgabe sehen, die anzeigt, dass ansible-core und seine Abhängigkeiten installiert werden.

...
Installed:
  ansible-core-2.x.x-1.el9.x86_64
  ...
Complete!

Nun richten wir unser Projektverzeichnis ein. Alle unsere Arbeiten für dieses Lab werden in einem dedizierten Verzeichnis stattfinden, um die Dinge organisiert zu halten.

cd ~/project
mkdir control-flow-lab
cd control-flow-lab

Ein Ansible-Projekt benötigt eine Inventardatei (inventory file), die die Hosts definiert, die Sie verwalten möchten. Für dieses Lab werden wir die lokale Maschine, localhost, verwalten.

Erstellen Sie eine Inventardatei namens inventory mit dem nano-Editor:

nano inventory

Fügen Sie die folgende Zeile in die Datei ein. Dies weist Ansible an, das Playbook auf localhost auszuführen und sich direkt damit zu verbinden, anstatt SSH zu verwenden.

localhost ansible_connection=local

Speichern Sie die Datei und beenden Sie nano, indem Sie Ctrl+X, dann Y und Enter drücken.

Als Nächstes erstellen wir unser erstes Playbook, playbook.yml, um eine Schleife zu demonstrieren. Dieses Playbook installiert eine Liste nützlicher Kommandozeilenwerkzeuge.

nano playbook.yml

Geben Sie den folgenden YAML-Inhalt in den Editor ein. Dieses Playbook definiert eine Aufgabe, die das Modul ansible.builtin.dnf zur Installation von Paketen verwendet. Die Direktive become: yes weist Ansible an, Aufgaben mit sudo-Berechtigungen auszuführen, was für die Installation von Paketen erforderlich ist. Das Schlüsselwort loop stellt eine Liste von Paketnamen bereit. Ansible führt diese Aufgabe einmal für jedes Element in der Liste aus und ersetzt den Platzhalter {{ item }} durch den aktuellen Paketnamen.

---
- name: Install common tools
  hosts: localhost
  become: yes
  tasks:
    - name: Install specified packages
      ansible.builtin.dnf:
        name: "{{ item }}"
        state: present
      loop:
        - git
        - tree
        - wget

Speichern und beenden Sie den Editor. Führen Sie nun das Playbook mit dem Befehl ansible-playbook aus und geben Sie Ihre Inventardatei mit dem Flag -i an.

ansible-playbook -i inventory playbook.yml

Die Ausgabe zeigt die Ausführung des Playbooks. Ansible prüft jedes Paket und installiert es, falls es noch nicht vorhanden ist. Die PLAY RECAP am Ende fasst die Ergebnisse zusammen.

PLAY [Install tools and run conditional tasks] *********************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [Install specified packages] **********************************************
changed: [localhost] => (item=git)
changed: [localhost] => (item=tree)
changed: [localhost] => (item=wget)

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Nun ändern wir das Playbook, um eine bedingte Aufgabe hinzuzufügen. Wir fügen eine Aufgabe hinzu, die eine Nachricht ausgibt, aber nur, wenn das Betriebssystem Red Hat Enterprise Linux ist. Dies ist ein gängiger Anwendungsfall, um die Automatisierung an spezifische Umgebungen anzupassen.

Öffnen Sie die Datei playbook.yml erneut:

nano playbook.yml

Fügen Sie die folgenden Aufgaben am Ende der Datei hinzu. Das Schlüsselwort when wertet den gegebenen Ausdruck aus. ansible_facts['distribution'] ist eine Variable, die Ansible automatisch über den verwalteten Host ermittelt. Die erste Aufgabe wird ausgeführt, da unsere Umgebung RHEL ist, und die zweite Aufgabe wird übersprungen.

---
- name: Install tools and run conditional tasks
  hosts: localhost
  become: yes
  tasks:
    - name: Install specified packages
      ansible.builtin.dnf:
        name: "{{ item }}"
        state: present
      loop:
        - git
        - tree
        - wget

    - name: Show message on Red Hat systems
      ansible.builtin.debug:
        msg: "This system is a Red Hat family distribution."
      when: ansible_facts['distribution'] == "RedHat"

    - name: Show message on other systems
      ansible.builtin.debug:
        msg: "This system is NOT a Red Hat family distribution."
      when: ansible_facts['distribution'] != "RedHat"

Speichern und beenden Sie den Editor. Führen Sie das aktualisierte Playbook aus:

ansible-playbook -i inventory playbook.yml

Beobachten Sie die Ausgabe sorgfältig. Die Paketinstallationsaufgabe wird wahrscheinlich für alle Elemente als ok gemeldet, da sie bereits installiert sind. Wichtiger ist, dass Sie die erste Debug-Nachricht ausgegeben sehen, während die zweite als skipping markiert ist.

PLAY [Install tools and run conditional tasks] *********************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [Install specified packages] **********************************************
ok: [localhost] => (item=git)
ok: [localhost] => (item=tree)
ok: [localhost] => (item=wget)

TASK [Show message on Red Hat systems] *****************************************
ok: [localhost] => {
    "msg": "This system is a Red Hat family distribution."
}

TASK [Show message on other systems] *******************************************
skipping: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

Sie haben erfolgreich ein Ansible-Playbook geschrieben und ausgeführt, das sowohl Schleifen für wiederholte Aktionen als auch Bedingungen zur Steuerung der Aufgabenausführung basierend auf Systeminformationen (facts) verwendet.

Handler zur Auslösung von Service-Neustarts implementieren

In diesem Schritt lernen Sie Ansible Handler kennen. Handler sind spezielle Aufgaben, die nur ausgeführt werden, wenn sie von einer anderen Aufgabe "benachrichtigt" (notified) werden. Sie werden typischerweise für Aktionen verwendet, die nur dann erfolgen sollen, wenn eine Änderung vorgenommen wurde, wie z. B. das Neustarten eines Dienstes, nachdem seine Konfigurationsdatei aktualisiert wurde. Dieser Ansatz ist effizienter als ein Neustart des Dienstes bei jeder Ausführung des Playbooks, da er sicherstellt, dass die Aktion nur bei Bedarf durchgeführt wird.

Wir erstellen ein Playbook, das den Nginx-Webserver installiert, eine benutzerdefinierte Homepage bereitstellt und einen Handler verwendet, um Nginx nur dann neu zu laden, wenn sich der Inhalt der Homepage ändert.

Erstellen Sie zunächst ein neues Verzeichnis für diese Übung, um unser Projekt organisiert zu halten.

cd ~/project
mkdir control-handlers-lab
cd control-handlers-lab

Wie zuvor benötigen wir eine Inventardatei, um Ansible mitzuteilen, wo das Playbook ausgeführt werden soll.

nano inventory

Fügen Sie die folgende Zeile hinzu, um die lokale Maschine anzugeben.

localhost ansible_connection=local

Speichern und beenden Sie den Editor (Ctrl+X, Y, Enter).

Als Nächstes benötigen wir eine Datei, die als Homepage unseres Webservers dient. Wir erstellen ein Verzeichnis files, um sie dort zu speichern.

mkdir files

Erstellen Sie nun eine einfache Datei index.html im Verzeichnis files.

nano files/index.html

Fügen Sie den folgenden HTML-Inhalt hinzu:

<h1>Welcome to the Ansible Handler Lab!</h1>

Speichern und beenden Sie den Editor.

Nun erstellen Sie das Playbook deploy_nginx.yml. Dieses Playbook führt drei Hauptaktionen aus: Installation von Nginx, Kopieren der Datei index.html und Definition eines Handlers zum Neuladen von Nginx.

nano deploy_nginx.yml

Geben Sie den folgenden Inhalt ein. Achten Sie genau auf das Schlüsselwort notify in der Aufgabe "Copy homepage" und den entsprechenden Abschnitt handlers am Ende. Die Direktive become: yes weist Ansible an, Aufgaben mit sudo-Berechtigungen auszuführen, was für die Installation von Paketen und die Verwaltung von Diensten erforderlich ist.

---
- name: Deploy Nginx with a handler
  hosts: localhost
  become: yes
  tasks:
    - name: Ensure Nginx is installed
      ansible.builtin.dnf:
        name: nginx
        state: present

    - name: Start and enable Nginx service
      ansible.builtin.systemd:
        name: nginx
        state: started
        enabled: yes

    - name: Copy homepage
      ansible.builtin.copy:
        src: files/index.html
        dest: /usr/share/nginx/html/index.html
      notify: reload nginx

  handlers:
    - name: reload nginx
      ansible.builtin.systemd:
        name: nginx
        state: reloaded

Speichern und beenden Sie den Editor.

Führen Sie nun das Playbook zum ersten Mal aus.

ansible-playbook -i inventory deploy_nginx.yml

Sie sehen eine Ausgabe, die zeigt, dass Nginx installiert wurde (oder bereits vorhanden war), der Nginx-Dienst gestartet und aktiviert wurde, die Datei index.html kopiert wurde (Status changed) und, was wichtig ist, der Handler am Ende des Plays benachrichtigt und ausgeführt wurde.

...
TASK [Copy homepage] ***********************************************************
changed: [localhost]

RUNNING HANDLER [reload nginx] *************************************************
changed: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Sie können mit curl überprüfen, ob der Webserver läuft und Ihre benutzerdefinierte Seite ausliefert.

curl http://localhost

Die Ausgabe sollte der Inhalt Ihrer index.html-Datei sein.

<h1>Welcome to the Ansible Handler Lab!</h1>

Führen Sie nun exakt dasselbe Playbook erneut aus, ohne Änderungen vorzunehmen.

ansible-playbook -i inventory deploy_nginx.yml

Beobachten Sie diesmal die Ausgabe. Die Aufgabe "Copy homepage" wird ok anstelle von changed melden, da die Datei auf dem Ziel bereits mit der Quelle übereinstimmt. Die Aufgabe "Start and enable Nginx service" wird ebenfalls ok melden, da der Dienst bereits läuft und aktiviert ist. Da keine Aufgaben den Handler benachrichtigt haben, wurde der Handler nicht ausgeführt.

...
TASK [Copy homepage] ***********************************************************
ok: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Um den Handler erneut in Aktion zu sehen, ändern wir die Quelldatei index.html.

nano files/index.html

Ändern Sie den Inhalt wie folgt:

<h1>The Handler Ran Again!</h1>

Speichern und beenden Sie. Führen Sie nun das Playbook noch einmal aus.

ansible-playbook -i inventory deploy_nginx.yml

Da sich die Quelldatei geändert hat, wird die Aufgabe "Copy homepage" erneut changed melden, was wiederum den Handler reload nginx benachrichtigt und ausführt.

...
TASK [Copy homepage] ***********************************************************
changed: [localhost]

RUNNING HANDLER [reload nginx] *************************************************
changed: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Überprüfen Sie die Änderung ein letztes Mal mit curl.

curl http://localhost

Sie sollten die aktualisierte Nachricht sehen.

<h1>The Handler Ran Again!</h1>

Diese Übung demonstriert die Leistungsfähigkeit und Effizienz von Handlern zur Verwaltung des Dienststatus als Reaktion auf Konfigurationsänderungen.

Task-Fehler mit Block und Rescue verwalten

In diesem Schritt lernen Sie, wie Sie Fehler in Ihren Ansible-Playbooks elegant behandeln. Standardmäßig stoppt Ansible die Ausführung des gesamten Playbooks auf diesem Host, wenn eine Aufgabe fehlschlägt. Obwohl dies eine sichere Standardeinstellung ist, benötigen Sie manchmal mehr Kontrolle. Sie werden zwei Methoden zur Fehlerbehandlung untersuchen: die einfache Direktive ignore_errors und die leistungsfähigere Struktur block, rescue und always, die eine Möglichkeit bietet, Aufgaben zu versuchen und Wiederherstellungsaktionen zu definieren, falls diese fehlschlagen.

Erstellen Sie zunächst ein neues Verzeichnis für diese Übung.

cd ~/project
mkdir control-errors-lab
cd control-errors-lab

Erstellen Sie die Standard-Inventardatei für localhost.

nano inventory

Fügen Sie den folgenden Inhalt hinzu:

localhost ansible_connection=local

Speichern und beenden Sie den Editor (Ctrl+X, Y, Enter).

Erstellen Sie nun ein Playbook namens playbook.yml, das so konzipiert ist, dass es fehlschlägt. Die erste Aufgabe versucht, ein nicht vorhandenes Paket zu installieren.

nano playbook.yml

Geben Sie den folgenden Inhalt ein. Dieses Playbook versucht, ein gefälschtes Paket httpd-fake und dann ein echtes Paket, mariadb-server, zu installieren.

---
- name: Demonstrate Task Failure
  hosts: localhost
  become: yes
  tasks:
    - name: Attempt to install a non-existent package
      ansible.builtin.dnf:
        name: httpd-fake
        state: present

    - name: Install MariaDB server
      ansible.builtin.dnf:
        name: mariadb-server
        state: present

Speichern und beenden Sie den Editor. Führen Sie nun das Playbook aus.

ansible-playbook -i inventory playbook.yml

Sie sehen, dass die erste Aufgabe mit einer Fehlermeldung fehlschlägt, da das Paket httpd-fake nicht gefunden werden kann. Entscheidend ist, dass Ansible stoppt und die zweite Aufgabe, "Install MariaDB server", nicht ausgeführt wird.

...
TASK [Attempt to install a non-existent package] *******************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "No match for argument: httpd-fake", "rc": 1, "results": []}

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

Verwenden wir nun block und rescue, um diesen Fehler eleganter zu behandeln. Das Schlüsselwort block gruppiert eine Reihe von Aufgaben. Wenn eine Aufgabe innerhalb des block fehlschlägt, überspringt Ansible die restlichen Aufgaben im block und führt die Aufgaben im rescue-Abschnitt aus. Der Abschnitt always wird unabhängig davon ausgeführt, ob die Abschnitte block oder rescue erfolgreich waren oder fehlgeschlagen sind.

Ändern Sie playbook.yml, um diese Struktur zu verwenden.

nano playbook.yml

Ersetzen Sie den gesamten Inhalt durch Folgendes. Hier versuchen wir, das gefälschte Paket im block zu installieren. Wenn es fehlschlägt, wird der rescue-Abschnitt ausgeführt, der mariadb-server als Wiederherstellungsschritt installiert. Der always-Abschnitt gibt am Ende eine Meldung aus.

---
- name: Handle Task Failure with Block and Rescue
  hosts: localhost
  become: yes
  tasks:
    - name: Attempt primary task, with recovery
      block:
        - name: Attempt to install a non-existent package
          ansible.builtin.dnf:
            name: httpd-fake
            state: present
        - name: This task will be skipped
          ansible.builtin.debug:
            msg: "This message will not appear because the previous task fails."
      rescue:
        - name: Install MariaDB server on failure
          ansible.builtin.dnf:
            name: mariadb-server
            state: present
      always:
        - name: This always runs
          ansible.builtin.debug:
            msg: "The block has completed, either by success or rescue."

Speichern und beenden Sie. Führen Sie das Playbook erneut aus.

ansible-playbook -i inventory playbook.yml

Beobachten Sie die Ausgabe. Die erste Aufgabe im block schlägt wie erwartet fehl. Die zweite Aufgabe im block wird übersprungen. Ansible wechselt dann zum rescue-Abschnitt und installiert erfolgreich mariadb-server. Schließlich wird der always-Abschnitt ausgeführt.

...
TASK [Attempt to install a non-existent package] *******************************
fatal: [localhost]: FAILED! => ...

TASK [This task will be skipped] ***********************************************
skipping: [localhost]

RESCUE START *******************************************************************

TASK [Install MariaDB server on failure] ***************************************
changed: [localhost]

ALWAYS START *******************************************************************

TASK [This always runs] ********************************************************
ok: [localhost] => {
    "msg": "The block has completed, either by success or rescue."
}

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=1    rescued=1    ignored=0

Sehen wir nun, was passiert, wenn der block erfolgreich ist. Bearbeiten Sie das Playbook und korrigieren Sie den Paketnamen.

nano playbook.yml

Ändern Sie httpd-fake in ein echtes Paket, httpd.

## ... (rest of the playbook)
block:
  - name: Attempt to install a valid package
    ansible.builtin.dnf:
      name: httpd ## Corrected from httpd-fake
      state: present
  - name: This task will now run
    ansible.builtin.debug:
      msg: "This message will now appear because the previous task succeeds."
## ... (rest of the playbook)

Speichern und beenden Sie. Führen Sie das Playbook ein letztes Mal aus.

ansible-playbook -i inventory playbook.yml

Diesmal sind beide Aufgaben im block erfolgreich. Da der block ohne Fehler abgeschlossen wurde, wird der rescue-Abschnitt vollständig übersprungen. Der always-Abschnitt wird, wie der Name schon sagt, weiterhin ausgeführt.

...
TASK [Attempt to install a valid package] **************************************
changed: [localhost]

TASK [This task will now run] **************************************************
ok: [localhost] => {
    "msg": "This message will now appear because the previous task succeeds."
}

RESCUE START *******************************************************************
skipping rescue

ALWAYS START *******************************************************************

TASK [This always runs] ********************************************************
ok: [localhost] => {
    "msg": "The block has completed, either by success or rescue."
}

PLAY RECAP *********************************************************************
localhost                  : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Sie haben nun erfolgreich die Struktur block/rescue/always verwendet, um ein robustes Playbook zu erstellen, das Fehler behandeln und Wiederherstellungsaktionen durchführen kann.

Task-Status mit changed_when und failed_when steuern

In diesem Schritt lernen Sie, wie Sie die Interpretation von Task-Ergebnissen durch Ansible feiner steuern. Sie lernen zwei leistungsstarke Direktiven kennen: changed_when und failed_when.

  • changed_when: Standardmäßig melden Module wie ansible.builtin.command oder ansible.builtin.shell fast immer einen "changed"-Status, auch wenn der von ihnen ausgeführte Befehl das System nicht verändert hat. changed_when ermöglicht es Ihnen, eine benutzerdefinierte Bedingung zu definieren, die bestimmt, ob eine Aufgabe als "changed" gemeldet werden soll. Dies ist entscheidend für das Schreiben idempotenter Playbooks und für das genaue Auslösen von Handlern.
  • failed_when: Manchmal kann ein Befehl mit einem Rückgabecode ungleich Null beendet werden (was Ansible als Fehler betrachtet), auch wenn das Ergebnis akzeptabel ist. failed_when ermöglicht es Ihnen, die Standardfehlerbedingungen zu überschreiben, sodass Ihr Playbook auf der Grundlage intelligenterer Kriterien fortgesetzt werden kann, wie z. B. der Ausgabe des Befehls oder einem bestimmten Rückgabecode.

Beginnen wir mit der Einrichtung eines neuen Projektverzeichnisses.

cd ~/project
mkdir control-state-lab
cd control-state-lab

Erstellen Sie die Standard-Inventardatei für localhost.

nano inventory

Fügen Sie den folgenden Inhalt hinzu:

localhost ansible_connection=local

Speichern und beenden Sie den Editor (Ctrl+X, Y, Enter).

Verwendung von changed_when

Sehen wir uns zunächst an, wie sich eine Befehlsaufgabe standardmäßig verhält. Wir erstellen ein Playbook, das den Befehl date ausführt. Dieser Befehl gibt einfach das Datum aus und verändert das System nicht, aber das command-Modul meldet ihn als Änderung.

Erstellen Sie ein neues Playbook namens playbook.yml.

nano playbook.yml

Geben Sie den folgenden Inhalt ein:

---
- name: Control Task State
  hosts: localhost
  tasks:
    - name: Check local time (default behavior)
      ansible.builtin.command: date

Speichern und beenden Sie. Führen Sie nun das Playbook aus.

ansible-playbook -i inventory playbook.yml

Beachten Sie in der Ausgabe, dass die Aufgabe als changed=1 gemeldet wird, obwohl nichts auf dem System verändert wurde.

...
TASK [Check local time (default behavior)] *************************************
changed: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    ...

Verwenden wir nun changed_when, um Ansible mitzuteilen, dass dieser Befehl das System niemals verändert. Ändern Sie playbook.yml.

nano playbook.yml

Fügen Sie der Aufgabe changed_when: false hinzu.

---
- name: Control Task State
  hosts: localhost
  tasks:
    - name: Check local time (with changed_when)
      ansible.builtin.command: date
      changed_when: false

Speichern und beenden Sie. Führen Sie das Playbook erneut aus.

ansible-playbook -i inventory playbook.yml

Diesmal meldet die Aufgabe ok, und die abschließende Zusammenfassung zeigt changed=0. Sie haben das Standardverhalten erfolgreich überschrieben.

...
TASK [Check local time (with changed_when)] ************************************
ok: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    ...

Verwendung von failed_when

Als Nächstes untersuchen wir failed_when. Wir erstellen eine Aufgabe, die auf die Existenz einer Datei prüft, die nicht vorhanden ist. Der Befehl wird standardmäßig "fehlschlagen".

Erstellen Sie zunächst eine Dummy-Datei, in der gesucht werden soll.

echo "System is running" > status.txt

Ändern Sie nun playbook.yml, um nach dem Wort "ERROR" in dieser Datei zu suchen. Der Befehl grep wird mit dem Rückgabecode 1 beendet, da das Wort nicht gefunden wird, was Ansible als Fehler interpretiert.

nano playbook.yml

Ersetzen Sie den Inhalt durch Folgendes:

---
- name: Control Task State
  hosts: localhost
  tasks:
    - name: Check for ERROR in status file (will fail)
      ansible.builtin.command: grep ERROR status.txt

Speichern und beenden Sie. Führen Sie das Playbook aus.

ansible-playbook -i inventory playbook.yml

Wie erwartet stoppt die Ausführung des Playbooks mit der Meldung FAILED!.

...
TASK [Check for ERROR in status file (will fail)] ******************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["grep", "ERROR", "status.txt"], "delta": "...", "end": "...", "msg": "non-zero return code", "rc": 1, ...}
...

Das ist nicht das, was wir wollen. Das Fehlen von "ERROR" ist für uns eine Erfolgsbedingung. Wir können failed_when verwenden, um neu zu definieren, was einen Fehler darstellt. Wir weisen Ansible an, nur dann fehlzuschlagen, wenn der Rückgabecode des Befehls größer als 1 ist. Ein Rückgabecode von 1 (Muster nicht gefunden) wird nun als Erfolg gewertet. Wir müssen auch das Ergebnis der Aufgabe registeren, um ihren Rückgabecode (rc) zu überprüfen.

Ändern Sie playbook.yml ein letztes Mal.

nano playbook.yml

Aktualisieren Sie das Playbook mit register und failed_when.

---
- name: Control Task State
  hosts: localhost
  tasks:
    - name: Check for ERROR in status file (with failed_when)
      ansible.builtin.command: grep ERROR status.txt
      register: grep_result
      failed_when: grep_result.rc > 1
      changed_when: false

Wir haben auch changed_when: false hinzugefügt, da grep eine schreibgeschützte Operation ist und das System nicht verändert.

Speichern und beenden Sie. Führen Sie das endgültige Playbook aus.

ansible-playbook -i inventory playbook.yml

Erfolg! Die Aufgabe meldet nun ok, da ihr Rückgabecode 1 war, was unserer neuen Fehlerbedingung (rc > 1) nicht entspricht. Das Playbook wird erfolgreich abgeschlossen.

...
TASK [Check for ERROR in status file (with failed_when)] ***********************
ok: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    ...

Sie haben nun gelernt, wie Sie mit changed_when und failed_when die Erfolgs-, Änderungs- und Fehlerzustände Ihrer Aufgaben präzise definieren können, was zu robusterer und intelligenterer Automatisierung führt.

Sicheren Webserver mit Task-Steuerung bereitstellen

In diesem letzten Schritt kombinieren Sie alle gelernten Konzepte – Schleifen, Bedingungen, Handler und Fehlerbehandlung –, um ein einzelnes, robustes Playbook zu erstellen. Ziel ist es, den Apache-Webserver (httpd) bereitzustellen, ihn mit mod_ssl zu sichern, ein selbstsigniertes SSL-Zertifikat zu generieren und eine benutzerdefinierte Homepage bereitzustellen. Diese praktische Übung spiegelt eine reale Automatisierungsaufgabe wider.

Richten wir zunächst das Projektverzeichnis für diese abschließende Übung ein.

cd ~/project
mkdir control-review-lab
cd control-review-lab

Erstellen Sie wie immer eine inventory-Datei, um Ihren Zielhost zu definieren.

nano inventory

Fügen Sie den localhost-Eintrag hinzu:

localhost ansible_connection=local

Speichern und beenden Sie den Editor (Ctrl+X, Y, Enter).

Als Nächstes benötigen wir ein Verzeichnis, um die Dateien zu speichern, die unser Playbook bereitstellen wird.

mkdir files

Erstellen Sie nun eine benutzerdefinierte Homepage, index.html, im Verzeichnis files.

nano files/index.html

Fügen Sie den folgenden HTML-Inhalt hinzu. Dies wird die Seite sein, die von unserem sicheren Webserver bereitgestellt wird.

<h1>Secure Web Server Deployed by Ansible!</h1>
<p>This page is served over HTTPS.</p>

Speichern und beenden Sie den Editor.

Nun ist es an der Zeit, das Haupt-Playbook deploy_secure_web.yml zu erstellen. Dieses Playbook wird komplexer sein als die vorherigen und integriert mehrere Konzepte.

nano deploy_secure_web.yml

Geben Sie das folgende vollständige Playbook ein. Lesen Sie die Kommentare im Code, um zu verstehen, wie jeder Teil zum Gesamtziel beiträgt.

---
- name: Deploy a Secure Apache Web Server
  hosts: localhost
  become: yes
  vars:
    packages_to_install:
      - httpd
      - mod_ssl
    ssl_cert_path: /etc/pki/tls/certs/localhost.crt
    ssl_key_path: /etc/pki/tls/private/localhost.key

  tasks:
    - name: Stop nginx to free port 80
      ansible.builtin.systemd:
        name: nginx
        state: stopped
      ignore_errors: yes

    - name: Install httpd and mod_ssl packages
      ansible.builtin.dnf:
        name: "{{ packages_to_install }}"
        state: present

    - name: Generate self-signed SSL certificate if it does not exist
      ansible.builtin.command: >
        openssl req -new -nodes -x509
        -subj "/C=US/ST=None/L=None/O=LabEx/CN=localhost"
        -keyout {{ ssl_key_path }}
        -out {{ ssl_cert_path }}
      args:
        creates: "{{ ssl_cert_path }}"

    - name: Deploy custom index.html
      ansible.builtin.copy:
        src: files/index.html
        dest: /var/www/html/index.html
      notify: restart httpd

    - name: Start and enable httpd service
      ansible.builtin.systemd:
        name: httpd
        state: started
        enabled: yes

  handlers:
    - name: restart httpd
      ansible.builtin.systemd:
        name: httpd
        state: restarted

Lassen Sie uns aufschlüsseln, was dieses Playbook tut:

  • vars: Definiert Variablen für die zu installierenden Pakete und die Pfade für das SSL-Zertifikat und den Schlüssel, wodurch das Playbook leichter zu lesen und zu warten ist.
  • Nginx stoppen Task: Stoppt den Nginx-Dienst aus dem vorherigen Lab-Schritt, um Port 80 für Apache freizugeben. Verwendet ignore_errors: yes, falls Nginx nicht läuft.
  • Installationsaufgabe: Verwendet die Variable packages_to_install, um sowohl httpd als auch mod_ssl zu installieren.
  • Zertifikatgenerierungsaufgabe: Dies ist eine Schlüsselaufgabe. Sie verwendet den openssl-Befehl, um ein selbstsigniertes Zertifikat zu erstellen. Die Direktive args: { creates: ... } macht diese Aufgabe idempotent. Der Befehl wird nur ausgeführt, wenn die Zertifikatsdatei (/etc/pki/tls/certs/localhost.crt) noch nicht existiert.
  • Homepage-Bereitstellungsaufgabe: Kopiert Ihre benutzerdefinierte index.html. Entscheidend ist, dass sie notify: restart httpd verwendet, um den Handler auszulösen, wenn die Datei geändert wird.
  • Dienststartaufgabe: Verwendet das systemd-Modul, um den httpd-Dienst zu starten und zu aktivieren, nachdem alle Konfigurationen vorhanden sind, und stellt sicher, dass er beim Booten startet.
  • Handler: Der Handler restart httpd führt einen Neustart von Apache mithilfe von systemd durch, der nur ausgelöst wird, wenn eine Konfigurations- oder Inhaltsdatei geändert wird.

Speichern und beenden Sie den Editor. Führen Sie nun Ihr umfassendes Playbook aus.

ansible-playbook -i inventory deploy_secure_web.yml

Beim ersten Ausführen sollten mehrere Aufgaben changed melden, darunter das Stoppen von Nginx, die Paketinstallation, die Zertifikatgenerierung, das Kopieren von Dateien und den Dienststart.

...
TASK [Start and enable httpd service] ******************************************
changed: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=6    changed=5    unreachable=0    failed=0    ...

Testen Sie abschließend, ob Ihr sicherer Webserver funktioniert. Testen Sie zuerst die HTTP-Version und dann die HTTPS-Version mit dem Flag -k, um Warnungen bezüglich des selbstsignierten Zertifikats zu ignorieren.

curl http://localhost

Sie sollten den Inhalt Ihrer benutzerdefinierten Homepage sehen.

<h1>Secure Web Server Deployed by Ansible!</h1>
<p>This page is served over HTTPS.</p>

Sie können auch die HTTPS-Version testen:

curl -k https://localhost

Wenn Sie das Playbook erneut ausführen, werden Sie feststellen, dass keine Aufgaben changed melden und der Handler nicht ausgeführt wird, was beweist, dass Ihr Playbook idempotent ist.

Herzlichen Glückwunsch! Sie haben erfolgreich ein praktisches, robustes Ansible-Playbook erstellt, das Schleifen, Variablen, idempotente Befehlsausführung und Handler kombiniert, um eine sichere Anwendung bereitzustellen.

Zusammenfassung

In diesem Lab haben Sie gelernt, die Ausführung von Ansible-Playbooks auf einem RHEL-System zu steuern. Sie begannen mit der Einrichtung einer grundlegenden Projektumgebung, einschließlich der Installation von Ansible und der Erstellung einer Inventardatei. Anschließend erkundeten Sie grundlegende Kontrollflussstrukturen, indem Sie Schleifen verwendeten, um Aufgaben mit unterschiedlichen Eingaben effizient zu wiederholen, und Bedingungen mit der when-Anweisung, um Aufgaben nur unter bestimmten Umständen auszuführen. Darauf aufbauend implementierten Sie Handler, um reaktionsschnelle Automatisierungen zu erstellen, wie z. B. das Auslösen eines Dienstneustarts nur dann, wenn seine Konfigurationsdatei geändert wurde.

Das Lab behandelte auch fortgeschrittene Techniken zur Verwaltung der Playbook-Ausführung. Sie lernten, robustere Playbooks zu erstellen, indem Sie block- und rescue-Klauseln verwendeten, um Task-Fehler elegant zu behandeln. Darüber hinaus erhielten Sie eine fein abgestufte Kontrolle über die Task-Ergebnisse, indem Sie changed_when und failed_when verwendeten, um benutzerdefinierte Erfolgs- und Fehlerbedingungen zu definieren. Schließlich konsolidierten Sie all diese Fähigkeiten, indem Sie sie auf ein praktisches Szenario anwendeten: die Bereitstellung eines sicheren Webservers, was zeigt, wie Schleifen, Bedingungen, Handler und Fehlerbehandlung effektiv in einem realen Automatisierungs-Workflow kombiniert werden können.