Aktualisiere einen Host nach dem anderen

AnsibleBeginner
Jetzt üben

Einführung

Die Website von Courtyard Sessions läuft auf zwei Backends. Das neue Programm ist bereit, doch eine gleichzeitige Aktualisierung beider Knoten könnte Besucher ohne verfügbare Website zurücklassen. Prüfe mit Ansible den laufenden Zustand und veröffentliche das neue Paket auf einem Backend nach dem anderen.

Du brauchst grundlegende Terminalnavigation und praktische Erfahrung mit einem kleinen Ansible-Inventar, Variablen und file/copy-Aufgaben. Gateway, SSH-Zugriff und Pakete sind vorbereitet. Das Lab erklärt Ausführungsort, Delegierung, Batches, Versionslinks und vollständige HTTP-Inhaltsprüfungen vor ihrer Verwendung. Die Dauer ist eine Schätzung des Autors einschließlich Lesen und üblicher Korrekturen.

Erkenne, wo jede Aufgabe läuft

In diesem Schritt erstellst du ein rein lesendes Prüf-Playbook, das die laufende Version jedes Backends und den tatsächlich aktiven Routing-Pool anzeigt.

Courtyard Sessions hat bereits eine funktionierende Besucherwebsite. Öffne den Tab Website. Lies Good things, around the corner. und klicke auf The program. Die Seite springt zu zwei Programmpunkten, beginnend mit Coffee in the courtyard. Klicke auf Download the program (CSV) und öffne die heruntergeladene Datei: Auf die Kopfzeile time,session,place folgen die beiden angezeigten Programmpunkte. Eine CSV ist eine Texttabelle mit durch Kommas getrennten Feldern. Dieser echte Besucherdownload muss während der Aktualisierung nutzbar bleiben.

Das Gateway verteilt kurze HTTP-Anfragen auf zwei Backends. Entfernst du ein Backend aus dem aktiven Pool, werden keine neuen Gateway-Anfragen dorthin geleitet; nach dem Hinzufügen nimmt es wieder an der Verteilung teil. Dieses kleine vorbereitete Gateway leert keine langlebigen Verbindungen und bildet keine vollständige Cloud-Veröffentlichungsplattform ab. Beide Backends sind per SSH verwaltete Container, die den Kernel dieser Trainings-VM teilen. Der Controller ist die Maschine, auf der Ansible und das Gateway laufen.

Wechsle zum Tab Controller. Er startet in /home/labex/project. pwd zeigt das aktuelle Verzeichnis; cat gibt eine Datei aus:

pwd
cat inventory.yml
cat group_vars/all.yml

Das Inventar enthält YAML-kompatible strukturierte Daten. Seine Gruppe website enthält web1 und web2, jeweils mit der tatsächlichen Adresse ansible_host und den vorbereiteten SSH-Verbindungsdaten. Die Gruppe control enthält controller mit einer lokalen Verbindung. Verwende inventory.yml, das die projektbezogene ansible.cfg auswählt; die ursprüngliche inventory.ini des Images ist nicht das Kursinventar.

Die Gruppenvariablen enthalten backend_port (8090), die nur auf dem Controller verfügbare pool_api, den erlaubten release_root, den lokalen package_root und die Versionskennungen. baseline_release ist courtyard-preview; approved_release ist courtyard-weekend. Das sind Verzeichnisnamen, keine Ansible- oder Softwareversionen. Ein HTTP-Port bezeichnet einen Dienst, der an einer Adresse lauscht. /identity liefert JSON, also strukturierten Text zur tatsächlich laufenden Backend-Version. /pool liefert die aktuelle Liste aktiver Knoten des Controllers.

Zeigt ein Befehl „No such file or directory“, prüfe die Schreibweise mit ls und kehre bei Bedarf mit cd /home/labex/project zurück. Lesebefehle kannst du gefahrlos wiederholen. Bewahre alle Paketdateien, Verbindungsdaten, ursprünglichen Versionsdateien und privaten Notizen des Veranstalters unverändert auf.

Standardmäßig führt Ansible eine Aufgabe über SSH auf jedem ausgewählten Backend aus. Dort bedeutet 127.0.0.1 das jeweilige Backend selbst. delegate_to: controller führt dagegen diese einzelne Aufgabe auf dem Controller aus, dessen eigenes 127.0.0.1 die Pool-API bereitstellt. Delegierung verändert inventory_hostname nicht: Der Wert bezeichnet weiterhin das gerade verwaltete Backend. So kann eine Controller-Anfrage den richtigen Knoten nennen.

Erstelle inspect.yml. cat > filename schreibt eine Datei und ersetzt eine vorhandene Datei gleichen Namens. <<'YAML' beginnt ein Here-Dokument: Füge den Text bis zum abschließenden YAML in einer eigenen Zeile ein. Der Begrenzer in Anführungszeichen übernimmt den eingefügten Text wörtlich; Ansible verarbeitet die Ausdrücke später beim Ausführen des Playbooks. Die Einrückung verwendet Leerzeichen. {{ variable }} setzt eine Ansible-Variable ein. gather_facts: false überspringt das Sammeln hier nicht benötigter Maschineninformationen.

Das Modul uri sendet eine echte HTTP-Anfrage; seine Standardmethode GET liest eine Ressource. register speichert das tatsächliche Ergebnis unter einem Namen. Bei JSON-Antworten enthält .json die ausgewerteten Felder. debug gibt diese gespeicherten Beobachtungen aus; es erzeugt weder die Version noch den Pool-Zustand.

cat > inspect.yml <<'YAML'
- name: Inspect the running website without changing it
  hosts: website
  gather_facts: false
  tasks:
    - name: Read this backend on its own loopback address
      ansible.builtin.uri:
        url: "http://127.0.0.1:{{ backend_port }}/identity"
      register: backend
    - name: Read the pool on the controller
      ansible.builtin.uri:
        url: "{{ pool_api }}/pool"
      delegate_to: controller
      register: pool
    - name: Show the actual release and active pool
      ansible.builtin.debug:
        msg:
          node: "{{ inventory_hostname }}"
          release: "{{ backend.json.release }}"
          active: "{{ pool.json.active }}"
YAML

Führe das Playbook aus. ansible-playbook liest die Aufgaben und wendet sie auf das Inventar an:

ansible-playbook inspect.yml

Jedes Backend sollte release: courtyard-preview anzeigen, und active sollte sowohl web1 als auch web2 enthalten. Format und Adressen können abweichen. Beachte: Das erste uri läuft auf dem Backend; das Lesen des Pools zeigt dagegen eine delegierte Ausführung wie web1 -> controller. Beide Aufgaben lesen ausschließlich.

Ist die YAML-Einrückung falsch, kopiere den vollständigen Block erneut in die Datei und versuche es nochmals. Dieses Playbook darf niemals Dateien kopieren oder den Pool verändern. Jetzt hast du ein nützliches Beobachtungswerkzeug, das du nach der Veröffentlichung erneut ausführen kannst.

Veröffentliche auf einem Backend nach dem anderen

In diesem Schritt lieferst du das freigegebene Website-Paket per SSH aus, prüfst die echten Besucherinformationen und nimmst jedes Backend wieder in Betrieb, bevor das nächste beginnt.

Bleibe im Tab Controller in /home/labex/project. Liste die Pakete auf und lies anschließend das freigegebene Programm. ls listet Verzeichniseinträge auf:

ls packages
cat packages/courtyard-weekend/schedule.csv

Das freigegebene Programm enthält drei Angebote: Bread and stories, Acoustic afternoon und Print a postcard. Eine funktionierende Startseite allein reicht nicht: Besucher brauchen den vollständigen zugehörigen Download. Das Paket courtyard-incomplete ist für das nächste Lab vorgesehen; wähle hier courtyard-weekend, wie es die vorhandene Variable release_id bereits tut.

Dein Playbook verarbeitet mit serial: 1 einen Batch aus einem Host. Darin entfernt es den aktuellen Knoten aus dem Pool, kopiert das Paket, schaltet den aktiven Versionslink um, prüft beide fachlichen Ressourcen und fügt den Knoten wieder hinzu. Erst danach beginnt der nächste Batch. order: sorted lässt die Demonstration mit web1 beginnen. any_errors_fatal: true gibt einen unbehandelten Fehler ausdrücklich weiter, um den Play zu stoppen. In den hier verwendeten Batches mit einem Host lässt dessen Fehler bereits den ganzen Batch scheitern. Die Einstellung macht erledigte Aufgaben nicht rückgängig. Das nächste Lab ergänzt die Wiederherstellung.

Die Pool-API akzeptiert einen HTTP POST an /pool mit einem JSON-Inhalt: node nennt den verwalteten Knoten, und enabled ist der boolesche Wert false zum Entfernen beziehungsweise true zum Hinzufügen. body_format: json serialisiert diesen Inhalt korrekt. Pool-Aufgaben laufen auf dem Controller, Dateiaufgaben auf dem ausgewählten SSH-Backend.

file mit state: directory erstellt ein Verzeichnis; Modus '0755' erlaubt dem Eigentümer das Schreiben und allen das Lesen und Durchqueren. copy sendet das ausgewählte Controller-Paket an dieses Backend. Der abschließende / bei src kopiert den Verzeichnisinhalt mit Modus '0644' für lesbare Dateien. file mit state: link erstellt oder aktualisiert einen symbolischen Link: current verweist auf ein Versionsverzeichnis. Der vorbereitete HTTP-Server liest bei jeder Anfrage die echten Dateien über diesen Link. Das Umschalten ändert die aktive Website ohne Serverneustart und lässt das ursprüngliche Versionsverzeichnis unberührt.

Bei HTTP-Prüfungen auf dem Controller liefert hostvars[inventory_hostname].ansible_host ausdrücklich die Adresse des verwalteten Backends. Während einer Delegierung kann eine unqualifizierte Verbindungsvariable zum delegierten Host gehören. return_content: true speichert den tatsächlichen Antwortinhalt in .content.

assert verlangt, dass der Ausdruck that wahr ist. lookup('file', ...) liest die erwartete Paketdatei auf dem Controller; ~ verbindet Pfadzeichenfolgen. rstrip=False erhält abschließende Zeilenumbrüche, damit der gesamte Inhalt statt eines gekürzten Ausschnitts verglichen wird. Die vollständige Kandidaten-Startseite und die vollständige freigegebene CSV müssen vor dem Wiederaufnehmen übereinstimmen. Ein 404 bedeutet: Der Server hat geantwortet, aber die Ressource fehlt. Ein Verbindungsfehler bedeutet, dass der Dienst nicht erreichbar ist. Beides ist keine gesunde Veröffentlichung.

Schreibe den vollständigen Ablauf. Das Here-Dokument verwendet dieselbe Schreibmethode wie das Prüfwerkzeug:

cat > rollout.yml <<'YAML'
- name: Release one website backend at a time
  hosts: website
  order: sorted
  gather_facts: false
  serial: 1
  any_errors_fatal: true
  tasks:
    - name: Drain this backend from the pool
      ansible.builtin.uri:
        url: "{{ pool_api }}/pool"
        method: POST
        body_format: json
        body:
          node: "{{ inventory_hostname }}"
          enabled: false
      delegate_to: controller
    - name: Create this release directory
      ansible.builtin.file:
        path: "{{ release_root }}/releases/{{ release_id }}"
        state: directory
        mode: '0755'
    - name: Copy the selected package over SSH
      ansible.builtin.copy:
        src: "{{ package_root }}/{{ release_id }}/"
        dest: "{{ release_root }}/releases/{{ release_id }}/"
        mode: '0644'
    - name: Activate the selected release
      ansible.builtin.file:
        src: "{{ release_root }}/releases/{{ release_id }}"
        dest: "{{ release_root }}/current"
        state: link
    - name: Read the real candidate homepage
      ansible.builtin.uri:
        url: "http://{{ hostvars[inventory_hostname].ansible_host }}:{{ backend_port }}/"
        return_content: true
      delegate_to: controller
      register: homepage
    - name: Require the complete candidate homepage
      ansible.builtin.assert:
        that: homepage.content == lookup('file', package_root ~ '/' ~ release_id ~ '/index.html', rstrip=False)
    - name: Read the real candidate download
      ansible.builtin.uri:
        url: "http://{{ hostvars[inventory_hostname].ansible_host }}:{{ backend_port }}/schedule.csv"
        return_content: true
      delegate_to: controller
      register: download
    - name: Require the complete approved program
      ansible.builtin.assert:
        that: download.content == lookup('file', package_root ~ '/' ~ approved_release ~ '/schedule.csv', rstrip=False)
    - name: Rejoin only after both business checks
      ansible.builtin.uri:
        url: "{{ pool_api }}/pool"
        method: POST
        body_format: json
        body:
          node: "{{ inventory_hostname }}"
          enabled: true
      delegate_to: controller
YAML

Prüfe zuerst YAML und Aufgabenstruktur, ohne zu veröffentlichen. --syntax-check prüft die Syntax, beweist aber nicht das fachliche Ergebnis:

ansible-playbook rollout.yml --syntax-check

Meldet die Prüfung einen Schreib- oder Einrückungsfehler, kopiere die vollständige Datei erneut und wiederhole die Prüfung. Starte dann die tatsächliche Bereitstellung:

ansible-playbook rollout.yml

Die Aufgaben für web1 sollten vor denen für web2 fertig werden. Beide Hosts sollten mit failed=0 und unreachable=0 enden. Die operativen Anfragen zum Entfernen und Hinzufügen können ok melden; HTTP-Erfolg und tatsächliche Wirkung sind wichtiger als der Zähler changed im Rückblick. Stoppt ein normaler Bearbeitungsfehler den Lauf, korrigiere die gesamte Datei und starte dasselbe freigegebene Paket erneut. Eine Wiederholung entfernt, prüft und integriert wieder; sie ist keine Bereitstellung ohne Operationen.

Prüfe den laufenden Zustand mit deinem ursprünglichen Werkzeug:

ansible-playbook inspect.yml

Beide Knoten sollten jetzt courtyard-weekend melden und im Pool aktiv sein. Wechsle zu Website und lade neu. Die Überschrift sollte A little room for a good afternoon. lauten. Klicke auf The program, prüfe alle drei Angebote, lade die CSV erneut herunter und öffne die neue Datei, um alle drei vollständigen Datenzeilen zu bestätigen. Bei einem früheren Download kann der Browser eine Nummer an den Dateinamen anhängen. Plan a visit muss weiterhin zu den Informationen über den stufenfreien Garteneingang führen.

Das aktualisierte Programm enthält alle drei freigegebenen Angebote; der echte CSV-Download steht neben der Liste.

Aktualisierte Website von Courtyard Sessions mit den drei Programmpunkten

Das belegt eine funktionierende Aktualisierung auf zwei Knoten in der vorbereiteten Umgebung mit kurzen Anfragen. Es begründet keine Garantie für unterbrechungsfreie Änderungen bei beliebigem Produktionstraffic oder beliebigen Anwendungen.

Zusammenfassung

Du hast ein Prüfwerkzeug mit rein lesenden Aufgaben erstellt und mit einer sequenziellen Ansible-Bereitstellung eine echte Website samt vollständigem Programmdownload ausgeliefert. Delegierte Pool-Änderungen und echte Inhaltsprüfungen verknüpfen die Veröffentlichungsentscheidungen mit dem jeweils verwalteten Backend.