RousselTMLEARNING

Automatisation

Ansible : gérer son infrastructure avec des playbooks (Infrastructure as Code)

Écrire des playbooks de production : structure d'un projet, plays, handlers, tags, strategy serial, blocs de gestion d'erreurs, déploiement progressif et orchestration multi-tiers.

1. Organiser un projet Ansible #

De quelques fichiers à un dépôt industrialisé.

Structure de référence #

projet-infra/
├── ansible.cfg
├── requirements.yml
├── site.yml                 # point d'entrée : importe les playbooks
├── playbooks/
│   ├── loadbalancers.yml
│   ├── webservers.yml
│   └── dbservers.yml
├── inventories/
│   ├── lab/    {hosts.ini, group_vars/, host_vars/}
│   └── prod/   {hosts.ini, group_vars/, host_vars/}
└── roles/
    ├── common/  nginx/  haproxy/  postgresql/
# site.yml
- import_playbook: playbooks/dbservers.yml
- import_playbook: playbooks/webservers.yml
- import_playbook: playbooks/loadbalancers.yml
Orchestration multi-tiers avec Ansible
L'ordre des plays reflète les dépendances : base, applications, puis load balancer.

2. Un playbook de production #

Variables, handlers, tags et conditions.

Exemple complet #

# playbooks/webservers.yml
- name: Configurer les serveurs web
  hosts: webservers
  become: true
  gather_facts: true
  vars:
    app_version: "1.4.2"
  roles:
    - role: common
      tags: [common]
    - role: nginx
      tags: [nginx]

  pre_tasks:
    - name: Retirer le serveur du load balancer
      ansible.builtin.uri:
        url: "http://lb.lab:8404/disable/{{ inventory_hostname }}"
        method: POST
      delegate_to: localhost

  tasks:
    - name: Déployer l'application
      ansible.builtin.unarchive:
        src: "https://artifacts.lab/app-{{ app_version }}.tgz"
        dest: /opt/app
        remote_src: true
      notify: Redémarrer l'application
      tags: [deploy]

  post_tasks:
    - name: Vérifier que l'application répond
      ansible.builtin.uri:
        url: "http://localhost:8080/health"
        status_code: 200
      retries: 10
      delay: 3
      register: health
      until: health.status == 200

  handlers:
    - name: Redémarrer l'application
      ansible.builtin.systemd:
        name: app
        state: restarted

Exécuter, simuler, cibler #

ansible-playbook site.yml --syntax-check
ansible-playbook site.yml --check --diff               # dry-run + différences
ansible-playbook site.yml -i inventories/prod --limit web1.lab
ansible-playbook site.yml --tags deploy
ansible-playbook site.yml --skip-tags common
ansible-playbook site.yml --start-at-task "Déployer l'application"
ansible-playbook site.yml -vvv                           # débogage détaillé
ansible-playbook site.yml --list-hosts --list-tasks

3. Déploiements progressifs et résilients #

serial, blocs et gestion des erreurs.

Rolling update avec serial #

- hosts: webservers
  serial: "25%"                 # ou 2, ou [1, 5, 10] pour des vagues croissantes
  max_fail_percentage: 10       # arrêter si plus de 10 % des hôtes échouent
  tasks:
    - name: Mettre à jour l'application
      ansible.builtin.package: { name: app, state: latest }
Bonne Pratique : Avec serial, les vagues se déroulent l'une après l'autre : une erreur sur la première vague empêche de propager un mauvais déploiement à toute la production.

block / rescue / always #

- name: Déploiement avec retour arrière
  block:
    - name: Appliquer la nouvelle version
      ansible.builtin.command: /opt/app/deploy.sh {{ app_version }}
    - name: Smoke test
      ansible.builtin.uri: { url: "http://localhost:8080/health" }
  rescue:
    - name: Restaurer la version précédente
      ansible.builtin.command: /opt/app/rollback.sh
    - name: Notifier l'équipe
      community.general.slack: { token: "{{ vault_slack_token }}", msg: "Déploiement échoué sur {{ inventory_hostname }}" }
      delegate_to: localhost
  always:
    - name: Journaliser l'opération
      ansible.builtin.lineinfile: { path: /var/log/deploys.log, line: "{{ ansible_date_time.iso8601 }} {{ app_version }}", create: true }

Délégation et exécution unique #

- name: Migrer le schéma (une seule fois pour tout le groupe)
  ansible.builtin.command: /opt/app/migrate.sh
  run_once: true
  delegate_to: "{{ groups['dbservers'][0] }}"

- name: Enregistrer le déploiement dans le CMDB
  ansible.builtin.uri: { url: "https://cmdb.lab/api/deployments", method: POST, body_format: json, body: { host: "{{ inventory_hostname }}" } }
  delegate_to: localhost
Erreur Courante : command et shell ne sont pas idempotents : ajoutez creates:, removes: ou changed_when: pour que le playbook reste rejouable.

Articles liés

Ansible : l'inventaire (statique, dynamique, groupes et variables)

Comprendre et maîtriser l'inventaire Ansible : fichiers INI et YAML, groupes, variables d'hôte et de groupe, …

Ansible : les commandes Ad-Hoc

Exécuter des actions immédiates sur un parc de serveurs avec ansible : modules ping, command, shell, copy, ap…

Ansible : structurer son code avec les rôles

Créer, organiser et réutiliser des rôles Ansible : arborescence, defaults, tasks, handlers, templates, dépend…

Pour aller plus loin