📋 Ansible : 10 Bonnes Pratiques pour des Rôles Réutilisables et Maintenables

🟡 Niveau : Intermédiaire | ⏱️ Lecture : 15 min | 🛠️ Mise en œuvre : Progressive

📚 Prérequis

  • Connaissances de base d’Ansible (inventaire, playbook, tâches)
  • Expérience avec l’écriture de playbooks simples
  • Familiarité avec YAML et Jinja2

Ansible est simple à apprendre, mais écrire du code Ansible de qualité production est un art. Après avoir développé et maintenu des dizaines de rôles, voici les 10 bonnes pratiques que nous appliquons systématiquement.


1️⃣ Structure de Rôle Standardisée

Respectez la structure officielle Ansible Galaxy :

mon_role/
├── defaults/           # Variables par défaut (écrasables)
│   └── main.yml
├── vars/               # Variables internes (non écrasables)
│   └── main.yml
├── tasks/              # Tâches principales
│   └── main.yml
├── handlers/           # Handlers (redémarrages, reloads)
│   └── main.yml
├── templates/          # Templates Jinja2
├── files/              # Fichiers statiques
├── meta/               # Métadonnées (dépendances, infos Galaxy)
│   └── main.yml
├── molecule/           # Tests automatisés
│   └── default/
├── docs/               # Documentation opérationnelle
│   ├── day1-guide.md
│   └── day2-runbook.md
└── README.md

Pourquoi : Tout le monde sait où trouver quoi. C’est la convention, respectez-la.

2️⃣ Defaults vs Vars : Bien Comprendre la Différence

defaults/main.yml

Variables configurables par l’utilisateur. Elles ont la priorité la plus basse et peuvent être écrasées dans l’inventaire ou le playbook.

# defaults/main.yml
docker_log_max_size: "10m"
docker_log_max_file: "3"
docker_prune_enabled: true
vars/main.yml

Variables internes au rôle. L’utilisateur ne devrait normalement pas les modifier. Priorité plus haute.

# vars/main.yml
_docker_service_name: "docker"
_docker_config_path: "/etc/docker/daemon.json"

Convention : Préfixez les variables internes avec _ pour les distinguer.

3️⃣ Préfixez Toutes Vos Variables

Les variables Ansible sont globales. Sans préfixe, vous risquez des collisions :

# ❌ Mauvais - risque de collision
enabled: true
port: 8080
version: "1.0"

# ✅ Bon - préfixe du rôle
myapp_enabled: true
myapp_port: 8080
myapp_version: "1.0"

Règle : {nom_du_role}_{nom_variable}

4️⃣ Garantir l’Idempotence

Un rôle idempotent produit le même résultat qu’il soit exécuté 1 ou 100 fois. Pas de surprises.

Problèmes Courants
# ❌ Non idempotent - ajoute une ligne à chaque exécution
- name: Add config line
  lineinfile:
    path: /etc/config
    line: "option=value"
    insertafter: EOF
    
# ✅ Idempotent - assure que la ligne existe
- name: Ensure config line
  lineinfile:
    path: /etc/config
    regexp: "^option="
    line: "option=value"
Testez l’Idempotence
# Première exécution
ansible-playbook playbook.yml

# Deuxième exécution - doit avoir 0 "changed"
ansible-playbook playbook.yml

5️⃣ Utilisez les Handlers Correctement

Les handlers sont des tâches qui ne s’exécutent que si notifiées, et une seule fois par play.

# handlers/main.yml
- name: Restart docker
  systemd:
    name: docker
    state: restarted
  listen: "restart docker"

- name: Reload docker
  systemd:
    name: docker
    state: reloaded
  listen: "reload docker"
# tasks/main.yml
- name: Configure daemon.json
  template:
    src: daemon.json.j2
    dest: /etc/docker/daemon.json
  notify: restart docker

Piège : Les handlers s’exécutent à la fin du play. Si vous avez besoin d’un redémarrage immédiat, utilisez meta: flush_handlers.

6️⃣ Documentez les Variables dans defaults/

Chaque variable doit avoir un commentaire explicatif :

# defaults/main.yml

# ============================================================================
# DOCKER LOG CONFIGURATION
# ============================================================================

# Log driver to use (json-file, journald, syslog, etc.)
docker_log_driver: "json-file"

# Maximum size of a log file before rotation (e.g., "10m", "100k")
docker_log_max_size: "10m"

# Maximum number of log files to keep
docker_log_max_file: "3"

# ============================================================================
# DOCKER PRUNING
# ============================================================================

# Enable automatic cleanup of unused Docker resources
docker_prune_enabled: true

# Hour to run the prune cron job (0-23)
docker_prune_hour: "3"

Pourquoi : Le fichier defaults/ est souvent la première documentation que l’utilisateur lit.

7️⃣ Factorisez avec des Fichiers de Tâches

Un fichier tasks/main.yml de 500 lignes est illisible. Découpez :

# tasks/main.yml
---
- name: Include OS-specific variables
  include_vars: "{{ ansible_os_family | lower }}.yml"

- name: Install prerequisites
  import_tasks: prerequisites.yml

- name: Configure repository
  import_tasks: repository.yml

- name: Install Docker
  import_tasks: install.yml

- name: Configure Docker
  import_tasks: configure.yml

- name: Setup maintenance tasks
  import_tasks: maintenance.yml
  when: docker_prune_enabled

import_tasks vs include_tasks :

  • import_tasks : Statique, parsé au chargement du playbook
  • include_tasks : Dynamique, évalué à l’exécution

8️⃣ Gérez les Différences entre OS

Évitez les when: partout pour distinguer Debian d’Ubuntu. Utilisez des fichiers de variables :

# vars/Debian.yml
_package_manager: apt
_docker_package: docker-ce
_service_manager: systemd

# vars/RedHat.yml
_package_manager: yum
_docker_package: docker-ce
_service_manager: systemd
# tasks/main.yml
- name: Include OS-specific variables
  include_vars: "{{ ansible_os_family }}.yml"

9️⃣ Testez avec Molecule

Molecule est le framework de test standard pour les rôles Ansible :

# molecule/default/molecule.yml
---
dependency:
  name: galaxy
driver:
  name: docker
platforms:
  - name: debian12
    image: debian:12
    pre_build_image: true
  - name: ubuntu2404
    image: ubuntu:24.04
    pre_build_image: true
provisioner:
  name: ansible
verifier:
  name: ansible
# Exécuter les tests
molecule test

# Développement itératif
molecule converge    # Applique le rôle
molecule verify      # Lance les vérifications
molecule destroy     # Nettoie

Ce qu’on teste :

  • Le rôle s’applique sans erreur
  • L’idempotence (deuxième application = 0 changed)
  • Les services sont démarrés et fonctionnels
  • Les configurations sont correctes

🔟 Documentation Opérationnelle

Le README ne suffit pas. Incluez :

Day-1 Guide

Ce que l’ops doit vérifier juste après le déploiement :

  • Commandes de vérification de santé
  • Comment accéder aux services
  • Premiers pas avec l’outil
Day-2+ Runbook

Guide pour les opérations courantes et le troubleshooting :

  • Procédures de backup/restore
  • Mise à jour et maintenance
  • Scénarios de disaster recovery
  • Problèmes courants et solutions

🏆 Exemple : Nos Rôles en Action

Tous nos rôles Ansible appliquent ces 10 bonnes pratiques :

  • ✅ Structure standardisée Galaxy-compatible
  • ✅ Variables préfixées et documentées
  • ✅ Idempotence garantie et testée
  • ✅ Tests Molecule sur 4 OS (Debian 12/13, Ubuntu 22.04/24.04)
  • ✅ Documentation Day-1 et Day-2 incluse

Besoin de rôles Ansible de qualité production ? Découvrez notre catalogue de rôles pour Docker, Monitoring, Haute Disponibilité et Sécurité.


📖 Glossaire

  • Playbook : Fichier YAML décrivant un ensemble de tâches à exécuter sur des serveurs
  • Rôle : Unité de réutilisation Ansible regroupant tâches, handlers, variables et templates
  • Handler : Tâche spéciale qui ne s’exécute que si notifiée par une autre tâche
  • Idempotence : Propriété garantissant que l’exécution multiple produit le même résultat
  • Molecule : Framework de test pour valider les rôles Ansible dans des conteneurs
  • Jinja2 : Moteur de templates Python utilisé par Ansible pour le templating
  • Ansible Galaxy : Hub communautaire de rôles Ansible réutilisables

Le prochain article abordera le DevSecOps : comment intégrer la sécurité dans votre pipeline CI/CD. Ne le manquez pas !