Aller au contenu principal
AnsibleDevOpsCI/CDAutomationInfrastructure as Code

Ansible dans un pipeline CI/CD

30 min de lecture Ansible — Chapitre 8

Dynamic inventory multi-cloud, callbacks et custom modules, et optimisation des performances Ansible avec Mitogen et stratégies parallèles.

Quand ton infrastructure grossit, le fichier hosts statique que tu maintiens à la main devient un cauchemar. Les instances apparaissent et disparaissent dans le cloud, les pipelines CI/CD tournent sans intervention humaine, et tu as besoin qu’Ansible s’adapte en temps réel. Ce chapitre couvre les trois piliers qui transforment Ansible d’un outil de configuration en une vraie machine de production : le dynamic inventory, les callbacks et modules custom, et l’optimisation des performances.

Pourquoi c’est important

Dans un environnement CI/CD, personne ne met à jour un fichier d’inventaire à la main avant chaque déploiement. Tes instances AWS montent et descendent avec l’auto-scaling, tes VMs Azure changent d’IP après chaque redéploiement, et ton pipeline GitHub Actions doit cibler les bons serveurs sans intervention humaine.

Sans dynamic inventory, tu te retrouves avec des playbooks qui échouent parce qu’ils ciblent des machines qui n’existent plus. Sans optimisation, un déploiement sur 100 serveurs prend 15 minutes au lieu de 2. Et sans callbacks, tu n’as aucune visibilité sur ce qui s’est passé quand le pipeline tourne à 3h du matin.

🔥 Cas réel : Une équipe SRE chez un e-commerçant européen a réduit son temps de déploiement de 18 minutes à 3 minutes en combinant dynamic inventory AWS, Mitogen et fact caching. Leur pipeline CI/CD pouvait enfin tenir la cadence des 15 déploiements quotidiens.

Comprendre le Dynamic Inventory

Au lieu de lister tes serveurs dans un fichier statique, tu laisses Ansible interroger directement tes cloud providers. Un plugin d’inventaire dynamique se connecte à l’API AWS, Azure ou GCP, récupère les instances en cours d’exécution, et les organise automatiquement en groupes basés sur les tags, régions ou types d’instance.

Voici un inventaire dynamique AWS qui regroupe automatiquement tes instances par rôle et par région :

# inventory/aws_ec2.yml
---
plugin: amazon.aws.aws_ec2
regions:
  - eu-west-1
  - eu-central-1
filters:
  tag:Environment: production
  instance-state-name: running
keyed_groups:
  - key: tags.Role
    prefix: role
    separator: "_"
  - key: placement.region
    prefix: aws_region
compose:
  ansible_host: private_ip_address
  ansible_user: "'ubuntu'"

💡 Tip DevOps : Tu peux combiner plusieurs fichiers d’inventaire dans un même répertoire (inventory/aws_ec2.yml, inventory/azure_rm.yml, inventory/static_hosts.yml). Ansible les charge tous quand tu passes le dossier avec -i inventory/.

Le principe est le même pour Azure et GCP — seul le plugin et la syntaxe des filtres changent. L’idée centrale reste identique : les tags sur tes instances deviennent tes groupes Ansible. Tag correctement dans le cloud, et ton inventaire se maintient tout seul.

Commandes essentielles

Pour vérifier que ton inventaire dynamique fonctionne, ces commandes sont ton point de départ avant tout playbook :

# Visualiser l'arbre des groupes détectés
ansible-inventory -i inventory/aws_ec2.yml --graph

# Lancer un playbook avec l'inventaire dynamique
ansible-playbook site.yml -i inventory/

# Tester la connectivité sur le groupe "role_webserver"
ansible role_webserver -i inventory/ -m ping

# Profiler l'exécution pour identifier les tâches lentes
ANSIBLE_CALLBACKS_ENABLED=ansible.posix.profile_tasks \
  ansible-playbook site.yml -i inventory/

⚠️ Attention : L’inventaire dynamique fait un appel API à chaque exécution. Sur un gros compte AWS avec des centaines d’instances, ça peut prendre 10-15 secondes. Utilise les filters pour limiter le scope et active le cache d’inventaire si tu lances plusieurs commandes d’affilée.

Cas concret entreprise : pipeline CI/CD optimisé

Imaginons une startup SaaS qui déploie son application sur 60 serveurs EC2 répartis en 3 rôles (web, api, worker). Le pipeline GitHub Actions déclenche Ansible après chaque merge sur main. Voici le ansible.cfg optimisé qui fait toute la différence :

[defaults]
forks = 30
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400
callbacks_enabled = ansible.posix.profile_tasks

[connection]
pipelining = true

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=300s

Chaque ligne compte. forks = 30 traite 30 serveurs en parallèle au lieu de 5 par défaut. pipelining = true réduit les allers-retours SSH en envoyant le module et son exécution en une seule connexion. gathering = smart ne recollecte les facts que s’ils ne sont pas déjà en cache. Et ControlPersist=300s garde les connexions SSH ouvertes pendant 5 minutes pour les réutiliser entre les tâches.

Pour les tâches longues comme les mises à jour système, le mode asynchrone évite de bloquer tout le pipeline :

- name: Mise à jour système (async)
  ansible.builtin.apt:
    upgrade: dist
    update_cache: true
  async: 600
  poll: 0
  register: apt_job

- name: Attendre la fin de la mise à jour
  ansible.builtin.async_status:
    jid: "{{ apt_job.ansible_job_id }}"
  register: job_result
  until: job_result.finished
  retries: 60
  delay: 10

Pour aller encore plus loin, Mitogen remplace le transport SSH d’Ansible de manière transparente et offre des gains de 2x à 7x en vitesse. L’installation est simple — pip install mitogen puis deux lignes dans ansible.cfg :

[defaults]
strategy_plugins = /path/to/mitogen/ansible_mitogen/plugins/strategy
strategy = mitogen_linear

🧠 À retenir : Mitogen ne change rien à tes playbooks. C’est un remplacement du transport SSH, pas de la logique Ansible. Tu l’actives, et tout va plus vite.

Enfin, pour avoir de la visibilité sur les déploiements automatiques, un callback custom envoie un résumé à chaque fin de playbook :

# callback_plugins/deployment_tracker.py
from ansible.plugins.callback import CallbackBase
import requests
from datetime import datetime

class CallbackModule(CallbackBase):
    CALLBACK_VERSION = 2.0
    CALLBACK_TYPE = 'notification'
    CALLBACK_NAME = 'deployment_tracker'
    CALLBACK_NEEDS_ENABLED = True

    def __init__(self):
        super().__init__()
        self.start_time = None
        self.results = {'ok': 0, 'changed': 0, 'failed': 0}

    def v2_playbook_on_start(self, playbook):
        self.start_time = datetime.utcnow()

    def v2_runner_on_ok(self, result):
        key = 'changed' if result.is_changed() else 'ok'
        self.results[key] += 1

    def v2_runner_on_failed(self, result, ignore_errors=False):
        self.results['failed'] += 1

    def v2_playbook_on_stats(self, stats):
        duration = (datetime.utcnow() - self.start_time).total_seconds()
        payload = {
            'duration': duration,
            'results': self.results,
            'status': 'success' if self.results['failed'] == 0 else 'failed'
        }
        requests.post('https://hooks.slack.com/...', json=payload, timeout=10)

🔥 Cas réel : Ce type de callback, combiné avec un canal Slack #deployments, donne à toute l’équipe une visibilité instantanée. Plus besoin d’aller fouiller les logs du pipeline pour savoir si le déploiement de 2h du matin s’est bien passé.

Pièges fréquents

Le pipelining casse si requiretty est activé dans /etc/sudoers sur tes hôtes. Vérifie avec grep requiretty /etc/sudoers et commente la ligne si elle existe. Sans ça, toutes tes tâches avec become: true échoueront silencieusement.

Le fact caching peut te jouer des tours quand tu changes la configuration d’un serveur. Les facts en cache reflètent l’état d’il y a 24h, pas l’état actuel. Si tu ajoutes de la RAM ou changes une IP, pense à purger le cache avec rm -rf /tmp/ansible_facts/ avant de relancer.

L’inventaire dynamique sans filtre sur un gros compte cloud renvoie des centaines d’instances — y compris celles de dev et de staging. Toujours filtrer par tag Environment pour éviter de déployer en prod sur des machines de test (ou l’inverse).

⚠️ Attention : Ne monte jamais forks au-delà de ce que ta machine de contrôle peut encaisser. Chaque fork consomme de la RAM et des file descriptors. Sur un runner CI avec 2 Go de RAM, forks = 50 va probablement OOM kill ton pipeline.

Exercice

Mets en place un inventaire dynamique complet avec optimisation :

  1. Crée un fichier inventory/aws_ec2.yml qui filtre les instances par tag Environment: production et les regroupe par tags.Role
  2. Configure ansible.cfg avec pipelining, fact caching en JSON, et forks = 20
  3. Crée un callback plugin dans callback_plugins/notify.py qui envoie un POST HTTP avec le résumé d’exécution (durée, nombre de tâches ok/changed/failed) vers un webhook de ton choix
  4. Lance ansible-playbook site.yml -i inventory/ et vérifie que le callback se déclenche
  5. Compare le temps d’exécution avec et sans pipelining en utilisant time
# Commandes pour valider ton setup
ansible-inventory -i inventory/ --graph
time ansible-playbook site.yml -i inventory/

À retenir

  • Le dynamic inventory élimine la maintenance manuelle — tes tags cloud deviennent tes groupes Ansible
  • Le pipelining et le SSH multiplexing réduisent les allers-retours réseau de 30 à 50%
  • Mitogen accélère Ansible de 2x à 7x sans modifier tes playbooks
  • Les callbacks custom donnent de la visibilité sur les déploiements automatiques
  • Monte les forks en fonction de ta cible (20-50 pour la plupart des setups) mais surveille la RAM
  • Le fact caching économise du temps mais attention aux données obsolètes — purge quand tu modifies l’infra
  • En CI/CD, combine tout : dynamic inventory + pipelining + Mitogen + callbacks = pipeline de déploiement rapide et observable

💡 Tip DevOps : Commence par activer le pipelining et monter les forks — c’est gratuit et immédiat. Mitogen vient ensuite pour les gros gains. Le dynamic inventory, lui, devient indispensable dès que tu dépasses 10 serveurs ou que tu fais de l’auto-scaling.

🖥️ Pratique sur ton propre serveur

Pour suivre Ansible en conditions réelles, tu as besoin d'un VPS. DigitalOcean offre 200$ de crédit gratuit pour démarrer.

Obtenir 200$

Articles liés