Maîtrise des déploiements multi-étapes avec des playbooks Ansible séquentiels
Apprenez à concevoir et exécuter des déploiements d'applications complexes et multi-étapes avec Ansible. Ce guide couvre la création de playbooks séquentiels pour des phases de déploiement distinctes, la mise en œuvre d'une gestion efficace des erreurs et le développement de stratégies de rollback. Maîtrisez l'art de la livraison automatisée et robuste d'applications avec des exemples pratiques et des bonnes pratiques.
Maîtrise des déploiements multi-étapes avec des playbooks Ansible séquentiels
Les déploiements Ansible multi-étapes deviennent nécessaires lorsque "copier les fichiers et redémarrer le service" n'est plus honnête. Un vrai déploiement peut nécessiter une migration de base de données, un changement de feature flag, un déploiement de package, un rechargement de service, un health check et un chemin de rollback si la nouvelle version échoue. Si tout cela vit dans un seul grand playbook sans limites claires, chaque déploiement échoué se transforme en exercice de lecture.
Les playbooks séquentiels donnent à chaque étape un travail clair. Vous pouvez les exécuter depuis un pipeline CI/CD, AWX, Ansible Automation Platform ou un simple script shell. La partie importante n'est pas l'outil qui appuie sur le bouton. La partie importante est que le déploiement a un ordre, que chaque étape peut être relancée en toute sécurité et que la gestion des échecs est explicite.
Pourquoi des playbooks séquentiels pour les déploiements multi-étapes ?
Déployer une application implique souvent plus que simplement copier des fichiers. Vous pourriez avoir besoin de :
- Préparer l'environnement : Créer des répertoires, définir des permissions, installer des dépendances.
- Mettre à jour la base de données : Exécuter des migrations de schéma, ensemencer des données initiales.
- Déployer le code de l'application : Transférer de nouvelles versions de code, redémarrer les services.
- Configurer les services : Mettre à jour les configurations d'application, recharger les démons.
- Effectuer des vérifications post-déploiement : Exécuter des tests de fumée, vérifier la disponibilité des services.
La séquence est importante car certaines opérations sont faciles à annuler et d'autres non. Revenir à un lien symbolique vers la version précédente est généralement simple. Annuler une migration destructive de base de données peut ne pas l'être. Cette différence devrait façonner le plan de déploiement avant que quiconque n'écrive du YAML.
Diviser ces étapes en playbooks séquentiels distincts offre plusieurs avantages :
- Modularité : Chaque playbook se concentre sur une seule étape, ce qui les rend plus faciles à comprendre, maintenir et réutiliser.
- Lisibilité : La logique complexe est divisée en morceaux gérables.
- Contrôle : Vous pouvez exécuter des étapes spécifiques de manière indépendante ou dans le cadre d'un flux de travail plus large.
- Isolation des erreurs : Si une défaillance se produit dans une étape, il est plus facile d'en identifier la cause et de revenir sur des modifications spécifiques sans affecter d'autres parties du déploiement.
- Idempotence : Des playbooks bien écrits sont intrinsèquement idempotents, ce qui signifie que les exécuter plusieurs fois a le même effet que les exécuter une seule fois. Ceci est crucial pour des relances sûres.
Il y a un compromis. Des playbooks séparés ajoutent du travail d'orchestration. Les variables, les artefacts et l'état peuvent devoir passer d'une étape à l'autre. Pour un petit service interne, un playbook avec des blocs tagués peut suffire. Pour une application orientée client avec des migrations et des exigences de rollback, la structure supplémentaire est généralement rentable.
Concevoir votre flux de travail de déploiement multi-étapes
Avant d'écrire du code Ansible, planifiez vos étapes de déploiement. Identifiez les étapes logiques, leurs dépendances et l'ordre d'exécution. Un flux de travail courant pourrait ressembler à ceci :
- Vérifications pré-déploiement : Assurez-vous que l'environnement cible est prêt.
- Migration de base de données : Appliquez les modifications nécessaires du schéma de base de données.
- Déploiement de l'application : Déployez la nouvelle version du code de l'application.
- Redémarrage/Rechargement du service : Mettez les services de l'application en ligne avec le nouveau code.
- Vérification post-déploiement : Exécutez des tests pour confirmer le succès du déploiement.
Pour chaque étape, déterminez les tâches Ansible requises et quel playbook les contiendra.
Décidez également quelles étapes sont autorisées à modifier l'état de production. Un playbook de test de fumée ne devrait pas réparer silencieusement la configuration. Un playbook de pré-vol ne devrait pas installer de packages manquants, sauf si cela fait explicitement partie du contrat de déploiement. Garder les vérifications en lecture seule séparées des étapes de mutation rend le flux de travail plus fiable.
Voici une structure de répertoires pratique :
deploy/
inventories/
staging.ini
production.ini
group_vars/
all.yml
production.yml
playbooks/
00-preflight.yml
01-migrate-db.yml
02-deploy-app.yml
03-reload-services.yml
04-smoke-test.yml
rollback-app.yml
Les chiffres ne sont pas magiques. Ils rendent simplement l'ordre visible dans les listes de fichiers et les logs CI.
Exécuter les playbooks de manière séquentielle
Ansible fournit un moyen simple d'exécuter des playbooks les uns après les autres en utilisant les commandes --playbook-dir et ansible-playbook. La méthode la plus simple est d'enchaîner les commandes dans votre pipeline CI/CD ou en ligne de commande.
Supposons que vous ayez les fichiers de playbook suivants :
01-database-migration.yml02-deploy-application.yml03-restart-services.yml04-smoke-tests.yml
Vous pouvez les exécuter séquentiellement comme ceci :
ansible-playbook -i inventory.ini 01-database-migration.yml
ansible-playbook -i inventory.ini 02-deploy-application.yml
ansible-playbook -i inventory.ini 03-restart-services.yml
ansible-playbook -i inventory.ini 04-smoke-tests.yml
En pratique, enveloppez cette séquence pour qu'une étape échouée arrête le pipeline :
set -euo pipefail
ansible-playbook -i inventories/production.ini playbooks/00-preflight.yml
ansible-playbook -i inventories/production.ini playbooks/01-migrate-db.yml
ansible-playbook -i inventories/production.ini playbooks/02-deploy-app.yml
ansible-playbook -i inventories/production.ini playbooks/03-reload-services.yml
ansible-playbook -i inventories/production.ini playbooks/04-smoke-test.yml
set -e n'est pas une stratégie de déploiement en soi, mais cela évite la pire erreur : continuer après une étape échouée comme si de rien n'était. Les systèmes CI fournissent généralement leur propre comportement en cas d'échec, mais la même idée s'applique.
Utiliser ansible-playbook --skip-tags ou --limit
Dans des scénarios plus avancés, vous pouvez combiner plusieurs étapes logiques en un seul playbook mais utiliser des tags pour contrôler l'exécution. Cependant, pour une véritable séparation multi-étapes, des playbooks distincts sont généralement préférés. Si vous souhaitez exécuter un sous-ensemble de playbooks ou en sauter certains, vous pouvez utiliser des arguments en ligne de commande.
Sauter un playbook : Si 03-restart-services.yml échoue en raison d'un problème de service temporaire, vous pouvez réexécuter uniquement cette étape après avoir corrigé la cause. Ne sautez pas les étapes aveuglément lorsque les étapes précédentes produisent des artefacts ou un état dont dépendent les étapes ultérieures.
Limiter à une étape spécifique : Vous pouvez également limiter l'exécution à un hôte ou un groupe spécifique en utilisant le drapeau --limit, ce qui peut être utile pour les tests.
Pour les déploiements progressifs, --limit peut également réduire le rayon d'impact :
ansible-playbook -i inventories/production.ini playbooks/02-deploy-app.yml --limit web_canary
Exécutez le déploiement sur un hôte ou un petit groupe, vérifiez-le, puis continuez vers le reste du parc. Ceci est particulièrement utile lorsque votre équilibreur de charge prend en charge le drainage des hôtes avant le rechargement ou le redémarrage.
Intégrer la gestion des erreurs et les stratégies de rollback
Des déploiements robustes nécessitent un plan pour quand les choses tournent mal.
ignore_errors et failed_when
Par défaut, Ansible arrête l'exécution si une tâche échoue. Vous pouvez contrôler ce comportement :
ignore_errors: true: Permet au playbook de continuer même si une tâche échoue. Utilisez-le avec prudence, généralement pour des tâches non critiques ou lorsque vous avez une tâche ultérieure pour nettoyer ou compenser.failed_when:: Définissez des conditions personnalisées sous lesquelles une tâche doit être considérée comme ayant échoué. C'est puissant pour gérer les erreurs non fatales attendues ou valider des résultats spécifiques.
- name: Vérifier l'état du service (potentiellement non fatal)
command: systemctl status myapp
register: service_status
ignore_errors: true
- name: Échouer si le service n'est pas actif
fail:
msg: "Le service myapp ne tourne pas !"
when: "service_status.rc != 0"
Utilisez ignore_errors avec parcimonie. Il est souvent préférable d'enregistrer le résultat et de prendre une décision claire. Un journal de déploiement rempli d'échecs ignorés apprend aux gens à arrêter de lire les échecs.
Pour les commandes, préférez les modules spécialisés lorsqu'ils existent. Par exemple, utilisez ansible.builtin.service, ansible.builtin.systemd, ansible.builtin.copy, ansible.builtin.template et les modules de package plutôt que d'utiliser shell. Les modules offrent généralement une meilleure idempotence et des états de changement et d'échec plus clairs.
Playbooks de rollback
Pour les déploiements critiques, ayez des playbooks de rollback dédiés. Ces playbooks doivent être conçus pour annuler les modifications apportées par leurs playbooks de déploiement correspondants.
01-database-migration-rollback.yml: Annule les modifications de schéma.02-deploy-application-rollback.yml: Déploie la version précédente de l'application ou restaure une sauvegarde.03-restart-services-rollback.yml: Redémarre les services dans leur état précédent.
Le rollback de base de données mérite une attention particulière. Certaines migrations ne peuvent pas être annulées en toute sécurité après que des écritures commencent à utiliser le nouveau schéma. Un modèle plus sûr est souvent l'expansion-contraction : ajoutez des modifications de schéma rétrocompatibles, déployez le code d'application qui peut fonctionner avec les anciennes et nouvelles formes, remplissez les données si nécessaire, puis supprimez les anciennes colonnes ou champs dans un déploiement ultérieur.
Avec ce modèle, le rollback signifie généralement annuler le code de l'application et laisser le schéma compatible en place, plutôt que d'essayer d'annuler une modification risquée de la base de données sous pression.
Exemple de déclencheur de rollback : Dans votre pipeline CI/CD, si le playbook 04-smoke-tests.yml échoue, vous déclenchez l'exécution des playbooks de rollback dans l'ordre inverse.
# Si 04-smoke-tests.yml échoue :
ansible-playbook -i inventory.ini 03-restart-services-rollback.yml
ansible-playbook -i inventory.ini 02-deploy-application-rollback.yml
ansible-playbook -i inventory.ini 01-database-migration-rollback.yml
Utiliser block, rescue et always
Les constructions block, rescue et always d'Ansible fournissent un moyen plus structuré de gérer les erreurs au sein d'un seul playbook. Bien qu'elles ne soient pas destinées à la séquence entre les playbooks, elles sont excellentes pour encapsuler une série de tâches qui pourraient échouer et définir quoi faire en cas d'échec.
- block:
- name: Déployer le nouveau code de l'application
copy:
src: /path/to/new/app/
dest: /var/www/myapp/
- name: Redémarrer le service d'application
service:
name: myapp
state: restarted
rescue:
- name: Tenter de revenir à la version précédente
copy:
src: /path/to/old/app/
dest: /var/www/myapp/
- name: Redémarrer le service d'application après rollback
service:
name: myapp
state: restarted
always:
- name: Journaliser la tentative de déploiement
debug:
msg: "Tentative de déploiement terminée."
Cette approche est utile pour regrouper des tâches connexes au sein d'un seul playbook d'étape de déploiement.
Pour le rollback entre playbooks, laissez l'orchestrateur prendre la décision. Un pipeline CI peut exécuter des playbooks de rollback uniquement si une étape ultérieure échoue. Les workflows de tâches AWX peuvent modéliser visuellement les mêmes branches de succès et d'échec. Gardez la commande de rollback ennuyeuse et répétée.
Transmettre l'état de la version entre les étapes
Les playbooks séquentiels ont souvent besoin d'un identifiant de version partagé. Par exemple, l'étape de déploiement doit savoir quel artefact installer, le test de fumée doit savoir à quelle version s'attendre, et le rollback doit connaître la version précédente.
Transmettez cet état explicitement :
ansible-playbook -i inventories/production.ini playbooks/02-deploy-app.yml \
-e release_version=2026.05.24.3 \
-e artifact_url=https://artifacts.example.com/myapp/2026.05.24.3.tar.gz
À l'intérieur du playbook, enregistrez ce qui a changé :
- name: Écrire le marqueur de version actuelle
ansible.builtin.copy:
dest: /opt/myapp/current-release.txt
content: "{{ release_version }}\n"
owner: root
group: root
mode: "0644"
Ce marqueur aide lors des incidents. Lorsque quelqu'un se connecte en SSH à un hôte, il peut voir quelle version l'hôte pense exécuter. Vous pouvez également faire en sorte que le playbook de test de fumée lise le marqueur et le compare à la version attendue.
Considérations avancées
Gérer l'état entre les playbooks
Parfois, une tâche dans un playbook doit informer un autre playbook de son résultat. Vous pouvez y parvenir en utilisant :
- Mise en cache des faits : Si la mise en cache des faits est activée, les faits recueillis par un playbook peuvent être disponibles pour les playbooks suivants exécutés dans la même session Ansible.
- Fichiers/Bases de données temporaires : Écrivez des informations d'état critiques ou des sorties dans un fichier temporaire ou une table d'état dédiée que les playbooks suivants peuvent lire.
Préférez l'état explicite à l'état caché. La mise en cache des faits peut être utile, mais elle peut aussi dérouter les gens lorsque les valeurs sont obsolètes ou lorsqu'un exécuteur a le cache activé et un autre non. Les fichiers de version, les métadonnées d'artefact, les variables CI et les enregistrements de déploiement sont plus faciles à inspecter.
Contrôle de version et outils d'orchestration
Pour des orchestrations complexes, envisagez d'intégrer vos playbooks Ansible séquentiels dans un outil de niveau supérieur :
- Pipelines CI/CD : Des outils comme Jenkins, GitLab CI, GitHub Actions ou CircleCI sont excellents pour définir et déclencher des déploiements multi-étapes. Vous définissez la séquence de commandes
ansible-playbookdans la configuration du pipeline. - Ansible Tower/AWX : Pour une orchestration de niveau entreprise, Ansible Tower (maintenant Automation Platform) ou son homologue open-source AWX fournit une interface utilisateur robuste pour planifier, surveiller et gérer des modèles de tâches complexes qui peuvent enchaîner plusieurs playbooks.
Si plusieurs personnes déploient le même système, l'orchestration centralisée devient moins une question de commodité que de contrôle. Elle vous donne des inventaires, des identifiants, des journaux d'audit, des approbations et un historique visible de l'étape qui a échoué. Ces détails comptent lors d'un incident de production.
Tagger pour un contrôle granulaire
Bien que nous préconisions des playbooks séparés pour des étapes distinctes, vous pouvez également utiliser des tags dans les playbooks. Si vous avez un très grand playbook pour une seule étape (par exemple, une migration de base de données), vous pouvez taguer des tâches spécifiques et exécuter uniquement celles-ci en utilisant ansible-playbook --tags <nom_du_tag>.
Cela concerne davantage le contrôle granulaire au sein d'une étape que la séquence entre les étapes.
Bonnes pratiques pour les déploiements multi-étapes
- Gardez les playbooks ciblés : Chaque playbook doit faire une chose correctement (par exemple, migration de base de données, déploiement d'application).
- Nommez les playbooks clairement : Utilisez une convention de nommage qui reflète l'étape et l'ordre (par exemple,
01-,02-). - Implémentez l'idempotence : Assurez-vous que toutes les tâches sont idempotentes pour permettre des relances sûres.
- Testez les rollbacks : Testez régulièrement vos procédures de rollback pour vous assurer qu'elles fonctionnent comme prévu.
- Utilisez le contrôle de version : Stockez tous vos playbooks et fichiers d'inventaire dans un système de contrôle de version (comme Git).
- Automatisez l'orchestration : Utilisez des pipelines CI/CD ou des outils comme Ansible Tower/AWX pour automatiser l'exécution de vos playbooks séquentiels.
- Documentez votre flux de travail : Documentez clairement les étapes, leur objectif, leurs dépendances et les procédures de rollback.
- Rendez les tests de fumée réels : Vérifiez le point de terminaison réel, le chemin de connexion, le worker de file d'attente ou le travail en arrière-plan qui compte. Une simple vérification de processus ne suffit pas.
- Protégez les inventaires de production : Utilisez des inventaires et des identifiants séparés pour la préproduction et la production. Une faute de frappe dans
--limitne doit pas déployer au mauvais endroit. - Utilisez un déploiement en série lorsque c'est possible :
serialvous permet de mettre à jour quelques hôtes à la fois et de vous arrêter avant que l'ensemble du parc ne soit affecté.
- name: Déployer l'application progressivement
hosts: web
serial: 2
tasks:
- name: Installer la version
ansible.builtin.unarchive:
src: "{{ artifact_path }}"
dest: /opt/myapp/releases/{{ release_version }}
remote_src: true
Avec serial, Ansible traite les hôtes par lots. Combinez-le avec le drainage de l'équilibreur de charge si votre application ne peut pas être redémarrée sans abandonner les requêtes actives.
Un flux de déploiement concret
Un déploiement Ansible sûr pour une application web pourrait ressembler à ceci :
00-preflight.yml vérifie l'espace disque, confirme que la version cible existe, vérifie la connectivité de la base de données et s'assure que les hôtes sont dans l'environnement attendu. Il ne modifie pas le système.
01-migrate-db.yml exécute uniquement des migrations rétrocompatibles. Il enregistre la version de la migration et échoue si la base de données est déjà en avance sur la version demandée.
02-deploy-app.yml télécharge l'artefact, le décompresse dans un répertoire de version numéroté, modélise la configuration et met à jour un lien symbolique current. Il ne redémarre pas encore les services.
03-reload-services.yml draine chaque hôte de l'équilibreur de charge, recharge ou redémarre le service, attend le point de terminaison de santé local, puis remet l'hôte en service.
04-smoke-test.yml appelle le point de terminaison public via le même chemin que les utilisateurs. Il vérifie le corps de la réponse ou le point de terminaison de version, pas seulement un 200 d'une page par défaut de l'équilibreur de charge.
Ce flux est plus lent qu'un redémarrage en une seule commande. Il est aussi beaucoup plus facile à raisonner lorsque le déploiement échoue à mi-chemin.
L'habitude qui fait fonctionner cela
Les playbooks Ansible séquentiels fonctionnent mieux lorsque chacun a un contrat étroit : ce qu'il attend, ce qu'il modifie, comment il prouve le succès et quoi faire s'il échoue. Ce contrat compte plus que le nombre de fichiers YAML.
Commencez par les étapes qui reflètent votre risque réel : pré-vol, migration, déploiement, rechargement, test de fumée, rollback. Gardez les commandes ennuyeuses. Testez le rollback avant d'en avoir besoin. Lorsqu'un déploiement se casse, vous devriez être en mesure de pointer vers l'étape exacte qui a échoué et de décider de la prochaine étape sans relire tout l'arbre d'automatisation.