Le comportement par défaut, et ses limites
Par défaut, une tâche qui échoue sur un hôte retire cet hôte du reste du playbook. Les autres continuent.
Ce comportement est raisonnable, et il produit une situation inconfortable : un parc à moitié configuré, dont l'état exact n'est plus connu. C'est pire qu'un échec net, parce que personne ne sait où reprendre.
Les mécanismes qui suivent servent à choisir ce qui arrive, plutôt qu'à le subir.
Décider ce qui compte comme un échec
- name: Vérifier la présence du service
ansible.builtin.command: systemctl is-active monservice
register: etat
failed_when: false
changed_when: false
- name: Alerter si le service est arrêté
ansible.builtin.debug:
msg: "Service arrêté sur {{ inventory_hostname }}"
when: etat.rc != 0
failed_when: false dit qu'un code de retour non nul n'est pas une erreur ici. changed_when: false dit que cette tâche ne modifie rien : sans elle, une simple vérification apparaît comme un changement, et le rapport devient illisible.
ignore_errors: true existe aussi, et c'est le plus mauvais des trois : il masque l'erreur sans la qualifier. Réservez-le au dépannage.
Arrêter tout le monde quand c'est nécessaire
- hosts: serveurs_web
max_fail_percentage: 20
serial: 5
serial: 5 traite les machines par lots de cinq. max_fail_percentage: 20 interrompt le playbook si plus d'un cinquième des machines du lot échoue.
L'intérêt est direct : une mise à jour qui casse s'arrête après cinq machines au lieu de cinquante. C'est la protection la plus rentable d'un playbook de production, et elle tient en deux lignes.
Pour un arrêt immédiat sur tout le parc dès la première défaillance : any_errors_fatal: true.
Nettoyer après un échec
- block:
- name: Basculer en maintenance
ansible.builtin.command: /opt/app/maintenance on
- name: Déployer
ansible.builtin.command: /opt/app/deploy.sh
rescue:
- name: Revenir à la version précédente
ansible.builtin.command: /opt/app/rollback.sh
always:
- name: Sortir de maintenance
ansible.builtin.command: /opt/app/maintenance off
rescue s'exécute en cas d'échec, always dans tous les cas. Le bloc always est ce qui évite de laisser un parc en mode maintenance parce qu'une tâche a échoué au milieu.
Reprendre après un échec partiel
Ansible écrit un fichier listant les hôtes en échec. Il permet de rejouer uniquement sur eux :
ansible-playbook site.yml --limit @/home/ops/site.retry
C'est utile, et cela suppose l'idempotence de la leçon précédente : sans elle, rejouer sur des machines partiellement traitées est risqué.
Les gestionnaires, un piège fréquent
Un gestionnaire déclenché par une tâche ne s'exécute qu'à la fin du playbook. Si une tâche ultérieure échoue, le redémarrage prévu n'a pas lieu : la configuration est en place, le service tourne encore avec l'ancienne.
force_handlers: true impose leur exécution même en cas d'échec. À poser sur tout playbook qui modifie une configuration de service.
À retenir
Qualifiez les erreurs avec failed_when plutôt que de les ignorer. serial et max_fail_percentage évitent de casser tout le parc. block/rescue/always garantit le nettoyage, et force_handlers évite le service laissé sur l'ancienne configuration.