Introduction : Pourquoi les Rôles Ansible ?
Lorsque vous commencez à travailler avec Ansible, vos premiers playbooks sont souvent de simples fichiers YAML contenant quelques tâches. Mais à mesure que votre infrastructure grandit, ces playbooks deviennent rapidement volumineux, difficiles à maintenir et impossibles à réutiliser. C'est précisément là qu'interviennent les rôles Ansible.
Un rôle Ansible est une unité d'organisation qui permet de décomposer un playbook complexe en composants modulaires et réutilisables. Pensez aux rôles comme à des briques LEGO : chaque brique a une fonction précise, et vous les assemblez pour construire des structures complètes. Un rôle pour Nginx, un rôle pour PostgreSQL, un rôle pour la sécurité du système — chacun encapsule toute la logique nécessaire à une responsabilité donnée.
Architecture en un coup d'œil

Les avantages des rôles sont nombreux :
- Réutilisabilité : un rôle bien conçu peut être utilisé dans plusieurs projets sans modification.
- Lisibilité : la structure standardisée rend le code immédiatement compréhensible par toute l'équipe.
- Maintenabilité : les modifications sont isolées dans le rôle concerné, sans effet de bord sur le reste.
- Testabilité : chaque rôle peut être testé indépendamment avec des outils comme Molecule.
- Partage : les rôles peuvent être publiés sur Ansible Galaxy pour la communauté.
Dans cet article, nous allons explorer en profondeur la structure d'un rôle Ansible, apprendre à en créer un de A à Z, et mettre en pratique avec un rôle complet pour le serveur web Nginx. Que vous soyez débutant ou praticien intermédiaire, ce guide vous donnera toutes les clés pour maîtriser les rôles Ansible.
La Structure d'un Rôle Ansible
Un rôle Ansible suit une convention de répertoires stricte. C'est cette convention qui permet à Ansible de savoir automatiquement où chercher les tâches, les variables, les templates et les autres composants. Voici la structure complète d'un rôle :
mon_role/
├── defaults/
│ └── main.yml # Variables par défaut (priorité la plus basse)
├── vars/
│ └── main.yml # Variables du rôle (priorité plus élevée)
├── tasks/
│ └── main.yml # Liste principale des tâches
├── handlers/
│ └── main.yml # Handlers (actions déclenchées par notify)
├── templates/
│ └── config.j2 # Templates Jinja2
├── files/
│ └── script.sh # Fichiers statiques à copier
├── meta/
│ └── main.yml # Métadonnées et dépendances du rôle
├── tests/
│ ├── inventory # Inventaire de test
│ └── test.yml # Playbook de test
└── README.md # Documentation du rôleChaque répertoire a un rôle bien défini. Examinons-les un par un.
Le répertoire tasks/
C'est le cœur du rôle. Le fichier tasks/main.yml contient la liste des tâches à exécuter. Ansible charge automatiquement ce fichier lorsque le rôle est appelé. Vous pouvez également découper vos tâches en plusieurs fichiers et les inclure depuis main.yml :
# tasks/main.yml
---
- name: Inclure les tâches d'installation
ansible.builtin.include_tasks: install.yml
- name: Inclure les tâches de configuration
ansible.builtin.include_tasks: configure.yml
- name: Inclure les tâches de service
ansible.builtin.include_tasks: service.ymlCe découpage améliore considérablement la lisibilité, surtout pour les rôles complexes comprenant des dizaines de tâches.
Le répertoire handlers/
Les handlers sont des tâches spéciales qui ne s'exécutent que lorsqu'elles sont notifiées par une autre tâche via le mot-clé notify. Ils sont typiquement utilisés pour redémarrer ou recharger des services après un changement de configuration :
# handlers/main.yml
---
- name: Redémarrer nginx
ansible.builtin.service:
name: nginx
state: restarted
- name: Recharger nginx
ansible.builtin.service:
name: nginx
state: reloadedUn handler ne s'exécute qu'une seule fois à la fin du play, même s'il est notifié plusieurs fois. C'est un mécanisme élégant pour éviter les redémarrages inutiles.
Le répertoire vars/
Ce répertoire contient les variables internes du rôle qui ne sont pas censées être modifiées par l'utilisateur. Elles ont une priorité élevée dans la hiérarchie des variables Ansible :
# vars/main.yml
---
nginx_user: www-data
nginx_pid_file: /run/nginx.pid
nginx_error_log: /var/log/nginx/error.log
nginx_access_log: /var/log/nginx/access.logLe répertoire defaults/
Contrairement à vars/, le répertoire defaults/ contient les variables par défaut du rôle. Elles ont la priorité la plus basse dans la hiérarchie des variables Ansible, ce qui signifie qu'elles peuvent être facilement surchargées par l'utilisateur du rôle :
# defaults/main.yml
---
nginx_listen_port: 80
nginx_server_name: localhost
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
nginx_gzip_enabled: true
nginx_ssl_enabled: false
nginx_ssl_certificate: ""
nginx_ssl_certificate_key: ""C'est ici que vous placez toutes les valeurs que l'utilisateur du rôle pourrait vouloir personnaliser. C'est une bonne pratique de fournir des valeurs par défaut sensées pour que le rôle fonctionne "out of the box".
Le répertoire templates/
Ce répertoire contient les templates Jinja2 (fichiers avec l'extension .j2). Ansible utilise le moteur de template Jinja2 pour générer des fichiers de configuration dynamiques en injectant les valeurs des variables :
# templates/nginx.conf.j2
worker_processes {{ nginx_worker_processes }};
pid {{ nginx_pid_file }};
events {
worker_connections {{ nginx_worker_connections }};
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log {{ nginx_access_log }};
error_log {{ nginx_error_log }};
keepalive_timeout {{ nginx_keepalive_timeout }};
{% if nginx_gzip_enabled %}
gzip on;
gzip_types text/plain text/css application/json application/javascript;
{% endif %}
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}Lorsque vous utilisez le module template dans vos tâches, Ansible cherche automatiquement les templates dans le répertoire templates/ du rôle. Vous n'avez pas besoin de spécifier le chemin complet.
Le répertoire files/
Ce répertoire contient les fichiers statiques qui doivent être copiés tels quels sur les hôtes distants. Contrairement aux templates, ces fichiers ne subissent aucune transformation. Exemples typiques : scripts shell, certificats, clés SSH.
# files/index.html
<!DOCTYPE html>
<html>
<head><title>Bienvenue</title></head>
<body>
<h1>Serveur configuré par Ansible</h1>
</body>
</html>Le répertoire meta/
Le fichier meta/main.yml contient les métadonnées du rôle ainsi que ses dépendances. Ces informations sont cruciales pour Ansible Galaxy et pour la résolution automatique des dépendances :
# meta/main.yml
---
galaxy_info:
author: votre_nom
description: Rôle pour installer et configurer Nginx
company: CodeClan
license: MIT
min_ansible_version: "2.14"
platforms:
- name: Ubuntu
versions:
- focal
- jammy
- name: Debian
versions:
- bullseye
- bookworm
galaxy_tags:
- nginx
- web
- reverse_proxy
dependencies:
- role: common
- role: firewall
vars:
firewall_allowed_ports:
- "80/tcp"
- "443/tcp"Les dépendances listées ici seront automatiquement exécutées avant le rôle lui-même. C'est un moyen puissant de chaîner les rôles.
Créer un Rôle avec ansible-galaxy init
Plutôt que de créer manuellement toute cette arborescence, Ansible fournit une commande dédiée qui génère le squelette d'un rôle en un instant :
# Créer un nouveau rôle
ansible-galaxy init mon_role_nginx
# Résultat affiché
# - Role mon_role_nginx was created successfullyCette commande crée automatiquement tous les répertoires et fichiers main.yml avec un contenu de base. Vous pouvez également spécifier un chemin de destination :
# Créer le rôle dans un répertoire spécifique
ansible-galaxy init --init-path ./roles mon_role_nginx
# Vérifier la structure créée
tree roles/mon_role_nginx/Par convention, les rôles sont placés dans un répertoire roles/ à la racine de votre projet Ansible. Vous pouvez aussi configurer d'autres chemins via le fichier ansible.cfg :
# ansible.cfg
[defaults]
roles_path = ./roles:~/.ansible/roles:/etc/ansible/rolesVariables par défaut vs Variables de rôle
La distinction entre defaults/main.yml et vars/main.yml est fondamentale et souvent source de confusion pour les débutants. Voici la règle d'or :
defaults/ contient les variables que l'utilisateur du rôle est censé pouvoir modifier. vars/ contient les variables internes au rôle qui ne devraient pas être changées.
Dans la hiérarchie de priorité des variables Ansible (de la plus basse à la plus haute), voici où se situent ces deux types :
- defaults/main.yml — priorité la plus basse (facilement surchargeable)
- Variables d'inventaire (group_vars, host_vars)
- Variables de play (vars: dans le playbook)
- vars/main.yml — priorité élevée (difficile à surcharger)
- Extra vars (-e sur la ligne de commande) — priorité maximale
En pratique, la grande majorité de vos variables devraient aller dans defaults/main.yml. Réservez vars/main.yml pour les constantes techniques qui ne doivent pas varier (chemins système, noms de paquets spécifiques à l'OS, etc.).
Voici un exemple concret illustrant cette distinction :
# defaults/main.yml — Configurable par l'utilisateur
---
nginx_listen_port: 80
nginx_server_name: localhost
nginx_worker_connections: 1024
nginx_client_max_body_size: "10m"
nginx_custom_locations: []
# vars/main.yml — Constantes internes du rôle
---
nginx_package_name: nginx
nginx_service_name: nginx
nginx_conf_dir: /etc/nginx
nginx_sites_available: /etc/nginx/sites-available
nginx_sites_enabled: /etc/nginx/sites-enabledVous pouvez même utiliser des variables conditionnelles basées sur le système d'exploitation, en chargeant dynamiquement des fichiers de variables :
# tasks/main.yml
- name: Charger les variables spécifiques à l'OS
ansible.builtin.include_vars: "{{ ansible_os_family | lower }}.yml"# vars/debian.yml
nginx_package_name: nginx
nginx_user: www-data
# vars/redhat.yml
nginx_package_name: nginx
nginx_user: nginxDépendances entre Rôles
Les rôles Ansible peuvent déclarer des dépendances vers d'autres rôles via le fichier meta/main.yml. Ces dépendances sont résolues et exécutées automatiquement avant le rôle principal.
# meta/main.yml
---
dependencies:
# Dépendance simple
- role: base_system
# Dépendance avec des variables
- role: firewall
vars:
firewall_allowed_ports:
- "80/tcp"
- "443/tcp"
# Dépendance depuis Galaxy
- role: geerlingguy.certbot
when: nginx_ssl_enabled | bool
# Dépendance avec tag
- role: monitoring
tags:
- monitoringQuelques points importants à retenir sur les dépendances :
- Par défaut, un rôle dépendant n'est exécuté qu'une seule fois, même s'il apparaît dans les dépendances de plusieurs rôles. Vous pouvez changer ce comportement avec
allow_duplicates: truedans lemeta/main.ymldu rôle dépendant. - Les dépendances sont exécutées avant les tâches du rôle qui les déclare.
- Les dépendances peuvent elles-mêmes avoir des dépendances, formant une chaîne récursive.
- Il est possible d'utiliser des conditions (
when) pour rendre une dépendance optionnelle.
Appeler un Rôle dans un Playbook
Il existe trois façons d'utiliser un rôle dans un playbook Ansible. Chacune a ses particularités et ses cas d'usage.
Méthode 1 : Le mot-clé roles (classique)
C'est la méthode historique et la plus courante. Les rôles listés sous roles: sont exécutés avant les tâches du play :
# playbook.yml
---
- name: Configurer les serveurs web
hosts: webservers
become: true
roles:
- role: common
- role: nginx
vars:
nginx_listen_port: 8080
nginx_server_name: monsite.fr
- role: monitoring
tags:
- monitoringMéthode 2 : import_role (importation statique)
L'importation statique avec import_role permet d'insérer un rôle au milieu des tâches d'un play. L'importation est résolue au moment du parsing du playbook, avant l'exécution :
# playbook.yml
---
- name: Déploiement complet
hosts: webservers
become: true
tasks:
- name: Préparer le système
ansible.builtin.apt:
update_cache: true
- name: Importer le rôle nginx
ansible.builtin.import_role:
name: nginx
vars:
nginx_listen_port: 80
- name: Vérifier que le service est actif
ansible.builtin.uri:
url: "http://localhost"
status_code: 200Méthode 3 : include_role (inclusion dynamique)
L'inclusion dynamique avec include_role est évaluée au moment de l'exécution. C'est utile lorsque le rôle à inclure dépend d'une condition ou d'une variable calculée pendant l'exécution :
# playbook.yml
---
- name: Déploiement conditionnel
hosts: all
become: true
tasks:
- name: Inclure le rôle web si nécessaire
ansible.builtin.include_role:
name: nginx
when: "'webservers' in group_names"
- name: Inclure un rôle dynamiquement
ansible.builtin.include_role:
name: "{{ item }}"
loop:
- common
- security
- monitoringQuelle méthode choisir ? Utilisezroles:pour les cas simples et classiques. Préférezimport_rolequand vous avez besoin de mélanger rôles et tâches dans un ordre précis. Réservezinclude_rolepour les inclusions conditionnelles ou dynamiques.
Exemple Complet : Un Rôle Nginx de Production
Mettons tout cela en pratique avec un rôle Nginx complet et prêt pour la production. Ce rôle gère l'installation, la configuration, les virtual hosts et le SSL.
Initialisation du rôle
ansible-galaxy init roles/nginx
defaults/main.yml
# roles/nginx/defaults/main.yml
---
# Configuration générale
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
nginx_client_max_body_size: "64m"
nginx_gzip_enabled: true
# Virtual hosts
nginx_vhosts:
- server_name: localhost
listen_port: 80
root: /var/www/html
index: "index.html index.htm"
locations:
- path: /
options: |
try_files $uri $uri/ =404;
# SSL (désactivé par défaut)
nginx_ssl_enabled: false
nginx_ssl_certificate: ""
nginx_ssl_certificate_key: ""
nginx_ssl_protocols: "TLSv1.2 TLSv1.3"
nginx_ssl_ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"
# Sécurité headers
nginx_security_headers_enabled: true
nginx_hsts_max_age: 31536000
# Gestion du service
nginx_service_enabled: true
nginx_service_state: started
# Page par défaut
nginx_default_page_enabled: true
nginx_default_page_title: "Bienvenue"vars/main.yml
# roles/nginx/vars/main.yml
---
nginx_package_name: nginx
nginx_service_name: nginx
nginx_conf_dir: /etc/nginx
nginx_conf_file: /etc/nginx/nginx.conf
nginx_sites_available: /etc/nginx/sites-available
nginx_sites_enabled: /etc/nginx/sites-enabled
nginx_log_dir: /var/log/nginx
nginx_pid_file: /run/nginx.pidtasks/main.yml
# roles/nginx/tasks/main.yml
---
- name: Inclure les tâches d'installation
ansible.builtin.include_tasks: install.yml
- name: Inclure les tâches de configuration
ansible.builtin.include_tasks: configure.yml
- name: Inclure les tâches de virtual hosts
ansible.builtin.include_tasks: vhosts.yml
- name: Inclure les tâches SSL
ansible.builtin.include_tasks: ssl.yml
when: nginx_ssl_enabled | bool
- name: Inclure les tâches de service
ansible.builtin.include_tasks: service.ymltasks/install.yml
# roles/nginx/tasks/install.yml
---
- name: Mettre à jour le cache APT
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
- name: Installer Nginx
ansible.builtin.apt:
name: "{{ nginx_package_name }}"
state: present
notify: Redémarrer nginx
- name: Créer les répertoires nécessaires
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0755"
loop:
- "{{ nginx_sites_available }}"
- "{{ nginx_sites_enabled }}"
- "{{ nginx_log_dir }}"
- /var/www/htmltasks/configure.yml
# roles/nginx/tasks/configure.yml
---
- name: Déployer la configuration principale Nginx
ansible.builtin.template:
src: nginx.conf.j2
dest: "{{ nginx_conf_file }}"
owner: root
group: root
mode: "0644"
validate: "nginx -t -c %s"
notify: Recharger nginx
- name: Supprimer la configuration par défaut
ansible.builtin.file:
path: "{{ nginx_sites_enabled }}/default"
state: absent
notify: Recharger nginx
- name: Déployer la page par défaut
ansible.builtin.template:
src: index.html.j2
dest: /var/www/html/index.html
owner: www-data
group: www-data
mode: "0644"
when: nginx_default_page_enabled | booltasks/vhosts.yml
# roles/nginx/tasks/vhosts.yml
---
- name: Déployer les virtual hosts
ansible.builtin.template:
src: vhost.conf.j2
dest: "{{ nginx_sites_available }}/{{ item.server_name }}.conf"
owner: root
group: root
mode: "0644"
loop: "{{ nginx_vhosts }}"
notify: Recharger nginx
- name: Activer les virtual hosts
ansible.builtin.file:
src: "{{ nginx_sites_available }}/{{ item.server_name }}.conf"
dest: "{{ nginx_sites_enabled }}/{{ item.server_name }}.conf"
state: link
loop: "{{ nginx_vhosts }}"
notify: Recharger nginxtasks/ssl.yml
# roles/nginx/tasks/ssl.yml
---
- name: Créer le répertoire SSL
ansible.builtin.file:
path: /etc/nginx/ssl
state: directory
owner: root
group: root
mode: "0700"
- name: Copier le certificat SSL
ansible.builtin.copy:
src: "{{ nginx_ssl_certificate }}"
dest: /etc/nginx/ssl/server.crt
owner: root
group: root
mode: "0644"
when: nginx_ssl_certificate | length > 0
notify: Recharger nginx
- name: Copier la clé privée SSL
ansible.builtin.copy:
src: "{{ nginx_ssl_certificate_key }}"
dest: /etc/nginx/ssl/server.key
owner: root
group: root
mode: "0600"
when: nginx_ssl_certificate_key | length > 0
notify: Recharger nginx
- name: Générer les paramètres Diffie-Hellman
ansible.builtin.command:
cmd: openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
creates: /etc/nginx/ssl/dhparam.pemtasks/service.yml
# roles/nginx/tasks/service.yml
---
- name: Vérifier la configuration Nginx
ansible.builtin.command:
cmd: nginx -t
changed_when: false
- name: Gérer le service Nginx
ansible.builtin.service:
name: "{{ nginx_service_name }}"
state: "{{ nginx_service_state }}"
enabled: "{{ nginx_service_enabled }}"handlers/main.yml
# roles/nginx/handlers/main.yml
---
- name: Redémarrer nginx
ansible.builtin.service:
name: "{{ nginx_service_name }}"
state: restarted
- name: Recharger nginx
ansible.builtin.service:
name: "{{ nginx_service_name }}"
state: reloadedtemplates/nginx.conf.j2
# roles/nginx/templates/nginx.conf.j2
user www-data;
worker_processes {{ nginx_worker_processes }};
pid {{ nginx_pid_file }};
events {
worker_connections {{ nginx_worker_connections }};
multi_accept on;
}
http {
# Configuration de base
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout {{ nginx_keepalive_timeout }};
types_hash_max_size 2048;
client_max_body_size {{ nginx_client_max_body_size }};
# Types MIME
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
access_log {{ nginx_log_dir }}/access.log;
error_log {{ nginx_log_dir }}/error.log;
{% if nginx_gzip_enabled %}
# Compression Gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
application/json
application/javascript
application/xml
application/rss+xml
image/svg+xml;
{% endif %}
{% if nginx_security_headers_enabled %}
# En-têtes de sécurité
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
{% if nginx_ssl_enabled %}
add_header Strict-Transport-Security "max-age={{ nginx_hsts_max_age }}; includeSubDomains" always;
{% endif %}
{% endif %}
# Virtual hosts
include {{ nginx_sites_enabled }}/*;
}templates/vhost.conf.j2
# roles/nginx/templates/vhost.conf.j2
server {
listen {{ item.listen_port }};
server_name {{ item.server_name }};
root {{ item.root }};
index {{ item.index | default('index.html index.htm') }};
{% if nginx_ssl_enabled and item.listen_port | int == 443 %}
listen 443 ssl;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
ssl_protocols {{ nginx_ssl_protocols }};
ssl_ciphers {{ nginx_ssl_ciphers }};
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
{% endif %}
# Logging par virtual host
access_log {{ nginx_log_dir }}/{{ item.server_name }}_access.log;
error_log {{ nginx_log_dir }}/{{ item.server_name }}_error.log;
{% for location in item.locations | default([]) %}
location {{ location.path }} {
{{ location.options | indent(8) }}
}
{% endfor %}
# Interdire l'accès aux fichiers cachés
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}templates/index.html.j2
# roles/nginx/templates/index.html.j2
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ nginx_default_page_title }}</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
h1 { color: #333; }
</style>
</head>
<body>
<h1>{{ nginx_default_page_title }}</h1>
<p>Serveur {{ inventory_hostname }} configuré avec Ansible.</p>
</body>
</html>Utilisation du rôle dans un playbook
# site.yml
---
- name: Déployer les serveurs web
hosts: webservers
become: true
roles:
- role: nginx
vars:
nginx_listen_port: 80
nginx_server_name: codeclan.fr
nginx_worker_connections: 2048
nginx_gzip_enabled: true
nginx_ssl_enabled: true
nginx_ssl_certificate: files/certs/codeclan.crt
nginx_ssl_certificate_key: files/certs/codeclan.key
nginx_vhosts:
- server_name: codeclan.fr
listen_port: 80
root: /var/www/codeclan
index: "index.html"
locations:
- path: /
options: |
try_files $uri $uri/ =404;
- path: /api
options: |
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;Bonnes Pratiques pour les Rôles Ansible
Conventions de nommage
Adoptez des conventions cohérentes pour vos rôles :
- Utilisez des noms en minuscules avec des underscores :
nginx_reverse_proxy, pasNginx-Reverse-Proxy. - Préfixez les variables du rôle avec le nom du rôle :
nginx_listen_port, pas simplementlisten_port. Cela évite les collisions de noms entre rôles. - Nommez explicitement chaque tâche avec un verbe d'action : "Installer Nginx", "Déployer la configuration", "Activer le service".
Documentation
Chaque rôle devrait inclure un README.md documentant :
- La description du rôle et son objectif.
- Les prérequis et les plateformes supportées.
- La liste complète des variables avec leurs valeurs par défaut.
- Des exemples d'utilisation dans un playbook.
- Les dépendances requises.
- La licence et les informations de l'auteur.
Idempotence
Vos tâches doivent être idempotentes : exécuter le rôle plusieurs fois doit produire le même résultat sans effet de bord. Évitez les modules command et shell quand un module dédié existe. Si vous devez les utiliser, pensez aux paramètres creates, removes et changed_when.
Tests avec Molecule
Molecule est l'outil de test standard pour les rôles Ansible. Il permet de tester vos rôles dans des conteneurs Docker ou des machines virtuelles de manière automatisée :
# Installer Molecule avec le driver Docker
pip install molecule molecule-docker
# Initialiser les tests Molecule pour un rôle existant
cd roles/nginx
molecule init scenario --driver-name docker
# Lancer les tests
molecule test
# Cycle de test complet :
# 1. molecule create — crée l'instance de test
# 2. molecule converge — applique le rôle
# 3. molecule idempotence — vérifie l'idempotence
# 4. molecule verify — lance les vérifications
# 5. molecule destroy — détruit l'instanceLe fichier molecule/default/molecule.yml définit l'environnement de test :
# molecule/default/molecule.yml
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: instance-ubuntu
image: ubuntu:22.04
pre_build_image: true
privileged: true
command: /sbin/init
- name: instance-debian
image: debian:12
pre_build_image: true
privileged: true
command: /sbin/init
provisioner:
name: ansible
verifier:
name: ansibleEt le fichier molecule/default/verify.yml contient les assertions de vérification :
# molecule/default/verify.yml
---
- name: Vérifier le rôle nginx
hosts: all
gather_facts: false
tasks:
- name: Vérifier que Nginx est installé
ansible.builtin.command: nginx -v
changed_when: false
- name: Vérifier que le service Nginx est actif
ansible.builtin.service_facts:
- name: Asserter que Nginx est en cours d'exécution
ansible.builtin.assert:
that:
- "'nginx.service' in services"
- "services['nginx.service'].state == 'running'"
- name: Vérifier que le port 80 est en écoute
ansible.builtin.wait_for:
port: 80
timeout: 5Organisation d'un Projet avec des Rôles
Pour un projet Ansible bien structuré utilisant des rôles, voici l'arborescence recommandée :
mon_projet_ansible/
├── ansible.cfg # Configuration Ansible
├── inventory/
│ ├── production/
│ │ ├── hosts.yml # Inventaire de production
│ │ ├── group_vars/
│ │ │ ├── all.yml
│ │ │ └── webservers.yml
│ │ └── host_vars/
│ │ └── web01.yml
│ └── staging/
│ ├── hosts.yml
│ └── group_vars/
│ └── all.yml
├── playbooks/
│ ├── site.yml # Playbook principal
│ ├── webservers.yml # Playbook serveurs web
│ └── databases.yml # Playbook bases de données
├── roles/
│ ├── common/ # Rôle de base commun
│ ├── nginx/ # Rôle Nginx
│ ├── postgresql/ # Rôle PostgreSQL
│ ├── security/ # Rôle durcissement sécurité
│ └── monitoring/ # Rôle surveillance
└── requirements.yml # Dépendances GalaxyLe playbook principal site.yml orchestre l'ensemble :
# playbooks/site.yml
---
- name: Appliquer la configuration commune
hosts: all
become: true
roles:
- common
- security
- name: Configurer les serveurs web
hosts: webservers
become: true
roles:
- nginx
- monitoring
- name: Configurer les bases de données
hosts: databases
become: true
roles:
- postgresql
- monitoringConclusion
Les rôles Ansible sont un pilier fondamental de toute automatisation professionnelle. Ils transforment des playbooks monolithiques en composants modulaires, réutilisables et testables. En suivant les conventions de structure, en séparant clairement les variables par défaut des variables internes, et en documentant chaque rôle, vous construisez une base d'automatisation solide et maintenable.
Le rôle Nginx que nous avons construit ensemble illustre les bonnes pratiques : séparation des tâches en fichiers logiques, utilisation de templates Jinja2, variables par défaut configurables, handlers pour les rechargements, et tests avec Molecule. Ce même patron peut être appliqué à n'importe quel logiciel ou composant d'infrastructure.
Dans le prochain article, nous explorerons Ansible Galaxy, la plateforme communautaire qui vous permet de partager vos rôles et de réutiliser ceux créés par la communauté. Vous découvrirez comment tirer parti de milliers de rôles et collections prêts à l'emploi pour accélérer considérablement vos projets d'automatisation.