Ansible: ignorare gli host irraggiungibili
Scopri perché Ansible distingue tra host unreachable e task falliti, come usare ignore_unreachable, serial e max_fail_percentage senza perdere quelli saltati.
Un host irraggiungibile non indica un'attività non riuscita
Per ignorare gli host irraggiungibili in Ansible, imposta ignore_unreachable: true. L'opzione funziona. La parte importante è sapere quando usarla, perché Ansible gestisce questi due problemi in modo diverso. Un'attività eseguita sull'host che restituisce un errore è un failure. Un host a cui Ansible non riesce a connettersi è unreachable. ignore_errors copre solo il primo caso. ignore_unreachable copre solo il secondo.
Ecco la differenza nel riepilogo dell'esecuzione.
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=0Ansible si è connesso a web1 ed ha eseguito sette attività. web2 mostra unreachable=1 e failed=0, quindi su quell'host non è stata eseguita alcuna attività. Ansible non ha mai stabilito una connessione, quindi ha rimosso l'host dall'esecuzione del play e ha continuato con il resto. Se il play installava un aggiornamento di sicurezza, uno dei server non lo ha ricevuto.
Che cosa rende un host irraggiungibile
Irraggiungibile significa che la connessione è fallita prima che un modulo raggiungesse l'host. Non c'è alcun output del modulo da leggere, ma solo un errore di connessione, che compare nella prima attività che contatta il computer.
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}Il campo msg contiene la causa effettiva. Questi sono i casi che incontrerai:
Connection refused: la connessione TCP è stata rifiutata, quindi non c'è alcun processo in ascolto su quella porta. sshd è arrestato oppure SSH è stato spostato su un'altra porta e l'inventory indica ancora 22.Connection timed out: non ha risposto nulla. Un firewall sta scartando i pacchetti oppure il server è spento. Ogni tentativo attende per l'intero timeout della connessione, che per impostazione predefinita è di 10 secondi.Host key verification failed.: la chiave in~/.ssh/known_hostsnon corrisponde alla chiave presentata dal server. Un VPS ricreato mantiene il proprio indirizzo IP, ma riceve una nuova chiave host; dopo una reinstallazione è quindi normale, mentre in qualsiasi altro momento è un problema serio.Permission denied (publickey): SSH ha risposto e ha rifiutato la tua chiave. La porta funziona, quindi il problema riguarda l'autenticazione, in genere per unansible_usererrato o per una chiave non caricata.Timeout (12s) waiting for privilege escalation prompt: la connessione è riuscita, mabecomeno. sudo attende una password che non arriva.
Un interprete Python mancante è la causa che ci si aspetta di trovare in questo elenco, ma non appartiene a questa categoria. SSH si connette, quindi l'host è raggiungibile. A quel punto il modulo non ha nulla da eseguire:
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}Quella riga indica FAILED! e il riepilogo la conta in failed, quindi ignore_unreachable non lo contatterà mai. Imposta ansible_python_interpreter per quell'host oppure installa python3 su di esso.
Come ignorare gli host non raggiungibili in un play
A livello di task, la keyword si trova accanto al modulo:
- 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: trueA livello di play, imposta il valore predefinito per ogni task del play; un singolo task può ripristinarlo:
- 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È importante sapere cosa cambia internamente. Con ignore_unreachable impostato, l'host non viene più rimosso dal play. Ogni task successivo tenta quindi di connettersi di nuovo e fallisce nello stesso modo. Ogni tentativo attende la scadenza del timeout di connessione: 10 secondi, a meno che non si modifichi timeout in ansible.cfg. Un play di 20 task diretto a un server non raggiungibile aggiunge circa 200 secondi all'esecuzione e 20 righe rosse al log.
Eseguire quindi un solo controllo, poi interrompere correttamente l'elaborazione per quell'host:
- 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: trueIn questo modo viene eseguito un tentativo di connessione per ogni host non raggiungibile, invece di uno per ogni task. end_host, aggiunto in Ansible 2.8, termina il play per l'host corrente senza contrassegnarlo come failed. La chiave unreachable esiste nel risultato registrato solo quando la connessione non è riuscita. default(false) mantiene quindi valida la condizione per ogni host che ha risposto. La raccolta dei facts è disattivata a livello di play perché, altrimenti, il task implicito Gathering Facts sarebbe il task che incontra la connessione interrotta. In questo modo il controllo della connessione viene eseguito dal ping definito dall'utente.
ignore_unreachable è una keyword del play e del task. Mantenerla nel playbook, in una posizione visibile al lettore, anziché all'interno di un role, perché determina quali host un'esecuzione può non raggiungere. La distinzione tra playbook e role spiega quale livello dovrebbe gestire un'impostazione di questo tipo.
Perché ignore_errors è lo strumento sbagliato in questo caso
La documentazione di Ansible è chiara su questo limite. ignore_errors "funziona solo quando il task può essere eseguito e restituisce un valore pari a 'failed'. Non consente ad Ansible di ignorare gli errori relativi a variabili non definite, i problemi di connessione, gli errori di esecuzione (ad esempio, pacchetti mancanti) o gli errori di sintassi."
Un errore di connessione non diventa mai un risultato del task con failed: true. Viene segnalato tramite un flag separato e Ansible gestisce prima quel flag: inserisce l'host nell'elenco degli host irraggiungibili e lo rimuove dal play. Impostare ignore_errors: true su tutti e dodici i task di un play non cambia questo comportamento: se la porta SSH dell'host è chiusa, l'esecuzione si interrompe comunque al primo task. È la confusione più comune in questo ambito. Conviene cercarla con grep nei playbook più vecchi, soprattutto in quelli scritti mentre si imparava a scrivere il primo playbook per un VPS.
Eseguire il debug prima di sopprimere l’errore
Una soppressione che diventa permanente causa il disallineamento della flotta, perché l’host che nessuno riesce a raggiungere è anche l’host che nessuno aggiorna. Prima segui questo ordine. Tutti i comandi riportati qui eseguono soltanto letture.
ansible web2 -i inventory.ini -m ansible.builtin.ping -oesegue un modulo su un singolo host e stampa una riga.- Aggiungi
-vvvvallo stesso comando. Ansible stampa il comando ssh completo che genera, inclusi l’utente di destinazione, la porta, la chiave privata e le opzioni passate. - Esegui manualmente quel comando ssh con
-v. Se ssh normale non riesce a connettersi, il problema è al di sotto di Ansible e nessuna direttiva del playbook può risolverlo. - Leggi la stringa
msge confrontala con l’elenco precedente.Connection refusedeConnection timed outindicano due punti diversi: uno nel servizio SSH e l’altro nel percorso di rete. - Per
Host key verification failed., controlla ciò che hai memorizzato conssh-keygen -F web2.example.com. Se il server è stato ricreato, rimuovi la voce precedente conssh-keygen -R web2.example.come accetta la nuova chiave dopo averla verificata nella console del provider. Impostarehost_key_checking = Falseinansible.cfgelimina l’errore, ma rimuove anche il controllo che segnalerebbe la risposta di un’altra macchina su quell’indirizzo. - Per
Permission denied (publickey), verifica quale configurazione Ansible ritiene di dover usare.ansible-inventory -i inventory.ini --host web2stampa le variabili effettive, incluseansible_usereansible_port. - Se SSH funziona ma i moduli no, controlla l’interprete con
ansible web2 -m ansible.builtin.raw -a 'command -v python3 || echo none'. Il modulorawesegue un comando tramite la shell e non richiede Python sul target.
Solo a questo punto ignorare l’host diventa una scelta consapevole invece che un’abitudine.
Il riepilogo conta separatamente gli host irraggiungibili, ma la CI di solito non rileva il problema
ansible-playbook restituisce 0 in caso di successo, 2 quando almeno un host ha avuto un errore e 4 quando almeno un host è irraggiungibile. Questi due valori sono flag binari nel codice sorgente. Di conseguenza, un'esecuzione con un host in errore e un host irraggiungibile restituisce 6. Il comando ansible restituisce gli stessi codici. Questi valori sono stati verificati nel codice sorgente di ansible-core ad agosto 2026.
Ora imposta ignore_unreachable: true ed esegui lo stesso play con sette task sullo stesso host non raggiungibile:
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=7web2 segnala unreachable=0 e sette task ok, mentre l'esecuzione termina con codice 0. Quando la keyword è impostata, Ansible incrementa per quell'host i contatori ok e ignored invece del contatore chiamato dark, che è quello che valorizza la colonna unreachable. Le righe rosse UNREACHABLE! vengono comunque visualizzate. Il log è quindi corretto, ma il riepilogo e il codice di uscita non lo sono.
Un job CI che esegue il playbook e controlla soltanto $? considera l'esecuzione riuscita. Nel riepilogo non compare alcuna indicazione del fatto che una macchina non sia mai stata contattata. Rendi il controllo della raggiungibilità uno step separato, prima del play:
ansible all -i inventory.ini -m ansible.builtin.ping -oQuesto comando stampa una riga per ogni host e restituisce 4 se almeno un host è irraggiungibile. In questo modo la pipeline può fallire e il log contiene i nomi degli host coinvolti. ping richiede un interprete Python funzionante sul target. Verifica quindi qualcosa in più della sola connessione, che di solito è ciò che serve. Esegui poi il playbook con ignore_unreachable, così gli host raggiungibili applicano comunque la modifica.
any_errors_fatal e max_fail_percentage in un batch
Queste due parole chiave dei play determinano cosa accade dopo un errore su una parte del parco di host e gestiscono in modo diverso gli host non raggiungibili.
any_errors_fatal: true reagisce a un host non raggiungibile. Ansible completa il task corrente sul resto del batch, quindi interrompe il play per tutti gli host del batch. Usarlo quando l'esecuzione ha senso solo se viene completata interamente o non viene completata affatto, ad esempio per una modifica coordinata dello schema.
max_fail_percentage: 30 non reagisce a un host non raggiungibile. Il controllo divide il numero di host con failed per la dimensione del batch. Gli host non raggiungibili vengono mantenuti in un elenco separato, quindi non modificano mai quel numero. Dieci host, di cui quattro non raggiungibili, continuano l'esecuzione con max_fail_percentage: 10, mentre due host che falliscono un task interrompono il play. La documentazione aggiunge un'altra precisazione: "La percentuale impostata deve essere superata, non raggiunta." Con serial: 4, per interrompere l'esecuzione dopo due errori su quattro bisogna specificare 49, non 50.
Esiste un caso in cui gli host non raggiungibili interrompono autonomamente l'esecuzione. Se tutti gli host del batch sono in stato failed o non raggiungibili, Ansible non ha più host su cui operare e termina il play con NO MORE HOSTS LEFT.
serial: applicare una modifica progressivamente a tutti i server
- 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: trueserial: 2 esegue l'intero play su due host, lo completa, quindi avvia il gruppo successivo di due host. serial: "25%" si adatta alle dimensioni del gruppo. Un elenco, serial: [1, 5, 10], definisce una progressione canary: prima un host, poi cinque, quindi dieci; gli host rimanenti vengono eseguiti in gruppi delle ultime dimensioni utilizzate. max_fail_percentage viene misurato per gruppo, quindi i due parametri operano insieme. Se la prima macchina si arresta in errore, l'esecuzione si interrompe prima di raggiungere la quarantesima. Questo consente di gestire una flotta di server Linux da un'unica macchina di controllo in sicurezza con un solo comando.
Quando ignorare gli host non raggiungibili e quando no
Ignorateli per le attività opportunistiche. Una raccolta di facts o un controllo orario delle differenze non perde informazioni se salta un host non disponibile, perché il passaggio successivo lo includerà. In questo caso la soluzione corretta è ignore_unreachable: true a livello di play, insieme al passaggio di ping, in modo che i nomi saltati finiscano in un punto che una persona consulterà.
Non ignorateli mai durante l'applicazione delle patch di sicurezza. Il valore di questa esecuzione sta nella garanzia che ogni host disponga della correzione. Sopprimere lo stato non raggiungibile trasforma «un server è ancora vulnerabile» in un riepilogo apparentemente positivo. L'host non raggiungibile da due settimane è quello con maggiori probabilità di essere rimasto molto indietro. Fate terminare l'esecuzione con il codice 4 e lasciate che una persona intervenga.
In entrambi i casi vale una regola: sopprimete l'arresto, mai la registrazione dell'evento. Se un host è stato saltato, qualcosa deve indicarlo: nel riepilogo, nel log CI o in un alert di monitoraggio. Ansible sa che un host esiste soltanto durante i secondi in cui un play viene eseguito su di esso. Per questo è uno strumento poco adatto a rilevare che un server è offline da martedì. Questo compito spetta al monitoraggio, e un playbook Ansible che installa Zabbix consente di ottenere una visione dell'intera flotta in un pomeriggio.
FAQ
Qual è la differenza tra ignore_errors e ignore_unreachable in Ansible?
ignore_errors: true si applica a un task eseguito sull'host che ha restituito un errore, ad esempio perché un comando è terminato con un codice diverso da zero. ignore_unreachable: true si applica a un host a cui Ansible non ha potuto connettersi e su cui non è stato eseguito alcun modulo. I due parametri leggono campi diversi del risultato del task e nessuno dei due copre l'altro caso. La documentazione di Ansible specifica che ignore_errors «non fa sì che Ansible ignori gli errori relativi a variabili non definite, i problemi di connessione, i problemi di esecuzione (ad esempio pacchetti mancanti) o gli errori di sintassi»; una porta SSH chiusa costituisce un problema di connessione.
ignore_unreachable nasconde l'host dal riepilogo dell'esecuzione?
Di fatto, sì. Quando la keyword è impostata, Ansible smette di conteggiare l'host in unreachable e lo conteggia come ok e ignored una volta per ogni task; l'esecuzione termina quindi con codice 0. Le righe fatal: [host]: UNREACHABLE! vengono comunque stampate, quindi il log è accurato anche se il riepilogo e il codice di uscita non lo sono. Controllate la colonna ignored oppure eseguite ansible all -m ansible.builtin.ping -o in un passaggio separato, in modo che un host non raggiungibile produca comunque un codice di uscita diverso da zero.
Quale codice di uscita restituisce ansible-playbook quando un host non è raggiungibile?
Restituisce 4. Un'esecuzione con almeno un host in errore restituisce 2. I due valori sono flag di bit, quindi un'esecuzione con un errore e un host non raggiungibile restituisce 6. Un'esecuzione senza errori restituisce 0. Questi codici sono stati verificati nel codice sorgente di ansible-core ad agosto 2026. L'impostazione di ignore_unreachable: true elimina il valore 4; per questo una pipeline che verifica soltanto il codice di uscita non rileva una macchina ignorata.
Come posso saltare il resto di un play per un host che non ha mai risposto?
Impostate il primo task come ansible.builtin.ping con ignore_unreachable: true e register: reachable, quindi aggiungete ansible.builtin.meta: end_host con la condizione when: reachable.unreachable | default(false). end_host termina il play per quell'host senza contrassegnarlo come non riuscito. Impostate gather_facts: false sul play, in modo che il ping sia il task che rileva la connessione interrotta. Senza questo schema, l'host inattivo rimane nel play e ogni task successivo attende nuovamente la scadenza del timeout di connessione.
Devo ignorare gli host non raggiungibili durante un'esecuzione di patch di sicurezza?
No. Una sessione di patch è utile perché garantisce che ogni host disponga dell'aggiornamento. Ignorare gli host non raggiungibili sostituisce questa garanzia con un riepilogo positivo. Lasciate che l'esecuzione termini con codice 4, individuate i nomi degli host che non hanno risposto e risolvete il problema. La soppressione degli errori è appropriata per esecuzioni opportunistiche ripetute, nelle quali il passaggio successivo recupererà ciò che è stato saltato.