Aller au contenu Skip to main content

Maîtriser les Playbooks Ansible

Le concept fondamental : la Gestion de Configuration

Avant de plonger dans les playbooks, il est essentiel de comprendre le problème qu’ils résolvent.

Historiquement, pour configurer un serveur, un administrateur se connectait en SSH et tapait des commandes manuellement. Cette approche pose plusieurs problèmes :

  • Non reproductible : On ne sait plus exactement ce qui a été fait 6 mois plus tard.
  • Non scalable : Configurer 1 serveur manuellement, c’est faisable. 50, c’est un calvaire.
  • Sujette aux erreurs : Une faute de frappe peut casser un environnement de production.

La gestion de configuration (Configuration Management) résout tout cela en décrivant l’état désiré de l’infrastructure dans des fichiers versionnés. C’est le paradigme Infrastructure as Code (IaC).

Il existe deux grandes approches :

ApprochePrincipeOutils
ImpérativeOn décrit les étapes à exécuter (“installe ce paquet, copie ce fichier”)Scripts Bash, Fabric
DéclarativeOn décrit l’état final désiré (“ce paquet doit être installé, ce fichier doit avoir ce contenu”)Ansible, Puppet, Chef, Salt

Ansible se situe dans une position hybride : ses playbooks sont déclaratifs (on décrit un état) mais les tâches s’exécutent de manière séquentielle comme un script.


Qu’est-ce qu’un Playbook ?

Un Playbook est un fichier écrit en YAML (Yet Another Markup Language) qui décrit une liste de tâches à exécuter sur un ou plusieurs groupes de machines (hosts). C’est votre “recette de cuisine” pour l’état désiré de votre infrastructure.

Principes Clés

  1. Idempotence : Un playbook peut être exécuté plusieurs fois sans casser le système. Si l’état désiré est déjà atteint (ex: “le fichier doit exister”), Ansible ne fait rien. C’est le principe le plus important.
  2. Lisibilité : Le format YAML est conçu pour être lisible par les humains, même sans connaissances techniques profondes.
  3. Ordre : Les tâches sont exécutées séquentiellement, de haut en bas.
  4. Agentless (sans agent) : Ansible ne nécessite aucun logiciel installé sur les machines cibles. Il utilise uniquement SSH.
  5. Push-based : C’est la machine de contrôle qui pousse la configuration vers les cibles.

Architecture d’Ansible

Pour bien comprendre les playbooks, il faut saisir comment Ansible fonctionne globalement.

graph LR
    subgraph "Machine de contrôle"
        PB["📄 Playbook.yml"]
        INV["📋 Inventory"]
        ROLES["📁 Roles/"]
    end

    subgraph "Serveurs cibles"
        A["🖥️ Serveur A<br/>Debian 12"]
        B["🖥️ Serveur B<br/>Ubuntu 24.04"]
        C["🖥️ Serveur C<br/>Rocky Linux 9"]
    end

    PB -->|SSH| A
    PB -->|SSH| B
    PB -->|SSH| C
    INV -.-> PB
    ROLES -.-> PB

Les composants clés :

  • Inventory : Le fichier qui liste les machines cibles et les regroupe.
  • Playbook : Le fichier YAML qui décrit les tâches.
  • Modules : Les briques unitaires (installer un paquet, copier un fichier, démarrer un service…). Ansible en fournit des milliers.
  • Roles : Un mécanisme de structuration pour réutiliser et partager des playbooks.
  • Facts : Des informations collectées automatiquement sur les machines cibles (OS, IP, RAM…).
  • Vault : Système de chiffrement intégré pour stocker des secrets (mots de passe, clés API).

L’Inventaire (Inventory)

L’inventaire est la première pièce du puzzle. Il définit vos playbooks vont s’exécuter.

Format INI (basique)

inventory.ini
[webservers]
web1.example.com
web2.example.com
[dbservers]
db1.example.com ansible_user=postgres
[production:children]
webservers
dbservers
[all:vars]
ansible_python_interpreter=/usr/bin/python3

Format YAML (plus flexible)

inventory.yml
all:
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
http_port: 8080
dbservers:
hosts:
db1.example.com:
ansible_user: postgres

Structure d’un Playbook

Un playbook est composé d’une ou plusieurs “plays”. Chaque “play” mappe un groupe d’hôtes à des tâches.

Exemple basique

Voici un playbook simple qui installe et démarre Nginx sur des serveurs web :

install_nginx.yml
---
- name: Installer et configurer Nginx
hosts: webservers
become: yes # Exécuter en tant que root (sudo)
tasks:
- name: S'assurer que Nginx est installé
apt:
name: nginx
state: present
update_cache: yes
- name: S'assurer que Nginx est démarré
service:
name: nginx
state: started
enabled: yes

Analyse des composants

  • hosts: Définit sur quelles machines ce play va s’exécuter (défini dans votre fichier d’inventaire).
  • become: Indique si les tâches doivent être exécutées avec élévation de privilèges (sudo).
  • tasks: La liste des actions à effectuer.
  • modules: apt et service sont des modules Ansible. Ce sont les outils qui font le travail réel.

Exemple complet : Déployer une stack LEMP

Voici un playbook réaliste qui illustre plusieurs fonctionnalités simultanément :

lemp_stack.yml
---
- name: Déployer une stack LEMP complète
hosts: webservers
become: yes
gather_facts: yes
vars:
mysql_root_password: '{{ vault_mysql_root_password }}'
php_version: '8.2'
app_domain: 'monsite.example.com'
pre_tasks:
- name: Mettre à jour le cache APT
apt:
update_cache: yes
cache_valid_time: 3600
tasks:
- name: Installer les paquets nécessaires
apt:
name:
- nginx
- 'php{{ php_version }}-fpm'
- 'php{{ php_version }}-mysql'
- mariadb-server
- python3-pymysql
state: present
- name: Déployer la config Nginx depuis un template Jinja2
template:
src: templates/nginx-vhost.conf.j2
dest: '/etc/nginx/sites-available/{{ app_domain }}'
owner: root
group: root
mode: '0644'
notify: Recharger Nginx
- name: Activer le vhost
file:
src: '/etc/nginx/sites-available/{{ app_domain }}'
dest: '/etc/nginx/sites-enabled/{{ app_domain }}'
state: link
notify: Recharger Nginx
- name: Configurer le mot de passe root MySQL
mysql_user:
name: root
password: '{{ mysql_root_password }}'
login_unix_socket: /var/run/mysqld/mysqld.sock
no_log: true
handlers:
- name: Recharger Nginx
service:
name: nginx
state: reloaded

Comment utiliser les Playbooks ?

Prérequis

  • Ansible installé sur votre machine de contrôle.
  • Un fichier d’inventaire (hosts.ini ou inventory) listant vos serveurs.
  • Accès SSH configuré vers les machines cibles (clés SSH recommandées).

Exécution

La commande principale est ansible-playbook.

Fenêtre de terminal
# Syntaxe basique
ansible-playbook -i inventory.ini mon_playbook.yml

Options utiles

  • Vérifier la syntaxe sans exécuter :
    Fenêtre de terminal
    ansible-playbook mon_playbook.yml --syntax-check
  • Mode “Dry Run” (simulation) pour voir ce qui changerait sans rien modifier :
    Fenêtre de terminal
    ansible-playbook mon_playbook.yml --check
  • Voir les différences détaillées (utile avec --check) :
    Fenêtre de terminal
    ansible-playbook mon_playbook.yml --diff
  • Limiter l’exécution à un hôte spécifique :
    Fenêtre de terminal
    ansible-playbook mon_playbook.yml --limit web1.example.com
  • Commencer à une tâche spécifique (utile pour reprendre après un échec) :
    Fenêtre de terminal
    ansible-playbook mon_playbook.yml --start-at-task="Activer le vhost"
  • Exécuter avec un tag spécifique :
    Fenêtre de terminal
    ansible-playbook mon_playbook.yml --tags "deploy"
  • Augmenter la verbosité pour le debug :
    Fenêtre de terminal
    ansible-playbook mon_playbook.yml -vvv

Fonctionnalités Avancées

1. Variables et précédence

Les variables permettent de rendre vos playbooks dynamiques. Elles peuvent être définies à de nombreux endroits avec un ordre de priorité :

  1. Ligne de commande (-e "var=value") — la plus haute priorité
  2. Variables de rôle (roles/x/vars/main.yml)
  3. Variables de play (vars: dans le playbook)
  4. Variables d’inventaire (host_vars/, group_vars/)
  5. Valeurs par défaut du rôle (roles/x/defaults/main.yml) — la plus basse
vars:
http_port: 80
max_clients: 200
tasks:
- name: Copier le fichier de config
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf

2. Templates Jinja2

Le module template utilise le moteur Jinja2 pour générer des fichiers de configuration dynamiques.

Fichier templates/nginx.conf.j2 :

server {
listen {{ http_port }};
server_name {{ app_domain }};
{% for backend in backends %}
location /{{ backend.name }} {
proxy_pass http://{{ backend.host }}:{{ backend.port }};
}
{% endfor %}
}

3. Handlers (Gestionnaires)

Les handlers sont des tâches spéciales qui ne s’exécutent que si elles sont notifiées par une autre tâche. Très utile pour redémarrer des services uniquement quand la config change.

apache_config.yml
tasks:
- name: Modifier la config Apache
template:
src: httpd.conf
dest: /etc/httpd/conf/httpd.conf
notify:
- Redémarrer Apache
handlers:
- name: Redémarrer Apache
service:
name: httpd
state: restarted

4. Conditionnels

Exécuter des tâches selon des conditions.

redhat_install_apache.yml
tasks:
- name: Installer Apache sur RedHat
yum:
name: httpd
state: present
when: ansible_os_family == "RedHat"
- name: Installer Apache sur Debian
apt:
name: apache2
state: present
when: ansible_os_family == "Debian"

5. Loops (Boucles)

Pour répéter une tâche sur plusieurs éléments.

create_users.yml
- name: Créer plusieurs utilisateurs
user:
name: '{{ item.name }}'
groups: '{{ item.groups }}'
state: present
loop:
- { name: alice, groups: 'sudo,docker' }
- { name: bob, groups: 'docker' }
- { name: charlie, groups: 'www-data' }

6. Vault (Gestion des secrets)

Ansible Vault permet de chiffrer des données sensibles directement dans vos fichiers YAML.

Fenêtre de terminal
# Créer un fichier chiffré
ansible-vault create secrets.yml
# Chiffrer un fichier existant
ansible-vault encrypt secrets.yml
# Éditer un fichier chiffré
ansible-vault edit secrets.yml
# Exécuter un playbook qui utilise des secrets
ansible-playbook site.yml --ask-vault-pass

7. Tags

Les tags permettent d’exécuter seulement une partie du playbook.

tasks:
- name: Installer les paquets
apt:
name: nginx
state: present
tags:
- install
- packages
- name: Déployer la configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
tags:
- config
- deploy

Les Rôles : structurer pour scaler

Pour les projets sérieux, un seul fichier playbook devient ingérable. Les Rôles imposent une structure de répertoire standard qui permet de réutiliser et partager du code.

Structure d’un Rôle

graph TD
    ROOT["📁 roles/nginx/"] --> TASKS["📁 tasks/<br/>main.yml"]
    ROOT --> HANDLERS["📁 handlers/<br/>main.yml"]
    ROOT --> TEMPLATES["📁 templates/<br/>nginx.conf.j2"]
    ROOT --> FILES["📁 files/<br/>index.html"]
    ROOT --> VARS["📁 vars/<br/>main.yml<br/><i>haute priorité</i>"]
    ROOT --> DEFAULTS["📁 defaults/<br/>main.yml<br/><i>basse priorité</i>"]
    ROOT --> META["📁 meta/<br/>main.yml"]

    style TASKS fill:#4CAF50,color:#fff
    style HANDLERS fill:#FF9800,color:#fff
    style TEMPLATES fill:#2196F3,color:#fff
    style VARS fill:#9C27B0,color:#fff
    style DEFAULTS fill:#607D8B,color:#fff

Utiliser un Rôle dans un Playbook

---
- name: Configurer les serveurs web
hosts: webservers
become: yes
roles:
- common # Rôle de base (sécurité, NTP, etc.)
- nginx
- { role: app, tags: ['deploy'] }

Ansible Galaxy

Ansible Galaxy est un hub communautaire de rôles prêts à l’emploi.

Fenêtre de terminal
# Installer un rôle depuis Galaxy
ansible-galaxy install geerlingguy.docker
# Installer les rôles listés dans un fichier requirements
ansible-galaxy install -r requirements.yml

Bonnes Pratiques

  1. Nommez toujours vos tâches (name: ...) : Cela rend la sortie de la commande compréhensible.
  2. Utilisez le contrôle de version (Git) : Vos playbooks sont du code (“Infrastructure as Code”).
  3. Séparez les rôles : Pour les projets complexes, ne mettez pas tout dans un seul fichier. Utilisez la structure de Rôles d’Ansible (ansible-galaxy init mon_role).
  4. Testez avec --check avant d’appliquer sur la prod.
  5. Utilisez ansible-lint : Un linter dédié qui vérifie les bonnes pratiques dans vos playbooks.
    Fenêtre de terminal
    pip install ansible-lint
    ansible-lint mon_playbook.yml
  6. Structurez vos projets avec un layout standard :
graph TD
    PROJECT["📁 project/"] --> CFG["⚙️ ansible.cfg"]
    PROJECT --> INVENTORY["📁 inventory/"]
    PROJECT --> GROUPVARS["📁 group_vars/"]
    PROJECT --> HOSTVARS["📁 host_vars/"]
    PROJECT --> ROLES["📁 roles/"]
    PROJECT --> PLAYBOOKS["📁 playbooks/"]

    INVENTORY --> PROD["production"]
    INVENTORY --> STAGING["staging"]
    GROUPVARS --> ALL["all.yml"]
    GROUPVARS --> WEB["webservers.yml"]
    HOSTVARS --> HOST1["web1.example.com.yml"]
    PLAYBOOKS --> SITE["site.yml"]
    PLAYBOOKS --> WEBPB["webservers.yml"]
    PLAYBOOKS --> DBPB["dbservers.yml"]

    style PROJECT fill:#1565C0,color:#fff
    style INVENTORY fill:#2E7D32,color:#fff
    style GROUPVARS fill:#6A1B9A,color:#fff
    style PLAYBOOKS fill:#E65100,color:#fff
  1. Ne stockez jamais de secrets en clair : Utilisez Ansible Vault ou un gestionnaire de secrets externe (HashiCorp Vault, AWS Secrets Manager).
  2. Testez vos playbooks avec Molecule, un framework de test pour les rôles Ansible.
    Fenêtre de terminal
    pip install molecule molecule-docker
    molecule init role mon_role
    molecule test

Conclusion

Les Playbooks sont au cœur de la puissance d’Ansible. Contrairement aux commandes qui permettent d’exécuter une tâche unique, les playbooks permettent d’orchestrer des configurations complexes, des déploiements d’applications et bien plus encore, de manière répétable et documentée.

C’est un outil indispensable pour l’administrateur système moderne et l’ingénieur DevOps. Il transforme des procédures manuelles sujettes aux erreurs en processus automatisés, fiables et partageables. Leur principal avantage face aux alternatives : la simplicité de démarrage (pas d’agent, YAML lisible) combinée à une puissance suffisante pour la majorité des cas. Pour le provisionnement d’infrastructure cloud, combinez Ansible avec Terraform.

Le workflow idéal en production :

  1. Terraform provisionne les serveurs et le réseau.
  2. Ansible configure les machines et déploie les applications.
  3. Git versionne le tout.
  4. CI/CD (GitLab CI, GitHub Actions) exécute les playbooks automatiquement à chaque commit.