SSD Nodes Learn 🎉 VPS ab $5.50/Monat
Anleitungen Matt ConnorVon Matt Connor

Ansible: Nicht erreichbare Hosts ignorieren

Ansible unterscheidet nicht erreichbare Hosts von fehlgeschlagenen Aufgaben. Nutzen Sie ignore_unreachable, serial und max_fail_percentage, ohne übergangene Hosts zu übersehen.

Ein nicht erreichbarer Host ist keine fehlgeschlagene Aufgabe

Um nicht erreichbare Hosts in Ansible zu ignorieren, setzen Sie ignore_unreachable: true. Der Schalter funktioniert. Entscheidend ist, wann Sie ihn verwenden. Ansible behandelt zwei unterschiedliche Probleme auf zwei unterschiedliche Arten. Eine Aufgabe, die auf dem Host ausgeführt wurde und einen Fehler zurückgibt, ist ein Fehler. Ein Host, zu dem Ansible überhaupt keine Verbindung herstellen konnte, ist nicht erreichbar. ignore_errors gilt nur für den ersten Fall. ignore_unreachable gilt nur für den zweiten Fall.

Der Unterschied ist in der Zusammenfassung eines Plays zu sehen.

PLAY RECAP *********************************************************************
web1  : ok=7  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
web2  : ok=0  changed=0  unreachable=1  failed=0  skipped=0  rescued=0  ignored=0

Ansible hat eine Verbindung zu web1 hergestellt und sieben Aufgaben ausgeführt. web2 zeigt unreachable=1 und failed=0. Das bedeutet, dass dort überhaupt nichts ausgeführt wurde. Ansible konnte keine Verbindung herstellen, entfernte den Host daher aus dem Play und setzte die Ausführung mit den übrigen Hosts fort. Wenn dieses Play ein Sicherheitsupdate installiert hat, ist einer Ihrer Server nicht aktualisiert.

Was macht einen Host nicht erreichbar?

„Nicht erreichbar“ bedeutet, dass die Verbindung fehlgeschlagen ist, bevor ein Modul den Host erreicht hat. Es gibt keine Modulausgabe, die Sie auswerten können, sondern nur einen Verbindungsfehler. Dieser erscheint bei der ersten Aufgabe, die den Rechner verwendet.

fatal: [web2]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 203.0.113.20 port 22: Connection refused", "unreachable": true}

Das Feld msg enthält die eigentliche Ursache. Diese Fälle treten häufig auf:

  • Connection refused: Die TCP-Verbindung wurde abgelehnt. An diesem Port lauscht kein Dienst. sshd wurde beendet, oder SSH verwendet einen anderen Port und Ihr Inventar enthält weiterhin 22.
  • Connection timed out: Es hat überhaupt keine Antwort gegeben. Eine Firewall verwirft die Pakete, oder der Server ist ausgeschaltet. Jeder Versuch wartet bis zum vollständigen Verbindungs-Timeout. Dieses beträgt standardmäßig 10 Sekunden.
  • Host key verification failed.: Der Schlüssel in ~/.ssh/known_hosts stimmt nicht mit dem Schlüssel überein, den der Server präsentiert hat. Ein neu erstellter VPS behält seine IP-Adresse und erhält einen neuen Hostschlüssel. Nach einer Neuinstallation ist das erwartbar. Zu jedem anderen Zeitpunkt ist dieser Fehler kritisch.
  • Permission denied (publickey): SSH hat geantwortet und Ihren Schlüssel abgelehnt. Der Port funktioniert, daher liegt ein Authentifizierungsproblem vor. Meistens ist ansible_user falsch oder ein Schlüssel wurde nicht geladen.
  • Timeout (12s) waiting for privilege escalation prompt: Die Verbindung hat funktioniert, aber become nicht. sudo wartet auf ein Passwort, das nicht übergeben wird.

Ein fehlender Python-Interpreter ist eine häufig erwartete Ursache und gehört nicht in diese Liste. SSH stellt die Verbindung her, der Host ist also erreichbar. Anschließend hat das Modul jedoch keine Laufzeitumgebung:

fatal: [db1]: FAILED! => {"changed": false, "module_stdout": "/bin/sh: 1: /usr/bin/python3: not found\r\n", "msg": "The module failed to execute correctly, you probably need to set the interpreter", "rc": 127}

Diese Zeile meldet FAILED!. Die Zusammenfassung zählt den Fehler unter failed, daher wird ignore_unreachable den Host nicht verwenden. Setzen Sie ansible_python_interpreter für diesen Host, oder installieren Sie python3 auf ihm.

Unerreichbare Hosts in einem Play ignorieren

Auf Task-Ebene steht das Schlüsselwort neben dem Modul:

- name: Read the package list, and do not stop if the host is down
  ansible.builtin.command: dpkg -l
  register: packages
  changed_when: false
  ignore_unreachable: true

Auf Play-Ebene legt es den Standard für jede Task im Play fest. Eine einzelne Task kann diesen Standard zurücksetzen:

- name: Opportunistic fleet maintenance
  hosts: all
  ignore_unreachable: true
  tasks:
    - name: This runs, cannot connect, and the play carries on
      ansible.builtin.ping:

    - name: This one still ends the play for a host that is down
      ansible.builtin.ping:
      ignore_unreachable: false

Die Auswirkungen im Hintergrund sollten Sie kennen. Wenn ignore_unreachable gesetzt ist, wird der Host nicht mehr aus dem Play entfernt. Jede nachfolgende Task versucht daher erneut, eine Verbindung herzustellen, und schlägt auf dieselbe Weise erneut fehl. Jeder dieser Versuche wartet bis zum Ablauf des Verbindungstimeouts: 10 Sekunden, sofern Sie timeout in ansible.cfg nicht ändern. Ein Play mit 20 Tasks gegen einen nicht erreichbaren Server verlängert den Lauf dadurch um etwa 200 Sekunden und erzeugt 20 rote Zeilen im Log.

Prüfen Sie den Host daher einmal und beenden Sie die Verarbeitung für diesen Host anschließend sauber:

- name: Opportunistic fleet maintenance
  hosts: all
  gather_facts: false
  tasks:
    - name: Check that the host answers before doing any work
      ansible.builtin.ping:
      register: reachable
      ignore_unreachable: true

    - name: End the play for this host if it never answered
      ansible.builtin.meta: end_host
      when: reachable.unreachable | default(false)

    - name: Gather facts now that the connection is known good
      ansible.builtin.setup:

    - name: Refresh the package index
      ansible.builtin.apt:
        update_cache: true
      become: true

Damit gibt es pro nicht erreichbarem Host einen Verbindungsversuch statt eines Versuchs pro Task. end_host, das in Ansible 2.8 hinzugefügt wurde, beendet das Play für den aktuellen Host, ohne ihn als fehlgeschlagen zu markieren. Der Schlüssel unreachable ist nur dann im registrierten Ergebnis vorhanden, wenn die Verbindung fehlgeschlagen ist. default(false) hält die Bedingung daher für jeden Host gültig, der geantwortet hat. Das Sammeln von Fakten ist auf Play-Ebene deaktiviert, weil sonst die implizite Task Gathering Facts auf die unterbrochene Verbindung treffen würde. Stattdessen soll Ihre eigene Ping-Task diese Prüfung ausführen.

ignore_unreachable ist ein Play-Schlüsselwort und ein Task-Schlüsselwort. Lassen Sie es im Playbook stehen, wo es für Leser sichtbar ist, statt es in einer Rolle zu verbergen. Es legt fest, welche Hosts ein Lauf nicht erreichen darf. Die Trennung zwischen Playbooks und Rollen erläutert, welche Ebene für eine Einstellung wie diese zuständig sein sollte.

Warum ignore_errors hier das falsche Werkzeug ist

Die Ansible-Dokumentation beschreibt diese Einschränkung ausdrücklich. ignore_errors „Funktioniert nur, wenn die Task ausgeführt werden kann und den Wert failed zurückgibt. Dadurch ignoriert Ansible keine Fehler durch nicht definierte Variablen, Verbindungsfehler, Ausführungsprobleme (beispielsweise fehlende Pakete) oder Syntaxfehler.“

Ein Verbindungsfehler wird niemals zu einem Task-Ergebnis mit failed: true. Er wird als separates Flag gemeldet. Ansible verarbeitet dieses Flag zuerst: Der Host wird auf die Liste der nicht erreichbaren Hosts gesetzt und aus dem Play entfernt. Setzen Sie ignore_errors: true für alle zwölf Tasks eines Plays, beendet ein Host mit geschlossenem SSH-Port die Ausführung trotzdem bereits bei der ersten Task. Das ist die häufigste Verwechslung in diesem Bereich. Suchen Sie deshalb insbesondere in älteren Playbooks danach, etwa in Playbooks, die Sie beim Lernen, ein erstes Playbook für einen VPS zu schreiben erstellt haben.

Debugging vor dem Unterdrücken

Eine dauerhafte Unterdrückung führt dazu, dass sich eine Serverflotte auseinanderentwickelt. Denn ein Host, den niemand erreichen kann, wird auch von niemandem gepatcht. Arbeiten Sie zunächst diese Reihenfolge ab. Jeder Befehl hier liest nur Daten.

  1. ansible web2 -i inventory.ini -m ansible.builtin.ping -o führt ein Modul gegen einen Host aus und gibt eine Zeile aus.
  2. Fügen Sie demselben Befehl -vvvv hinzu. Ansible gibt den vollständigen SSH-Befehl aus, den es erstellt, einschließlich Zielbenutzer, Port, privatem Schlüssel und übergebenen Optionen.
  3. Führen Sie diesen SSH-Befehl mit -v selbst aus. Wenn eine einfache SSH-Verbindung nicht möglich ist, liegt das Problem unterhalb von Ansible. Kein Playbook-Schlüsselwort kann es beheben.
  4. Lesen Sie die Zeichenfolge msg und gleichen Sie sie mit der obigen Liste ab. Connection refused und Connection timed out verweisen auf zwei verschiedene Stellen: einmal auf den SSH-Dienst und einmal auf den Netzwerkpfad.
  5. Bei Host key verification failed. prüfen Sie, was Sie mit ssh-keygen -F web2.example.com gespeichert haben. Wenn der Server neu erstellt wurde, entfernen Sie den alten Eintrag mit ssh-keygen -R web2.example.com und akzeptieren Sie den neuen Schlüssel, nachdem Sie ihn anhand der Provider-Konsole geprüft haben. Das Setzen von host_key_checking = False in ansible.cfg beseitigt den Fehler. Es entfernt jedoch auch die Prüfung, die erkennen würde, dass nun ein anderer Computer unter dieser Adresse antwortet.
  6. Bei Permission denied (publickey) prüfen Sie, was Ansible verwenden soll. ansible-inventory -i inventory.ini --host web2 gibt die aktiven Variablen aus, einschließlich ansible_user und ansible_port.
  7. Wenn SSH funktioniert, die Module jedoch nicht, prüfen Sie den Interpreter mit ansible web2 -m ansible.builtin.raw -a 'command -v python3 || echo none'. Das Modul raw führt einen Befehl über die Shell aus und benötigt kein Python auf dem Zielhost.

Erst danach wird das Ignorieren des Hosts zu einer bewussten Entscheidung statt zu einer Gewohnheit.

Der Recap zählt nicht erreichbare Hosts separat, und CI erkennt das normalerweise nicht

ansible-playbook beendet sich bei Erfolg mit 0, mit 2, wenn mindestens ein Host fehlgeschlagen ist, und mit 4, wenn mindestens ein Host nicht erreichbar war. Diese beiden Werte sind im Quellcode Bit-Flags. Ein Lauf mit einem fehlgeschlagenen und einem nicht erreichbaren Host endet daher mit 6. Der Befehl ansible gibt dieselben Codes zurück. Diese Angaben wurden im August 2026 anhand des Quellcodes von ansible-core geprüft.

Setzen Sie nun ignore_unreachable: true und führen Sie dasselbe Play mit sieben Tasks gegen denselben nicht erreichbaren Host aus:

PLAY RECAP *********************************************************************
web1  : ok=7  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
web2  : ok=7  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=7

web2 meldet unreachable=0 und sieben Tasks ok, und der Lauf endet mit 0. Wenn das Schlüsselwort gesetzt ist, erhöht Ansible für diesen Host die Zähler ok und ignored statt des Zählers, den Ansible dark nennt und der die Spalte unreachable füllt. Die roten Zeilen UNREACHABLE! werden weiterhin ausgegeben. Das Log ist daher korrekt, der Recap und der Exit-Code sind es jedoch nicht.

Ein CI-Job, der das Playbook ausführt und nur $? prüft, behandelt diesen Lauf als erfolgreich. In seiner Zusammenfassung steht dann nicht, dass eine Maschine nie kontaktiert wurde. Führen Sie die Erreichbarkeitsprüfung als eigenen Schritt vor dem Play aus:

ansible all -i inventory.ini -m ansible.builtin.ping -o

Das gibt für jeden Host eine Zeile aus und endet mit 4, wenn ein Host nicht erreichbar ist. Dadurch kann die Pipeline mit einem Fehler abbrechen, und die Hostnamen stehen im Log. ping benötigt auf dem Zielsystem einen funktionierenden Python-Interpreter. Der Befehl prüft daher etwas mehr als nur die Verbindung, was normalerweise erwünscht ist. Führen Sie das Playbook anschließend mit ignore_unreachable aus, damit die erreichbaren Hosts weiterhin geändert werden.

any_errors_fatal und max_fail_percentage innerhalb eines Batches

Diese beiden Play-Schlüsselwörter legen fest, was passiert, wenn auf einem Teil der Hosts ein Fehler auftritt. Nicht erreichbare Hosts behandeln sie unterschiedlich.

any_errors_fatal: true reagiert auf einen nicht erreichbaren Host. Ansible beendet die aktuelle Aufgabe auf den übrigen Hosts des Batches und beendet anschließend das Play für alle Hosts im Batch. Verwenden Sie diese Option, wenn ein Lauf nur als Ganzes erfolgreich sein darf, beispielsweise bei einer koordinierten Schemaänderung.

max_fail_percentage: 30 reagiert nicht auf einen nicht erreichbaren Host. Die Prüfung teilt die Anzahl der fehlgeschlagenen Hosts durch die Größe des Batches. Nicht erreichbare Hosts werden in einer separaten Liste geführt und fließen daher nicht in diese Zahl ein. Zehn Hosts mit vier nicht erreichbaren Hosts laufen unter max_fail_percentage: 10 weiter, während zwei Hosts, bei denen eine Aufgabe fehlschlägt, das Play beenden. Die Dokumentation nennt eine weitere Stolperfalle: „Der festgelegte Prozentsatz muss überschritten werden, nicht erreicht sein.“ Mit serial: 4 bedeutet das: Um nach zwei Fehlern von vier Hosts zu stoppen, müssen Sie 49 statt 50 angeben.

Es gibt einen Fall, in dem nicht erreichbare Hosts einen Lauf selbstständig beenden. Wenn jeder Host im Batch fehlgeschlagen oder nicht erreichbar ist, kann Ansible nicht weiterarbeiten und beendet das Play mit NO MORE HOSTS LEFT.

serial: Eine Änderung stufenweise im gesamten Serverbestand ausrollen

- name: Rolling nginx config update
  hosts: webservers
  serial: 2
  max_fail_percentage: 25
  tasks:
    - name: Deploy the site config
      ansible.builtin.template:
        src: site.conf.j2
        dest: /etc/nginx/conf.d/site.conf
        owner: root
        mode: "0644"
      become: true
      notify: Reload nginx
  handlers:
    - name: Reload nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded
      become: true

serial: 2 führt den gesamten Play zunächst auf zwei Hosts aus, wartet, bis er abgeschlossen ist, und startet dann die nächste Gruppe von zwei Hosts. serial: "25%" skaliert mit der Größe der Gruppe. Eine Liste, serial: [1, 5, 10], definiert die Canary-Reihenfolge: zuerst ein Host, dann fünf, dann zehn; verbleibende Hosts werden in Batches der zuletzt verwendeten Größe ausgeführt. max_fail_percentage wird pro Batch gemessen, daher ergänzen sich beide Einstellungen. Wenn der erste Rechner ausfällt, wird die Ausführung beendet, bevor vierzig Rechner betroffen sind. Dadurch ist die Verwaltung eines Linux-Serverbestands von einer einzigen Steuerungsmaschine aus mit einem einzigen Befehl sicher möglich.

Wann nicht erreichbare Hosts ignoriert werden sollten – und wann nicht

Ignorieren Sie sie bei opportunistischen Aufgaben. Eine Faktensammlung oder eine stündliche Drift-Prüfung verliert nichts, wenn ein ausgefallener Host übersprungen wird, weil der nächste Durchlauf ihn wieder erfasst. Auf Play-Ebene ist ignore_unreachable: true dafür die richtige Lösung. Kombinieren Sie sie mit dem Ping-Schritt, damit die übersprungenen Namen an einer Stelle landen, die eine Person prüft.

Bei einem Lauf zur Installation von Sicherheits-Patches dürfen Sie sie niemals ignorieren. Der Wert dieses Laufs besteht in der Zusicherung, dass jeder Host den Fix erhalten hat. Wenn der Status „nicht erreichbar“ unterdrückt wird, erscheint „ein Server ist weiterhin verwundbar“ als vollständig erfolgreiche Zusammenfassung. Ein Host, der seit zwei Wochen nicht erreichbar ist, ist höchstwahrscheinlich besonders weit zurück. Lassen Sie den Lauf mit 4 beenden, und lassen Sie eine Person den Fall prüfen.

In beiden Fällen gilt dieselbe Regel: Unterdrücken Sie den Abbruch, niemals den Eintrag. Wenn ein Host übersprungen wurde, muss dies irgendwo erkennbar sein: in der Zusammenfassung, im CI-Log oder in einem Monitoring-Alarm. Ansible weiß nur während der Sekunden, in denen ein Play gegen einen Host ausgeführt wird, dass dieser Host existiert. Daher ist Ansible nicht geeignet, um festzustellen, dass ein Server seit Dienstag ausgefallen ist. Diese Aufgabe gehört zum Monitoring. Mit einem Ansible-Playbook, das Zabbix installiert lässt sich innerhalb eines Nachmittags ein flottenweiter Überblick einrichten.

FAQ

Was ist der Unterschied zwischen ignore_errors und ignore_unreachable in Ansible?

ignore_errors: true gilt für eine Aufgabe, die auf dem Host ausgeführt wurde und einen Fehler zurückgegeben hat, beispielsweise weil ein Befehl mit einem Exit-Code ungleich 0 beendet wurde. ignore_unreachable: true gilt für einen Host, zu dem Ansible keine Verbindung herstellen konnte und auf dem daher kein Modul ausgeführt wurde. Die beiden Schlüsselwörter beziehen sich auf unterschiedliche Felder im Aufgabenergebnis. Keines deckt den jeweils anderen Fall ab. In der Ansible-Dokumentation heißt es, dass ignore_errors „Ansible nicht dazu bringt, Fehler wegen nicht definierter Variablen, Verbindungsfehler, Ausführungsprobleme (beispielsweise fehlende Pakete) oder Syntaxfehler zu ignorieren“. Ein geschlossener SSH-Port ist ein Verbindungsfehler.

Wird der Host durch ignore_unreachable aus der Play-Zusammenfassung ausgeblendet?

Im Ergebnis ja. Wenn dieses Schlüsselwort gesetzt ist, zählt Ansible den Host nicht mehr unter unreachable, sondern zählt ihn pro Aufgabe als ok und ignored. Der Lauf wird anschließend mit 0 beendet. Die Zeilen mit fatal: [host]: UNREACHABLE! werden weiterhin ausgegeben. Das Log bleibt daher korrekt, auch wenn die Zusammenfassung und der Exit-Code den Zustand nicht abbilden. Achten Sie auf die Spalte ignored. Alternativ können Sie ansible all -m ansible.builtin.ping -o als separaten Schritt ausführen, damit ein nicht erreichbarer Host an irgendeiner Stelle weiterhin einen Exit-Code ungleich 0 erzeugt.

Welchen Exit-Code gibt ansible-playbook zurück, wenn ein Host nicht erreichbar ist?

Der Rückgabewert ist 4. Ein Lauf mit mindestens einem fehlgeschlagenen Host gibt 2 zurück. Die beiden Werte sind Bit-Flags. Ein Lauf mit mindestens einem Fehler und mindestens einem nicht erreichbaren Host gibt daher 6 zurück. Ein fehlerfreier Lauf gibt 0 zurück. Diese Codes wurden im August 2026 anhand des Quellcodes von ansible-core geprüft. Das Setzen von ignore_unreachable: true entfernt die 4. Deshalb kann eine Pipeline, die nur den Exit-Code prüft, eine übersprungene Maschine nicht erkennen.

Wie überspringe ich den Rest eines Plays für einen Host, der nicht geantwortet hat?

Definieren Sie die erste Aufgabe als ansible.builtin.ping mit ignore_unreachable: true und register: reachable. Fügen Sie danach ansible.builtin.meta: end_host unter der Bedingung when: reachable.unreachable | default(false) ein. end_host beendet das Play für diesen Host, ohne ihn als fehlgeschlagen zu markieren. Setzen Sie gather_facts: false auf dem Play, damit Ihr Ping die unterbrochene Verbindung erkennt. Ohne dieses Muster bleibt der nicht erreichbare Host im Play. Jede spätere Aufgabe wartet dann erneut das Verbindungs-Timeout ab.

Sollte ich nicht erreichbare Hosts während eines Security-Patch-Laufs ignorieren?

Nein. Ein Patch-Lauf ist sinnvoll, weil er garantiert, dass jeder Host das Update erhalten hat. Das Ignorieren nicht erreichbarer Hosts ersetzt diese Garantie durch eine grüne Zusammenfassung. Lassen Sie den Lauf mit 4 beenden, lesen Sie die Namen der Hosts aus, die nicht geantwortet haben, und beheben Sie das Problem. Die Unterdrückung gehört in wiederholte, opportunistische Läufe, bei denen der nächste Durchlauf die ausgelassenen Hosts erfasst.

#ansible#playbooks#error-handling#inventory#automation