Ansible : ignorer les hôtes inaccessibles
Découvrez pourquoi ignore_errors ne suffit pas face à « UNREACHABLE! », et comment combiner ignore_unreachable, serial et max_fail_percentage.
Un hôte inaccessible n’est pas une tâche en échec
Pour ignorer les hôtes inaccessibles dans Ansible, définissez ignore_unreachable: true. L’option fonctionne. Le point important est de savoir quand l’utiliser, car Ansible traite ces deux problèmes différemment. Une tâche qui s’est exécutée sur l’hôte et a renvoyé une erreur est un échec. Un hôte auquel Ansible n’a pas pu se connecter du tout est inaccessible. ignore_errors couvre uniquement le premier cas. ignore_unreachable couvre uniquement le second.
Voici la différence dans le récapitulatif de l’exécution.
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 s’est connecté à web1 et a exécuté sept tâches. web2 affiche unreachable=1 et failed=0, ce qui signifie qu’aucune tâche ne s’est exécutée sur cet hôte. Ansible n’a jamais pu établir de connexion. Il a donc retiré l’hôte de l’exécution et a poursuivi avec les autres. Si cette exécution installait une mise à jour de sécurité, l’un de vos serveurs ne l’a pas reçue.
Ce qui rend un hôte inaccessible
Un hôte inaccessible signifie que la connexion a échoué avant qu’un module n’atteigne l’hôte. Aucun résultat de module n’est disponible. Seule une erreur de connexion s’affiche, au niveau de la première tâche qui tente d’accéder à la machine.
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}Le champ msg contient la cause réelle. Voici les cas que vous rencontrerez :
Connection refused: la connexion TCP a été refusée. Aucun service n’écoute sur ce port. sshd est arrêté, ou SSH a été déplacé vers un autre port alors que votre inventaire indique toujours 22.Connection timed out: aucune réponse n’a été reçue. Un pare-feu ignore les paquets, ou le serveur est arrêté. Chaque tentative consomme la totalité du délai d’expiration de la connexion, qui est de 10 secondes par défaut.Host key verification failed.: la clé dans~/.ssh/known_hostsne correspond pas à celle présentée par le serveur. Un VPS reconstruit conserve son adresse IP, mais reçoit une nouvelle clé d’hôte. Cette erreur est donc attendue après une réinstallation et doit être prise au sérieux dans les autres cas.Permission denied (publickey): SSH a répondu, mais a refusé votre clé. Le port fonctionne. Le problème concerne donc l’authentification, généralement à cause d’unansible_userincorrect ou d’une clé non chargée.Timeout (12s) waiting for privilege escalation prompt: la connexion a fonctionné, mais pasbecome. sudo attend un mot de passe qui n’arrive jamais.
L’interpréteur Python manquant est souvent cité dans cette liste, mais il n’y a pas sa place. SSH se connecte, donc l’hôte est accessible. Le module ne dispose ensuite d’aucun interpréteur dans lequel s’exécuter :
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}Cette ligne indique que FAILED! et le récapitulatif le compte sous failed. ignore_unreachable ne tentera donc jamais d’y accéder. Définissez ansible_python_interpreter pour cet hôte ou installez python3 sur celui-ci.
Comment ignorer les hôtes injoignables dans un play
Au niveau de la tâche, le mot-clé se place à côté du module :
- 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: trueAu niveau du play, il définit la valeur par défaut pour toutes les tâches du play. Une tâche peut rétablir le comportement par défaut :
- 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: falseIl est important de comprendre ce qui se passe ensuite. Avec ignore_unreachable activé, l’hôte n’est plus retiré du play. Toutes les tâches suivantes tentent donc de se reconnecter et échouent de la même manière. Chaque tentative attend l’expiration du délai de connexion, soit 10 secondes, sauf si vous modifiez timeout dans ansible.cfg. Un play de vingt tâches exécuté sur un serveur hors service ajoute environ 200 secondes à l’exécution et vingt lignes rouges dans le journal.
Testez donc une seule fois, puis arrêtez proprement le traitement de cet hôte :
- 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: trueVous obtenez ainsi une tentative de connexion par hôte hors service, au lieu d’une tentative par tâche. end_host, ajouté dans Ansible 2.8, termine le play pour l’hôte courant sans le marquer comme ayant échoué. La clé unreachable n’existe dans le résultat enregistré que lorsque la connexion a échoué. default(false) conserve donc une condition valide pour chaque hôte qui a répondu. La collecte des facts est désactivée au niveau du play, car la tâche implicite Gathering Facts serait sinon la tâche qui rencontrerait la connexion défaillante. Vous voulez que cette tâche soit votre propre ping.
ignore_unreachable est un mot-clé de play et de tâche. Laissez-le dans le playbook, à un endroit visible pour le lecteur, plutôt qu’à l’intérieur d’un rôle, car il détermine quels hôtes une exécution peut ignorer. La séparation entre les playbooks et les rôles explique quelle couche doit gérer un paramètre de ce type.
Pourquoi ignore_errors est le mauvais outil ici
La documentation Ansible est claire sur cette limite. ignore_errors « ne fonctionne que lorsque la tâche peut s’exécuter et renvoie une valeur égale à 'failed'. Cela ne permet pas à Ansible d’ignorer les erreurs de variable non définie, les échecs de connexion, les problèmes d’exécution (par exemple, les paquets manquants) ou les erreurs de syntaxe. »
Un échec de connexion ne devient jamais un résultat de tâche avec failed: true. Il arrive sous la forme d’un indicateur distinct, qu’Ansible traite en priorité : l’hôte est ajouté à la liste des hôtes injoignables, puis retiré du play. Ajoutez ignore_errors: true aux douze tâches d’un play : un hôte dont le port SSH est fermé s’arrête tout de même à la première tâche. C’est la confusion la plus fréquente dans ce domaine. Recherchez-la dans vos anciens playbooks, en particulier ceux écrits lorsque vous appreniez à écrire un premier playbook pour un VPS.
Déboguez avant de supprimer
Une suppression qui devient permanente entraîne une dérive du parc, car l’hôte que personne ne peut joindre est aussi celui que personne ne corrige. Suivez d’abord cet ordre. Toutes les commandes ci-dessous sont en lecture seule.
ansible web2 -i inventory.ini -m ansible.builtin.ping -oexécute un module sur un seul hôte et affiche une ligne.- Ajoutez
-vvvvà cette même commande. Ansible affiche la commande ssh complète qu’il construit, notamment l’utilisateur cible, le port, la clé privée et les options transmises. - Exécutez vous-même cette commande ssh avec
-v. Si ssh ne peut pas se connecter directement, le problème se situe en dessous d’Ansible et aucun mot-clé de playbook ne le corrigera. - Lisez la chaîne
msget comparez-la à la liste ci-dessus.Connection refusedetConnection timed outdésignent deux emplacements différents : l’un concerne le service SSH, l’autre le chemin réseau. - Pour
Host key verification failed., examinez ce que vous avez enregistré avecssh-keygen -F web2.example.com. Si le serveur a été reconstruit, supprimez l’ancienne entrée avecssh-keygen -R web2.example.com, puis acceptez la nouvelle clé après l’avoir vérifiée dans la console du fournisseur. Définirhost_key_checking = Falsedansansible.cfgsupprime l’erreur, mais désactive aussi le contrôle qui vous signalerait qu’une autre machine répond désormais à cette adresse. - Pour
Permission denied (publickey), vérifiez ce qu’Ansible pense devoir utiliser.ansible-inventory -i inventory.ini --host web2affiche les variables actives, notammentansible_useretansible_port. - Si SSH fonctionne, mais pas les modules, vérifiez l’interpréteur avec
ansible web2 -m ansible.builtin.raw -a 'command -v python3 || echo none'. Le modulerawexécute une commande via le shell et ne nécessite pas Python sur la cible.
Ce n’est qu’après ces vérifications qu’ignorer l’hôte devient une décision plutôt qu’une habitude.
Le récapitulatif comptabilise séparément les hôtes injoignables, ce que la CI ignore généralement
ansible-playbook renvoie 0 en cas de succès, 2 lorsqu’au moins un hôte a échoué et 4 lorsqu’au moins un hôte est injoignable. Ces deux valeurs sont des indicateurs binaires dans le code source. Une exécution avec un hôte en échec et un hôte injoignable renvoie donc 6. La commande ansible renvoie les mêmes codes. Ces valeurs ont été vérifiées dans le code source d’ansible-core en août 2026.
Définissez maintenant ignore_unreachable: true et exécutez le même playbook de sept tâches sur le même hôte hors service :
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 indique unreachable=0 et sept tâches ok, tandis que l’exécution renvoie 0. Lorsque ce mot-clé est défini, Ansible incrémente pour cet hôte les compteurs ok et ignored au lieu du compteur nommé dark, qui est celui alimentant la colonne unreachable. Les lignes rouges UNREACHABLE! sont toujours affichées. Le journal reste donc fidèle, mais le récapitulatif et le code de sortie ne le sont pas.
Un job de CI qui exécute le playbook et vérifie uniquement $? considère cette exécution comme réussie. Rien dans son récapitulatif n’indique qu’une machine n’a jamais été contactée. Faites du contrôle de l’accessibilité une étape distincte, avant le playbook :
ansible all -i inventory.ini -m ansible.builtin.ping -oCette commande affiche une ligne par hôte et renvoie 4 si un hôte est injoignable. Le pipeline dispose ainsi d’une condition d’échec et le journal contient les noms concernés. ping nécessite un interpréteur Python fonctionnel sur la cible. Il vérifie donc davantage que la connexion, ce qui est généralement souhaitable. Exécutez ensuite le playbook avec ignore_unreachable afin que les hôtes accessibles reçoivent malgré tout leur modification.
any_errors_fatal et max_fail_percentage sur un batch
Ces deux mots-clés de play déterminent ce qui se passe lorsqu’une partie du parc rencontre un problème. Ils traitent différemment les hôtes injoignables.
any_errors_fatal: true réagit à un hôte injoignable. Ansible termine la tâche en cours sur le reste du batch, puis arrête le play pour tous les hôtes du batch. Utilisez-le lorsqu’une exécution doit réussir entièrement ou échouer entièrement, par exemple pour une modification coordonnée du schéma.
max_fail_percentage: 30 ne réagit pas à un hôte injoignable. Le contrôle divise le nombre d’hôtes en échec par la taille du batch. Les hôtes injoignables sont conservés dans une liste distincte et ne sont donc jamais inclus dans ce nombre. Dix hôtes dont quatre sont injoignables continuent l’exécution avec max_fail_percentage: 10, tandis que deux hôtes qui échouent sur une tâche arrêtent le play. La documentation signale un autre piège : « Le pourcentage défini doit être dépassé, et non égalé. » Avec serial: 4, pour arrêter l’exécution après deux échecs sur quatre, il faut écrire 49 et non 50.
Il existe un cas où les hôtes injoignables arrêtent seuls l’exécution. Si tous les hôtes du batch sont en échec ou injoignables, Ansible ne peut plus rien exécuter et termine le play avec NO MORE HOSTS LEFT.
serial : déployer une modification progressivement sur le parc
- 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 exécute le play complet sur deux hôtes, attend sa fin, puis passe aux deux suivants. serial: "25%" s’adapte à la taille du groupe. Une liste, serial: [1, 5, 10], définit une stratégie canary : un hôte d’abord, puis cinq, puis dix, les hôtes restants étant exécutés par lots de la dernière taille utilisée. max_fail_percentage est évalué pour chaque lot ; ces deux paramètres fonctionnent donc ensemble. Si la modification casse la première machine, l’exécution s’arrête avant d’en casser quarante. C’est ce qui permet de gérer un parc de serveurs Linux depuis une seule machine de contrôle à l’aide d’une seule commande.
Quand ignorer les hôtes injoignables, et quand ne pas le faire
Ignorez-les pour les tâches opportunistes. Une collecte de facts ou une vérification horaire de la dérive ne perd rien à ignorer un hôte hors ligne, car le passage suivant le prendra en charge. ignore_unreachable: true au niveau du play est la bonne solution dans ce cas. Associez-le à l’étape ping pour que les noms ignorés apparaissent quelque part où une personne les lira.
Ne les ignorez jamais lors d’une exécution de correctifs de sécurité. La valeur de cette exécution repose sur la garantie que chaque hôte possède le correctif. Masquer l’état injoignable transforme « un serveur est toujours vulnérable » en un récapitulatif entièrement vert. L’hôte injoignable depuis deux semaines est probablement celui qui a pris le plus de retard. Faites terminer cette exécution avec le code 4 et demandez à une personne d’examiner le problème.
Une règle s’applique dans les deux cas : supprimez l’arrêt, jamais le signalement. Si un hôte a été ignoré, cela doit apparaître quelque part, dans le récapitulatif, le journal CI ou une alerte de monitoring. Ansible ne sait qu’un hôte existe que pendant les quelques secondes où un play s’exécute contre lui. C’est donc un mauvais outil pour découvrir qu’un serveur est hors ligne depuis mardi. Cette tâche revient au monitoring, et un playbook Ansible qui installe Zabbix permet d’obtenir une vue globale du parc en un après-midi.
FAQ
Quelle est la différence entre ignore_errors et ignore_unreachable dans Ansible ?
ignore_errors: true s’applique à une tâche qui s’est exécutée sur l’hôte et a renvoyé un échec, par exemple lorsqu’une commande se termine avec un code différent de 0. ignore_unreachable: true s’applique à un hôte auquel Ansible n’a pas pu se connecter et sur lequel aucun module n’a donc été exécuté. Ces options lisent des champs différents du résultat de la tâche, et aucune ne couvre l’autre cas. La documentation Ansible précise que ignore_errors « ne fait pas ignorer à Ansible les erreurs de variables non définies, les échecs de connexion, les problèmes d’exécution (par exemple, des paquets manquants) ni les erreurs de syntaxe » ; un port SSH fermé constitue un échec de connexion.
ignore_unreachable masque-t-il l’hôte dans le récapitulatif du play ?
En pratique, oui. Lorsque ce mot-clé est défini, Ansible cesse de comptabiliser cet hôte dans unreachable et le comptabilise dans ok et ignored une fois par tâche ; l’exécution se termine alors avec le code 0. Les lignes fatal: [host]: UNREACHABLE! sont toujours affichées. Le journal reste donc exact, même si le récapitulatif et le code de sortie ne le sont pas. Surveillez la colonne ignored, ou exécutez ansible all -m ansible.builtin.ping -o dans une étape séparée afin qu’un hôte inaccessible produise tout de même un code de sortie différent de 0 à un moment donné.
Quel code de sortie ansible-playbook renvoie-t-il lorsqu’un hôte est inaccessible ?
Il renvoie 4. Une exécution avec au moins un hôte en échec renvoie 2. Ces deux valeurs sont des indicateurs binaires : une exécution comportant à la fois un échec et un hôte inaccessible renvoie donc 6. Une exécution réussie renvoie 0. Ces codes ont été vérifiés dans le code source d’ansible-core en août 2026. La définition de ignore_unreachable: true supprime le 4. C’est pourquoi un pipeline qui teste uniquement le code de sortie ne peut pas détecter une machine ignorée.
Comment ignorer le reste d’un play pour un hôte qui n’a jamais répondu ?
Définissez la première tâche comme ansible.builtin.ping avec ignore_unreachable: true et register: reachable, puis ajoutez ansible.builtin.meta: end_host sous la condition when: reachable.unreachable | default(false). end_host termine le play pour cet hôte sans le marquer comme étant en échec. Définissez gather_facts: false sur le play afin que votre ping soit la tâche qui détecte la connexion défaillante. Sans ce mécanisme, l’hôte hors service reste dans le play et chaque tâche suivante attend de nouveau l’expiration du délai de connexion.
Dois-je ignorer les hôtes inaccessibles pendant l’application d’un correctif de sécurité ?
Non. L’intérêt d’une exécution de correctifs est de garantir que chaque hôte dispose de la mise à jour. Ignorer les hôtes inaccessibles remplace cette garantie par un récapitulatif vert. Laissez l’exécution se terminer avec le code 4, relevez les noms des hôtes qui n’ont pas répondu et corrigez le problème. Cette suppression des erreurs est adaptée aux exécutions opportunistes répétées, lors desquelles le passage suivant traitera les hôtes manqués.