1. Organiser un projet Ansible #
De quelques fichiers à un dépôt industrialisé.
Structure de référence #
projet-infra/
├── ansible.cfg
├── requirements.yml
├── site.yml # point d'entrée : importe les playbooks
├── playbooks/
│ ├── loadbalancers.yml
│ ├── webservers.yml
│ └── dbservers.yml
├── inventories/
│ ├── lab/ {hosts.ini, group_vars/, host_vars/}
│ └── prod/ {hosts.ini, group_vars/, host_vars/}
└── roles/
├── common/ nginx/ haproxy/ postgresql/# site.yml
- import_playbook: playbooks/dbservers.yml
- import_playbook: playbooks/webservers.yml
- import_playbook: playbooks/loadbalancers.yml2. Un playbook de production #
Variables, handlers, tags et conditions.
Exemple complet #
# playbooks/webservers.yml
- name: Configurer les serveurs web
hosts: webservers
become: true
gather_facts: true
vars:
app_version: "1.4.2"
roles:
- role: common
tags: [common]
- role: nginx
tags: [nginx]
pre_tasks:
- name: Retirer le serveur du load balancer
ansible.builtin.uri:
url: "http://lb.lab:8404/disable/{{ inventory_hostname }}"
method: POST
delegate_to: localhost
tasks:
- name: Déployer l'application
ansible.builtin.unarchive:
src: "https://artifacts.lab/app-{{ app_version }}.tgz"
dest: /opt/app
remote_src: true
notify: Redémarrer l'application
tags: [deploy]
post_tasks:
- name: Vérifier que l'application répond
ansible.builtin.uri:
url: "http://localhost:8080/health"
status_code: 200
retries: 10
delay: 3
register: health
until: health.status == 200
handlers:
- name: Redémarrer l'application
ansible.builtin.systemd:
name: app
state: restartedExécuter, simuler, cibler #
ansible-playbook site.yml --syntax-check
ansible-playbook site.yml --check --diff # dry-run + différences
ansible-playbook site.yml -i inventories/prod --limit web1.lab
ansible-playbook site.yml --tags deploy
ansible-playbook site.yml --skip-tags common
ansible-playbook site.yml --start-at-task "Déployer l'application"
ansible-playbook site.yml -vvv # débogage détaillé
ansible-playbook site.yml --list-hosts --list-tasks3. Déploiements progressifs et résilients #
serial, blocs et gestion des erreurs.
Rolling update avec serial #
- hosts: webservers
serial: "25%" # ou 2, ou [1, 5, 10] pour des vagues croissantes
max_fail_percentage: 10 # arrêter si plus de 10 % des hôtes échouent
tasks:
- name: Mettre à jour l'application
ansible.builtin.package: { name: app, state: latest }Bonne Pratique : Avec serial, les vagues se déroulent l'une après l'autre : une erreur sur la première vague empêche de propager un mauvais déploiement à toute la production.block / rescue / always #
- name: Déploiement avec retour arrière
block:
- name: Appliquer la nouvelle version
ansible.builtin.command: /opt/app/deploy.sh {{ app_version }}
- name: Smoke test
ansible.builtin.uri: { url: "http://localhost:8080/health" }
rescue:
- name: Restaurer la version précédente
ansible.builtin.command: /opt/app/rollback.sh
- name: Notifier l'équipe
community.general.slack: { token: "{{ vault_slack_token }}", msg: "Déploiement échoué sur {{ inventory_hostname }}" }
delegate_to: localhost
always:
- name: Journaliser l'opération
ansible.builtin.lineinfile: { path: /var/log/deploys.log, line: "{{ ansible_date_time.iso8601 }} {{ app_version }}", create: true }Délégation et exécution unique #
- name: Migrer le schéma (une seule fois pour tout le groupe)
ansible.builtin.command: /opt/app/migrate.sh
run_once: true
delegate_to: "{{ groups['dbservers'][0] }}"
- name: Enregistrer le déploiement dans le CMDB
ansible.builtin.uri: { url: "https://cmdb.lab/api/deployments", method: POST, body_format: json, body: { host: "{{ inventory_hostname }}" } }
delegate_to: localhostErreur Courante :commandetshellne sont pas idempotents : ajoutezcreates:,removes:ouchanged_when:pour que le playbook reste rejouable.
