Aller au contenu
  1. Blog/

Apprendre Ansible : automatiser l'administration de vos serveurs

Sommaire
Apprendre Ansible - Cet article fait partie d'une série.
Partie 1: Cet article

Vous administrez plusieurs serveurs Linux et vous passez encore vos journées à enchaîner des commandes SSH à la main ? Vous maintenez des scripts Bash de 500 lignes que plus personne n’ose toucher ? Il est temps de passer à Ansible.

Dans ce premier chapitre, nous allons poser les fondations : comprendre ce qu’est Ansible, pourquoi il s’est imposé comme l’outil d’automatisation de référence, et surtout — écrire ensemble votre premier playbook fonctionnel.

Qu’est-ce qu’Ansible ?
#

Ansible est un outil open source d’automatisation IT développé initialement par Michael DeHaan en 2012, puis racheté par Red Hat en 2015. Il permet d’automatiser trois grandes familles de tâches :

  • La gestion de configuration — installer des paquets, gérer des fichiers de config, créer des utilisateurs
  • Le déploiement d’applications — pousser du code, redémarrer des services, orchestrer des mises à jour
  • L’orchestration d’infrastructure — provisionner des VMs, configurer des load balancers, gérer du cloud

Sa philosophie tient en trois mots : simple, agentless, idempotent.

Pourquoi pas juste des scripts Bash ?
#

Soyons honnêtes : un script Bash bien écrit peut faire beaucoup. Mais passé un certain seuil, les problèmes s’accumulent :

CritèreScript BashAnsible
IdempotenceÀ gérer manuellement (if/else partout)Native — un module ne refait pas ce qui est déjà fait
LisibilitéDépend du développeurYAML structuré, lisible par tous
Gestion d’erreursset -e et bricolageGestion intégrée, handlers, blocs rescue
Multi-serveursBoucles SSH fragilesParallélisme natif sur l’inventaire
MaintenanceCauchemar au-delà de 200 lignesRôles, variables, templates réutilisables
DocumentationLe script est la doc (en théorie)Le playbook est la doc (en pratique)

Un script Bash dit « exécute ces commandes ». Ansible dit « assure-toi que le système est dans cet état ». La différence est fondamentale.

Ansible vs Puppet vs Chef
#

Ansible n’est pas le seul outil de gestion de configuration. Puppet et Chef existent depuis plus longtemps. Voici ce qui les distingue :

Puppet utilise un langage déclaratif propriétaire (Puppet DSL) et repose sur un agent installé sur chaque machine cible. Il faut un Puppet Server central. La courbe d’apprentissage est raide.

Chef utilise Ruby comme langage de configuration. Il nécessite également un agent (chef-client) et un serveur central (Chef Infra Server). Puissant mais complexe.

Ansible utilise YAML — un format que n’importe quel ops peut lire en 5 minutes. Il ne nécessite aucun agent sur les machines cibles. Il se connecte en SSH, exécute ce qu’il faut, et repart. C’est cette simplicité qui explique son adoption massive.

En résumé : si vous démarrez aujourd’hui, Ansible est le choix le plus pragmatique. Pas d’infrastructure à maintenir pour l’outil lui-même, pas de langage à apprendre, et une communauté immense.

Architecture d’Ansible
#

Comprendre l’architecture d’Ansible, c’est comprendre pourquoi il est si simple à déployer.

Le modèle agentless
#

Contrairement à Puppet ou Chef, Ansible ne requiert aucun daemon sur les machines cibles. Tout ce qu’il lui faut :

  • Python installé sur les machines distantes (présent par défaut sur quasi toutes les distributions Linux)
  • Un accès SSH depuis la machine de contrôle

C’est tout. Pas de port supplémentaire à ouvrir, pas de certificat à gérer, pas de service à superviser. Votre machine de contrôle (votre laptop, un bastion, un runner CI) lance Ansible, qui se connecte en SSH, pousse un petit module Python, l’exécute, récupère le résultat, et nettoie derrière lui.

Les composants clés
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
┌─────────────────────────────────────────────┐
│           Machine de contrôle               │
│                                             │
│  ┌───────────┐  ┌───────────┐  ┌────────┐  │
│  │ Playbook  │  │ Inventaire│  │ Modules│  │
│  │  (YAML)   │  │  (hosts)  │  │(Python)│  │
│  └─────┬─────┘  └─────┬─────┘  └───┬────┘  │
│        │              │             │       │
│        └──────────┬───┘─────────────┘       │
│                   │                         │
│              SSH / WinRM                    │
└───────────────────┼─────────────────────────┘
        ┌───────────┼───────────┐
        │           │           │
   ┌────▼───┐  ┌───▼────┐  ┌──▼─────┐
   │ Web 01 │  │ Web 02 │  │ DB 01  │
   └────────┘  └────────┘  └────────┘
  • Playbook : fichier YAML décrivant l’état souhaité de vos machines. C’est votre « recette ».
  • Inventaire : liste des machines cibles, organisées en groupes.
  • Modules : unités de travail réutilisables (installer un paquet, copier un fichier, démarrer un service…). Ansible en fournit des milliers.
  • Tasks : une action unitaire dans un playbook (appel à un module avec des paramètres).
  • Handlers : tâches déclenchées par notification (ex : redémarrer Nginx après modification de sa config).
  • Rôles : structure de réutilisation qui regroupe tasks, handlers, templates, variables.
  • Facts : informations collectées automatiquement sur les machines cibles (OS, IP, RAM…).

Installation
#

Sur Debian / Ubuntu
#

1
2
sudo apt update
sudo apt install -y ansible

Cette méthode installe la version packagée par la distribution. Elle peut être un peu ancienne.

Via pip (recommandé)
#

Pour avoir la dernière version :

1
2
3
4
5
6
7
8
9
# Installer pip si nécessaire
sudo apt install -y python3-pip python3-venv

# Créer un environnement virtuel (bonne pratique)
python3 -m venv ~/ansible-env
source ~/ansible-env/bin/activate

# Installer Ansible
pip install ansible

Vérifier l’installation
#

1
ansible --version

Vous devriez voir quelque chose comme :

1
2
3
4
5
ansible [core 2.17.x]
  config file = None
  configured module search path = ['/home/user/.ansible/plugins/modules']
  ansible python module location = /home/user/ansible-env/lib/python3.x/site-packages/ansible
  python version = 3.x.x

Configurer SSH
#

Ansible utilise SSH. Assurez-vous que votre clé publique est déployée sur les machines cibles :

1
2
3
4
5
# Générer une paire de clés si vous n'en avez pas
ssh-keygen -t ed25519 -C "ansible@control"

# Copier la clé sur un serveur distant
ssh-copy-id user@192.168.1.10

L’inventaire
#

L’inventaire est le fichier qui dit à Ansible sur quelles machines travailler. C’est le point de départ de tout.

Inventaire statique
#

Le format le plus simple est le fichier INI. Créez un fichier inventory.ini :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[webservers]
web01 ansible_host=192.168.1.10
web02 ansible_host=192.168.1.11

[databases]
db01 ansible_host=192.168.1.20

[all:vars]
ansible_user=deploy
ansible_python_interpreter=/usr/bin/python3

Quelques explications :

  • [webservers] et [databases] sont des groupes
  • ansible_host spécifie l’adresse IP ou le FQDN
  • [all:vars] définit des variables pour tous les hôtes
  • ansible_user indique l’utilisateur SSH à utiliser

Vous pouvez aussi utiliser le format YAML (inventory.yml) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
all:
  vars:
    ansible_user: deploy
    ansible_python_interpreter: /usr/bin/python3
  children:
    webservers:
      hosts:
        web01:
          ansible_host: 192.168.1.10
        web02:
          ansible_host: 192.168.1.11
    databases:
      hosts:
        db01:
          ansible_host: 192.168.1.20

Inventaire dynamique
#

En environnement cloud, vos serveurs apparaissent et disparaissent. Maintenir un fichier statique devient vite pénible. Ansible supporte les inventaires dynamiques : des scripts ou plugins qui interrogent une API (AWS, Azure, GCP, VMware…) pour générer la liste des machines à la volée.

Exemple avec le plugin AWS EC2 — créez un fichier aws_ec2.yml :

1
2
3
4
5
6
7
8
plugin: amazon.aws.aws_ec2
regions:
  - eu-west-1
filters:
  tag:Environment: production
keyed_groups:
  - key: tags.Role
    prefix: role
1
2
# Vérifier que l'inventaire dynamique fonctionne
ansible-inventory -i aws_ec2.yml --list

Pour ce cours, nous utiliserons l’inventaire statique. L’inventaire dynamique sera couvert en détail dans un chapitre ultérieur.

Commandes ad-hoc
#

Avant d’écrire des playbooks, commençons par les commandes ad-hoc. Ce sont des commandes Ansible one-shot, parfaites pour des actions rapides.

Syntaxe
#

1
ansible <pattern> -i <inventaire> -m <module> -a "<arguments>"

Exemples pratiques
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Tester la connectivité (module ping)
ansible all -i inventory.ini -m ping

# Vérifier l'espace disque
ansible webservers -i inventory.ini -m command -a "df -h"

# Installer un paquet
ansible webservers -i inventory.ini -m apt -a "name=htop state=present" --become

# Copier un fichier
ansible all -i inventory.ini -m copy -a "src=./motd.txt dest=/etc/motd" --become

# Redémarrer un service
ansible webservers -i inventory.ini -m service -a "name=nginx state=restarted" --become

# Créer un utilisateur
ansible all -i inventory.ini -m user -a "name=deployer shell=/bin/bash state=present" --become

Le flag --become active l’élévation de privilèges (équivalent sudo).

Les commandes ad-hoc sont utiles pour du dépannage ou des actions ponctuelles. Pour tout ce qui doit être reproductible et versionné, on écrit un playbook.

Les modules essentiels
#

Ansible embarque des milliers de modules. Voici ceux que vous utiliserez quotidiennement :

apt / yum — Gestion des paquets
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Debian/Ubuntu
- name: Installer Nginx
  ansible.builtin.apt:
    name: nginx
    state: present
    update_cache: yes

# RHEL/CentOS
- name: Installer Nginx
  ansible.builtin.yum:
    name: nginx
    state: present

Les états possibles : present (installé), absent (désinstallé), latest (dernière version).

copy — Copier des fichiers
#

1
2
3
4
5
6
7
8
- name: Déployer le fichier de configuration
  ansible.builtin.copy:
    src: files/nginx.conf
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: "0644"
  notify: Redémarrer Nginx

template — Fichiers dynamiques avec Jinja2
#

1
2
3
4
5
6
7
- name: Déployer la config Nginx depuis un template
  ansible.builtin.template:
    src: templates/vhost.conf.j2
    dest: /etc/nginx/sites-available/monsite.conf
    owner: root
    group: root
    mode: "0644"

Le template Jinja2 correspondant (vhost.conf.j2) :

server {
    listen 80;
    server_name {{ domain_name }};

    root {{ document_root }};
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

La différence avec copy : template traite les variables Jinja2 ({{ }}) avant de déposer le fichier.

service — Gérer les services
#

1
2
3
4
5
- name: Démarrer et activer Nginx
  ansible.builtin.service:
    name: nginx
    state: started
    enabled: yes

États possibles : started, stopped, restarted, reloaded.

user — Gérer les utilisateurs
#

1
2
3
4
5
6
7
- name: Créer l'utilisateur deployer
  ansible.builtin.user:
    name: deployer
    shell: /bin/bash
    groups: sudo
    append: yes
    create_home: yes

Votre premier playbook : déployer Nginx
#

Passons à la pratique. Nous allons écrire un playbook complet qui installe et configure Nginx sur les serveurs du groupe webservers.

Structure du projet
#

1
2
3
4
5
6
7
projet-ansible/
├── inventory.ini
├── playbook.yml
├── files/
│   └── index.html
└── templates/
    └── nginx-default.conf.j2

Le fichier d’inventaire
#

1
2
3
4
5
6
7
8
# inventory.ini
[webservers]
web01 ansible_host=192.168.1.10
web02 ansible_host=192.168.1.11

[webservers:vars]
ansible_user=deploy
ansible_python_interpreter=/usr/bin/python3

Le template Nginx
#

# templates/nginx-default.conf.j2
server {
    listen 80 default_server;
    listen [::]:80 default_server;

    root /var/www/{{ site_name }};
    index index.html;

    server_name {{ ansible_hostname }};

    location / {
        try_files $uri $uri/ =404;
    }
}

La page d’accueil
#

1
2
3
4
5
6
7
8
9
<!-- files/index.html -->
<!DOCTYPE html>
<html>
<head><title>Déployé par Ansible</title></head>
<body>
    <h1>Ansible fonctionne !</h1>
    <p>Ce serveur a été configuré automatiquement.</p>
</body>
</html>

Le playbook
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# playbook.yml
---
- name: Déployer Nginx sur les serveurs web
  hosts: webservers
  become: yes
  vars:
    site_name: monsite

  tasks:
    - name: Mettre à jour le cache apt
      ansible.builtin.apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Installer Nginx
      ansible.builtin.apt:
        name: nginx
        state: present

    - name: Créer le répertoire du site
      ansible.builtin.file:
        path: "/var/www/{{ site_name }}"
        state: directory
        owner: www-data
        group: www-data
        mode: "0755"

    - name: Déployer la page d'accueil
      ansible.builtin.copy:
        src: files/index.html
        dest: "/var/www/{{ site_name }}/index.html"
        owner: www-data
        group: www-data
        mode: "0644"

    - name: Configurer le vhost Nginx
      ansible.builtin.template:
        src: templates/nginx-default.conf.j2
        dest: /etc/nginx/sites-available/default
        owner: root
        group: root
        mode: "0644"
      notify: Recharger Nginx

    - name: Activer le site par défaut
      ansible.builtin.file:
        src: /etc/nginx/sites-available/default
        dest: /etc/nginx/sites-enabled/default
        state: link
      notify: Recharger Nginx

    - name: S'assurer que Nginx est démarré et activé
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: yes

  handlers:
    - name: Recharger Nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

Exécuter le playbook
#

1
2
3
4
5
6
7
8
# Vérifier la syntaxe
ansible-playbook playbook.yml -i inventory.ini --syntax-check

# Lancer en mode dry-run (check mode)
ansible-playbook playbook.yml -i inventory.ini --check --diff

# Exécuter pour de vrai
ansible-playbook playbook.yml -i inventory.ini

Le flag --check simule l’exécution sans rien modifier. Le flag --diff affiche les différences sur les fichiers modifiés. Prenez l’habitude de toujours faire un dry-run avant d’appliquer.

Comprendre la sortie
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
PLAY [Déployer Nginx sur les serveurs web] ************************************

TASK [Gathering Facts] ********************************************************
ok: [web01]
ok: [web02]

TASK [Installer Nginx] ********************************************************
changed: [web01]
changed: [web02]

TASK [Déployer la page d'accueil] *********************************************
changed: [web01]
changed: [web02]

PLAY RECAP ********************************************************************
web01  : ok=7  changed=5  unreachable=0  failed=0  skipped=0
web02  : ok=7  changed=5  unreachable=0  failed=0  skipped=0
  • ok : la tâche s’est exécutée, rien n’a changé (état déjà conforme)
  • changed : la tâche a modifié quelque chose sur la machine
  • unreachable : impossible de se connecter
  • failed : la tâche a échoué

Relancez le même playbook : tout passera en ok au lieu de changed. C’est l’idempotence en action.

Exercices pratiques
#

Exercice 1 — Tester la connectivité
#

Créez un inventaire avec au moins une machine (ça peut être localhost) et vérifiez la connectivité :

1
2
# Pour tester en local sans SSH
ansible localhost -m ping -c local

Exercice 2 — Commandes ad-hoc
#

Utilisez des commandes ad-hoc pour :

  1. Afficher l’uptime de toutes vos machines
  2. Vérifier la version de Python installée
  3. Créer un fichier /tmp/ansible-test.txt avec le contenu « Hello from Ansible »
1
2
3
ansible all -i inventory.ini -m command -a "uptime"
ansible all -i inventory.ini -m command -a "python3 --version"
ansible all -i inventory.ini -m copy -a "content='Hello from Ansible\n' dest=/tmp/ansible-test.txt"

Exercice 3 — Modifier le playbook Nginx
#

Partez du playbook fourni dans ce chapitre et ajoutez :

  1. L’installation du paquet curl en plus de Nginx
  2. Une tâche qui vérifie que le site répond (module uri)
  3. Un handler qui redémarre Nginx (pas seulement reload)

Indice pour la vérification :

1
2
3
4
- name: Vérifier que le site répond
  ansible.builtin.uri:
    url: "http://localhost"
    status_code: 200

Exercice 4 — Variables et template
#

Créez un playbook qui :

  1. Définit une variable message avec la valeur de votre choix
  2. Utilise un template Jinja2 pour générer un fichier /var/www/html/index.html contenant ce message
  3. S’assure que Nginx sert cette page

C’est l’occasion de pratiquer la différence entre copy et template.

Bonnes pratiques pour bien démarrer
#

Avant de passer au chapitre suivant, quelques règles à intégrer dès maintenant :

  1. Nommez toutes vos tâches. Un name: explicite en français ou anglais rend le playbook lisible et la sortie compréhensible.
  2. Utilisez --check --diff systématiquement avant d’appliquer en production.
  3. Versionnez vos playbooks dans Git dès le premier jour.
  4. Une tâche = une action. Ne combinez pas plusieurs opérations dans une seule tâche.
  5. Utilisez les FQCN (Fully Qualified Collection Names) comme ansible.builtin.apt plutôt que juste apt. C’est la convention moderne et ça évite les ambiguïtés.

Récapitulatif
#

Dans ce premier chapitre, vous avez appris :

  • Ce qu’est Ansible et pourquoi il surpasse les scripts Bash et les outils à agent
  • L’architecture agentless : SSH, pas d’agent, pas de serveur central
  • L’installation via pip ou apt
  • L’inventaire statique (INI / YAML) et le concept d’inventaire dynamique
  • Les commandes ad-hoc pour des actions rapides
  • Les modules essentiels : apt, yum, copy, template, service, user
  • Votre premier playbook complet pour déployer Nginx

Dans le prochain chapitre, nous approfondirons les variables, les facts et les templates Jinja2 pour rendre vos playbooks véritablement dynamiques.


Cet article fait partie de la série Apprendre Ansible sur devopslab.ch — des cours DevOps gratuits, en français, pensés pour la pratique.

Apprendre Ansible - Cet article fait partie d'une série.
Partie 1: Cet article

Articles connexes