TL;DR — Résumé Rapide
Maitrisez les playbooks et roles Ansible pour automatiser l'infrastructure. Couvre inventaire, modules, Jinja2, ansible-vault, gestion d'erreurs et LAMP.
Gerer une infrastructure a grande echelle sans automatisation est une recette pour la derive de configuration, les etapes oubliees et les incidents a 3 heures du matin. Les playbooks et roles Ansible vous offrent une maniere structuree et repetable de definir exactement comment chaque serveur doit etre configure et d’appliquer cet etat de facon idempotente sur toute votre flotte. Ce guide couvre le flux de travail complet d’Ansible — de la gestion de l’inventaire et l’architecture des playbooks aux roles, templates Jinja2, secrets avec ansible-vault et gestion des erreurs — avec un deploiement reel d’une pile LAMP comme exemple final.
Prerequis
- Ansible installe sur un noeud de controle (Ubuntu 22.04+ ou toute machine Linux/macOS)
- Acces SSH par cle a un ou plusieurs hotes geres
- Python 3 sur tous les hotes cibles (pre-installe sur la plupart des distributions)
- Familiarite de base avec la syntaxe YAML et la ligne de commande Linux
Architecture d’Ansible: Le Modele Push
Ansible utilise une architecture push sans agent. Votre noeud de controle envoie la configuration aux noeuds geres via SSH. Il n’y a aucun daemon a executer, aucune base de donnees d’etat et rien a installer sur les cibles au-dela d’un interpreteur Python fonctionnel.
La chaine d’execution est: Inventaire → Playbook → Modules.
- Ansible lit l’inventaire pour determiner quels hotes cibler
- Il analyse le playbook pour construire une liste ordonnee de taches
- Pour chaque tache, il copie un petit module Python sur l’hote distant via SSH, l’execute, capture le resultat et supprime le module
- Les resultats (ok / changed / failed / skipped) sont agreges et affiches dans le terminal
Gestion de l’Inventaire
Format INI vs YAML
# inventory.ini
[webservers]
web1.example.com
web2.example.com ansible_port=2222
[dbservers]
db1.example.com
db2.example.com
[production:children]
webservers
dbservers
[all:vars]
ansible_user=deployer
ansible_ssh_private_key_file=~/.ssh/id_ed25519
# inventory.yml
all:
vars:
ansible_user: deployer
ansible_ssh_private_key_file: ~/.ssh/id_ed25519
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
ansible_port: 2222
dbservers:
hosts:
db1.example.com:
db2.example.com:
group_vars et host_vars
projet/
inventory.ini
group_vars/
all.yml
webservers.yml
dbservers.yml
host_vars/
web1.example.com.yml
site.yml
# group_vars/webservers.yml
http_port: 80
https_port: 443
document_root: /var/www/html
worker_processes: 4
Inventaire dynamique avec le plugin aws_ec2
# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
filters:
instance-state-name: running
"tag:Environment": production
keyed_groups:
- key: tags.Role
prefix: role
Structure d’un Playbook
- name: Configurer les serveurs web
hosts: webservers
become: true
vars:
app_name: myapp
app_port: 8080
tasks:
- name: Installer nginx
apt:
name: nginx
state: present
update_cache: true
- name: Deployer la config nginx depuis un template
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
notify: Redemarrer nginx
- name: S'assurer que nginx est actif et active
service:
name: nginx
state: started
enabled: true
handlers:
- name: Redemarrer nginx
service:
name: nginx
state: restarted
Conditionnels avec when
- name: Installer apache2 (Debian uniquement)
apt:
name: apache2
state: present
when: ansible_os_family == "Debian"
- name: Installer httpd (Red Hat uniquement)
yum:
name: httpd
state: present
when: ansible_os_family == "RedHat"
Boucles avec loop
- name: Installer les paquets necessaires
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- curl
- ufw
- fail2ban
- name: Creer les repertoires de l'application
file:
path: "{{ item.path }}"
state: directory
owner: "{{ item.owner }}"
mode: "{{ item.mode }}"
loop:
- { path: /var/www/myapp, owner: www-data, mode: "0755" }
- { path: /var/log/myapp, owner: www-data, mode: "0750" }
Templates Jinja2
{# templates/nginx.conf.j2 #}
user www-data;
worker_processes {{ worker_processes | default('auto') }};
http {
server {
listen {{ http_port }};
server_name {{ server_name }};
root {{ document_root }};
{% if enable_ssl | default(false) %}
listen {{ https_port }} ssl;
ssl_certificate {{ ssl_cert_path }};
{% endif %}
}
}
Tags pour l’execution selective
# Executer uniquement les taches avec le tag 'config'
ansible-playbook site.yml --tags config
# Ignorer les taches avec le tag 'install'
ansible-playbook site.yml --skip-tags install
Modules Essentiels
| Module | Objectif | Parametres cles |
|---|---|---|
apt / yum | Gestion des paquets | name, state, update_cache |
copy | Copier des fichiers statiques | src, dest, owner, mode |
template | Deployer des templates Jinja2 | src, dest, owner, mode |
service | Gerer les services systeme | name, state, enabled |
user | Gerer les comptes utilisateurs | name, groups, shell, state |
file | Creer des dossiers, liens | path, state, owner, mode |
lineinfile | Modifier des lignes dans un fichier | path, regexp, line, state |
command | Executer une commande | argv, creates |
shell | Commandes shell avec pipes | cmd, chdir |
git | Cloner ou mettre a jour un depot | repo, dest, version |
docker_container | Gerer des conteneurs Docker | name, image, state, ports |
debug | Afficher des valeurs de variables | msg, var |
Structure des Roles
ansible-galaxy init roles/webserver
roles/webserver/
tasks/
main.yml
handlers/
main.yml
templates/
nginx.conf.j2
files/
index.html
vars/
main.yml
defaults/
main.yml
meta/
main.yml
defaults vs vars
defaults/main.yml: Basse priorite. Les appelants peuvent facilement remplacer ces valeurs.vars/main.yml: Haute priorite. Ces valeurs remplacent la plupart des autres sources de variables.
# roles/webserver/defaults/main.yml
http_port: 80
https_port: 443
worker_processes: auto
document_root: /var/www/html
enable_ssl: false
# roles/webserver/tasks/main.yml
- name: Installer nginx
apt:
name: nginx
state: present
update_cache: true
- name: Creer le repertoire racine du document
file:
path: "{{ document_root }}"
state: directory
owner: www-data
group: www-data
mode: "0755"
- name: Deployer la configuration nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Redemarrer nginx
Utiliser les roles dans un playbook
- name: Configurer le niveau web
hosts: webservers
become: true
roles:
- role: common
- role: security
- role: webserver
vars:
http_port: 80
enable_ssl: true
document_root: /var/www/myapp
Ansible Galaxy pour les Roles Communautaires
ansible-galaxy install geerlingguy.mysql
ansible-galaxy install -r requirements.yml
# requirements.yml
roles:
- name: geerlingguy.nginx
version: "3.2.0"
collections:
- name: community.docker
version: ">=3.4.0"
Ansible Vault pour la Gestion des Secrets
ansible-vault create group_vars/all/vault.yml
ansible-vault edit group_vars/all/vault.yml
ansible-vault encrypt secrets.yml
ansible-vault encrypt_string 'MonSecret123' --name 'db_password'
# group_vars/all/vars.yml (texte brut, dans git)
db_host: db1.example.com
db_password: "{{ vault_db_password }}"
# group_vars/all/vault.yml (chiffre, dans git)
vault_db_password: "SuperSecret123!"
vault_api_key: "abcdef1234567890"
ansible-playbook site.yml --vault-password-file ~/.vault_pass
Gestion des Erreurs
ignore_errors et failed_when
- name: Verifier si l'application est installee
command: which myapp
register: myapp_check
ignore_errors: true
changed_when: false
- name: Installer l'application seulement si absente
apt:
name: myapp
state: present
when: myapp_check.rc != 0
- name: Executer le script de migration
command: /opt/myapp/migrate.sh
register: migration_result
failed_when: "'ERROR' in migration_result.stdout"
changed_when: "'Changes applied' in migration_result.stdout"
block / rescue / always
- name: Deployer l'application avec rollback
block:
- name: Arreter le service de l'application
service:
name: myapp
state: stopped
- name: Deployer la nouvelle version
git:
repo: https://github.com/org/myapp.git
dest: /opt/myapp
version: "{{ app_version }}"
- name: Demarrer le service de l'application
service:
name: myapp
state: started
rescue:
- name: Revenir a la version precedente
git:
repo: https://github.com/org/myapp.git
dest: /opt/myapp
version: "{{ previous_version }}"
- name: Notifier l'equipe de l'echec
debug:
msg: "Deploiement de {{ app_version }} echoue. Revenu a {{ previous_version }}."
always:
- name: Enregistrer la tentative de deploiement
shell: echo "{{ app_version }} tente le $(date)" >> /var/log/deployments.log
changed_when: false
Bonnes Pratiques d’Idempotence
- Preferez les modules a
shell/command: les modules verifient l’etat avant d’agir - Utilisez
createsaveccommand: ignore la commande si le fichier existe deja - Definissez
changed_when: falsesur les commandes qui ne font que lire l’etat - Utilisez
lineinfileplutot queshell echo >>pour modifier les fichiers de configuration - Evitez
apt: update_cache: truea chaque tache: utilisezcache_valid_time: 3600
Configuration d’ansible.cfg
[defaults]
inventory = ./inventory.ini
remote_user = deployer
private_key_file = ~/.ssh/id_ed25519
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml
roles_path = ./roles:~/.ansible/roles
forks = 10
[privilege_escalation]
become = True
become_method = sudo
become_user = root
Debogage et Tests
ansible-playbook site.yml --check --diff
ansible-playbook site.yml -vvv
ansible-playbook site.yml --syntax-check
- name: Afficher les variables resolues
debug:
msg: "App: {{ app_name }}, Port: {{ app_port }}"
- name: Capturer et inspecter la sortie d'une commande
command: systemctl status nginx
register: nginx_status
ignore_errors: true
changed_when: false
- name: Afficher la sortie de nginx
debug:
var: nginx_status.stdout_lines
Comparaison des Outils
| Fonctionnalite | Ansible | Terraform | Puppet | Chef | SaltStack |
|---|---|---|---|---|---|
| Usage principal | Gestion config + orchestration | Provisionnement infra | Gestion config | Gestion config | Config + exec distante |
| Architecture | Sans agent (SSH) | Sans agent (API) | Avec agent | Avec agent | Avec ou sans agent |
| Langage | YAML | HCL | Puppet DSL | Ruby | YAML / Python |
| Suivi d’etat | Sans etat | Fichier tfstate | PuppetDB | Chef server | Salt mine |
| Courbe d’apprentissage | Basse | Moyenne | Elevee | Elevee | Moyenne |
| Idempotent | Oui | Oui | Oui | Oui | Oui |
Ansible et Terraform sont complementaires: utilisez Terraform pour creer des ressources cloud, puis Ansible pour les configurer.
Exemple Reel: Deploiement d’une Pile LAMP
# lamp.yml
- name: Deployer la pile LAMP
hosts: webservers
become: true
vars:
php_version: "8.3"
mysql_root_password: "{{ vault_mysql_root_password }}"
mysql_db_name: myapp
mysql_db_user: appuser
mysql_db_password: "{{ vault_mysql_db_password }}"
tasks:
- name: Installer Apache et les paquets PHP
apt:
name:
- apache2
- "libapache2-mod-php{{ php_version }}"
- "php{{ php_version }}"
- "php{{ php_version }}-mysql"
- "php{{ php_version }}-curl"
- "php{{ php_version }}-mbstring"
state: present
update_cache: true
- name: Installer le serveur MySQL
apt:
name:
- mysql-server
- python3-pymysql
state: present
- name: Demarrer et activer MySQL
service:
name: mysql
state: started
enabled: true
- name: Creer la base de donnees de l'application
mysql_db:
login_user: root
login_password: "{{ mysql_root_password }}"
name: "{{ mysql_db_name }}"
state: present
- name: Activer mod_rewrite d'Apache
apache2_module:
name: rewrite
state: present
notify: Redemarrer Apache
- name: Cloner l'application depuis git
git:
repo: "{{ app_repo }}"
dest: /var/www/html
version: "{{ app_version }}"
force: true
- name: Definir la propriete du repertoire web
file:
path: /var/www/html
owner: www-data
group: www-data
recurse: true
- name: S'assurer qu'Apache est demarre et active
service:
name: apache2
state: started
enabled: true
handlers:
- name: Redemarrer Apache
service:
name: apache2
state: restarted
ansible-playbook -i inventory.ini lamp.yml --vault-password-file ~/.vault_pass
Resume
- L’architecture push sans agent d’Ansible signifie que vous avez seulement besoin d’un acces SSH pour commencer a automatiser
- L’inventaire (INI, YAML ou dynamique) dirige tout le ciblage des hotes;
group_varsethost_varsgardent les variables organisees - Les playbooks enchainent plays, taches, handlers et templates Jinja2 dans un flux de travail declaratif et lisible
- Les roles empaquettent taches, handlers, templates et defaults en unites reutilisables et partageables
- ansible-vault chiffre les secrets au repos; combinez les fichiers vault avec des fichiers de variables en texte brut pour garder l’historique git propre
block/rescue/alwaysfournit une gestion structuree des erreurs et une logique de rollback pour les deploiements en production- Validez toujours avec
--check --diffet-vvvavant d’executer en production
Articles Connexes
- Ansible pour Debutants: Automatisez la Configuration des Serveurs sans Agent
- Ansible Vault: Chiffrez et Gerez les Secrets en Toute Securite
- Depannage des Playbooks Ansible: Erreurs Courantes et Debogage
- Semaphore UI: Interface Web Ansible pour la Gestion des Playbooks
- Introduction a Terraform: Infrastructure en tant que Code