Aller au contenu
Infrastructure as Code medium

any_errors_fatal Ansible : arrêter le play sur 1ère erreur cluster

70 min de lecture

Logo Ansible

Par défaut, si une tâche échoue sur un hôte mais réussit sur les autres, Ansible continue le play sur les hôtes restants. Avec any_errors_fatal: true au niveau du play, dès qu'une tâche échoue sur n'importe quel hôte, le play s'arrête net pour tous les hôtes. C'est l'inverse philosophique d'ignore_errors:, ici on veut une opération atomique sur le cluster : soit toutes les machines sont OK, soit on arrête tout.

C'est l'outil pour les rolling updates stricts ou les migrations cluster où une moitié-réussite est pire qu'un échec total.

  • L'effet de any_errors_fatal: true au niveau du play
  • La différence avec max_fail_percentage: 0
  • Cas d'usage : rolling update strict, migration de schéma, opération all-or-nothing
  • Combinaison avec serial:
- name: Migration cluster atomique
hosts: webservers
any_errors_fatal: true
tasks:
- name: Tâche critique
ansible.builtin.command: /usr/local/bin/migrate.sh
changed_when: true

Si migrate.sh échoue sur n'importe quel webserver, le play s'arrête immédiatement, même les hôtes qui ont réussi voient leurs tâches suivantes non exécutées.

Les deux directives sont liées mais subtilement différentes :

DirectiveEffet
any_errors_fatal: trueArrête le play dès la 1ère erreur sur n'importe quel hôte
max_fail_percentage: 0Arrête le play si plus de 0% des hôtes du batch courant ont échoué (= dès la 1ère erreur du batch, pour le batch courant)
max_fail_percentage: 30Arrête si plus de 30% du batch a échoué

Avec serial:, la nuance compte : max_fail_percentage: 0 arrête seulement le batch courant, any_errors_fatal: true arrête tout immédiatement.

# Rolling update : arrêter dès qu'un hôte échoue, pas continuer sur les autres
- name: Déploiement cluster strict
hosts: webservers
serial: 1
any_errors_fatal: true
tasks:
- name: Déployer la version cible de l'application
ansible.builtin.dnf:
name: "app-{{ app_version }}"
state: present

Sur 5 webservers en serial: 1, si web2 échoue, web3, web4, web5 ne sont pas déployés (au lieu de l'être avec un comportement par défaut).

- name: Migrer le schéma sur tous les replica
hosts: dbservers
any_errors_fatal: true
tasks:
- name: Lancer la migration
ansible.builtin.command: /usr/local/bin/migrate-schema.sh
changed_when: true

Si la migration échoue sur un seul replica, vous voulez arrêter immédiatement plutôt que continuer et créer des replica désynchronisés.

- name: Renouveler les certificats sur tout le cluster
hosts: all
any_errors_fatal: true
tasks:
- name: Lancer certbot
ansible.builtin.command: /usr/bin/certbot renew --no-self-upgrade
changed_when: true

Si un nœud échoue, on arrête plutôt que de partager des certificats expirant à des moments différents.

- name: Arrêter un service partout en même temps (maintenance)
hosts: webservers
any_errors_fatal: true
tasks:
- name: Stopper le service
ansible.builtin.systemd:
name: app
state: stopped

Avant une maintenance globale, vous voulez soit tout arrêter, soit rien, un état partiel laisse des hôtes inutilisables tandis que d'autres tournent.

Voici l'exemple validé sur le lab ecrire-code-any-errors-fatal :

- name: Challenge any errors fatal
hosts: webservers
become: true
any_errors_fatal: true
tasks:
- name: Marqueur sans erreur
ansible.builtin.copy:
dest: "/tmp/anyfatal-{{ inventory_hostname }}.txt"
content: "any_errors_fatal OK\n"
mode: "0644"

Sans erreur réelle, le play tourne normalement et pose 2 fichiers (web1 + web2). Pour prouver que any_errors_fatal: true arrête tout, il faudrait injecter une erreur, ce qui casserait le test (le play s'arrête en failed).

Le comportement réel se vérifie en injectant volontairement un échec : on observe alors que tous les webservers ont leurs tâches suivantes non exécutées.

any_errors_fatal: est une directive de play par défaut. Depuis Ansible 2.7, on peut aussi la poser au niveau block :

- name: Grouper les tâches liées
block:
- name: Tâche critique 1
...
- name: Tâche critique 2
...
any_errors_fatal: true

Effet : l'échec d'une tâche du block sur n'importe quel hôte arrête le play. Hors du block, le comportement par défaut reprend.

SymptômeCauseFix
any_errors_fatal: true au niveau taskPas supportéLe mettre au niveau play ou block
Play s'arrête trop vite avec une erreur "secondaire"C'est le comportement attenduConsidérer max_fail_percentage: plus permissif si la tolérance est OK
Combiné avec ignore_errors: trueignore_errors: neutralise l'effetChoisir l'un ou l'autre
Combiné avec block/rescueLe rescue peut "capturer" et neutraliser any_errors_fatalBien réfléchir à l'interaction
serial: 1 + any_errors_fatal: true mais le play continue après échecVérifier qu'il n'y a pas un block/rescue qui captureLogiquement, any_errors_fatal doit gagner sauf rescue
  • any_errors_fatal: true au niveau play : arrête le play dès la 1ère erreur sur n'importe quel hôte.
  • Différent de max_fail_percentage: 0 qui agit sur le batch courant (avec serial:).
  • Cas d'usage : migration cluster atomique, renouvellement de certificats, arrêt synchrone de service.
  • Combiné avec serial: strict : un seul échec arrête tout le rolling update.
  • Disponible aussi au niveau block depuis Ansible 2.7.

Un arrêt immédiat se juge sur un play réel, pas sur un extrait. Le lab vous fait activer any_errors_fatal: true sur un play multi-hôtes, comparer le résultat au comportement par défaut puis à max_fail_percentage, et observer ce que donne la combinaison avec serial:. La validation porte sur l'état des hôtes à la fin du play, donc sur la propagation réelle de l'échec plutôt que sur la syntaxe écrite.

  • Quiz Écrire du code : 30 questions pour valider tout le bloc « Écrire du code » avant d'attaquer les inventaires.
  • Inventaires statiques : any_errors_fatal raisonne sur un ensemble d'hôtes, et l'inventaire est ce qui le définit.
  • Patterns d'hôtes : réduire le périmètre d'un play avec --limit avant de le rendre bloquant à la moindre erreur.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn