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.

ANSIBLE — PLAYBOOKS, ROLES ET VAULT Inventaire INI / YAML group_vars/ host_vars/ Playbook plays / taches handlers / vars Moteur Ansible Roles / Modules Vault / Jinja2 SSH webservers nginx / php dbservers mysql / backup loadbalancers haproxy / certs ansible-vault Secrets chiffres AES-256 Roles tasks / handlers templates / files defaults / meta Modele push sans agent — l'inventaire pilote tout

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.

  1. Ansible lit l’inventaire pour determiner quels hotes cibler
  2. Il analyse le playbook pour construire une liste ordonnee de taches
  3. Pour chaque tache, il copie un petit module Python sur l’hote distant via SSH, l’execute, capture le resultat et supprime le module
  4. 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

ModuleObjectifParametres cles
apt / yumGestion des paquetsname, state, update_cache
copyCopier des fichiers statiquessrc, dest, owner, mode
templateDeployer des templates Jinja2src, dest, owner, mode
serviceGerer les services systemename, state, enabled
userGerer les comptes utilisateursname, groups, shell, state
fileCreer des dossiers, lienspath, state, owner, mode
lineinfileModifier des lignes dans un fichierpath, regexp, line, state
commandExecuter une commandeargv, creates
shellCommandes shell avec pipescmd, chdir
gitCloner ou mettre a jour un depotrepo, dest, version
docker_containerGerer des conteneurs Dockername, image, state, ports
debugAfficher des valeurs de variablesmsg, 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 creates avec command: ignore la commande si le fichier existe deja
  • Definissez changed_when: false sur les commandes qui ne font que lire l’etat
  • Utilisez lineinfile plutot que shell echo >> pour modifier les fichiers de configuration
  • Evitez apt: update_cache: true a chaque tache: utilisez cache_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

FonctionnaliteAnsibleTerraformPuppetChefSaltStack
Usage principalGestion config + orchestrationProvisionnement infraGestion configGestion configConfig + exec distante
ArchitectureSans agent (SSH)Sans agent (API)Avec agentAvec agentAvec ou sans agent
LangageYAMLHCLPuppet DSLRubyYAML / Python
Suivi d’etatSans etatFichier tfstatePuppetDBChef serverSalt mine
Courbe d’apprentissageBasseMoyenneEleveeEleveeMoyenne
IdempotentOuiOuiOuiOuiOui

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_vars et host_vars gardent 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/always fournit une gestion structuree des erreurs et une logique de rollback pour les deploiements en production
  • Validez toujours avec --check --diff et -vvv avant d’executer en production

Articles Connexes