Ansible : guide complet de l'automatisation d'infrastructure

CI/CD Culture DevOps Python
Ansible : guide complet de l'automatisation d'infrastructure

Si vous avez déjà passé des heures à configurer manuellement des dizaines de serveurs, vous savez à quel point ce processus peut être fastidieux et source d'erreurs. Un oubli par-ci, une typo par-là, et voilà que votre infrastructure devient incohérente. C'est exactement le problème qu'Ansible résout de manière élégante.

Ansible est un outil d'automatisation open-source qui vous permet de gérer vos serveurs, déployer des applications et orchestrer des configurations complexes à partir de simples fichiers YAML. Contrairement à d'autres solutions comme Puppet ou Chef, Ansible ne nécessite aucun agent sur les machines distantes. Tout fonctionne via SSH, ce qui simplifie drastiquement son adoption.

Dans cet article, je vais vous guider à travers les fondamentaux d'Ansible, de l'installation jusqu'aux concepts avancés comme les roles et les plugins. Que vous gériez trois serveurs ou trois cents, ces connaissances vous feront gagner un temps précieux.

Installation d'Ansible

L'installation d'Ansible est remarquablement simple. Sur la plupart des distributions Linux, quelques commandes suffisent. Ansible s'installe uniquement sur votre machine de contrôle (celle depuis laquelle vous lancez les commandes), pas besoin de l'installer sur les serveurs cibles.

Sur Ubuntu ou Debian, commencez par mettre à jour votre cache de paquets puis installez Ansible :

sudo apt update
sudo apt install ansible -y

Pour CentOS, RHEL ou Fedora, utilisez dnf ou yum :

sudo dnf install ansible -y
# ou pour les versions plus anciennes
sudo yum install ansible -y

Si vous préférez avoir la dernière version d'Ansible (recommandé), utilisez pip qui est souvent plus à jour que les dépôts système :

pip install ansible --user
# Ou dans un environnement virtuel
python3 -m venv ansible-env
source ansible-env/bin/activate
pip install ansible

Vérifiez que l'installation s'est bien déroulée avec :

ansible --version

Cette commande affiche la version installée ainsi que des informations sur l'emplacement des fichiers de configuration. Si vous voyez un numéro de version, félicitations, Ansible est prêt à être utilisé !

Initialisation et configuration de base

Maintenant qu'Ansible est installé, il faut lui indiquer quels serveurs vous souhaitez gérer. C'est là qu'intervient le fichier d'inventaire. Par défaut, Ansible cherche ce fichier dans /etc/ansible/hosts, mais vous pouvez en créer un local dans votre répertoire de travail.

Créez un répertoire pour vos projets Ansible :

mkdir ~/ansible-project
cd ~/ansible-project

Créez ensuite un fichier d'inventaire inventory.ini qui liste vos serveurs :

[webservers]
web1.example.com ansible_host=192.168.1.10
web2.example.com ansible_host=192.168.1.11

[databases]
db1.example.com ansible_host=192.168.1.20

[production:children]
webservers
databases

[all:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/id_rsa

Dans cet exemple, j'ai créé deux groupes : webservers et databases. Le groupe production est un méta-groupe qui contient les deux précédents. La section all:vars définit des variables communes à tous les hôtes.

Testez la connectivité avec le module ping d'Ansible (qui ne fait pas exactement un ping ICMP, mais vérifie que vous pouvez exécuter des commandes Python sur les hôtes) :

ansible all -i inventory.ini -m ping

Si tout fonctionne, vous devriez voir un retour JSON avec "ping": "pong" pour chaque serveur. Sinon, vérifiez vos clés SSH et que vous pouvez vous connecter manuellement avec ssh utilisateur@serveur.

Votre premier playbook Ansible

Les playbooks sont le cœur d'Ansible. Ce sont des fichiers YAML qui décrivent l'état souhaité de vos systèmes. Plutôt que d'exécuter des commandes ad-hoc, vous écrivez un playbook qui peut être versionné, testé et rejoué autant de fois que nécessaire.

Créons un playbook simple qui installe Nginx sur vos serveurs web et s'assure qu'il tourne. Créez un fichier webserver.yml :

---
- name: Configuration des serveurs web
  hosts: webservers
  become: yes

  tasks:
    - name: Installer Nginx
      apt:
        name: nginx
        state: present
        update_cache: yes
      when: ansible_os_family == "Debian"

    - name: S'assurer que Nginx est démarré
      service:
        name: nginx
        state: started
        enabled: yes

    - name: Copier une page d'accueil personnalisée
      copy:
        content: |
          <html>
          <head><title>Bienvenue</title></head>
          <body>
            <h1>Ce serveur est géré par Ansible</h1>
            <p>Hostname: {{ ansible_hostname }}</p>
          </body>
          </html>
        dest: /var/www/html/index.html
        owner: www-data
        group: www-data
        mode: '0644'

    - name: Configurer le pare-feu
      ufw:
        rule: allow
        port: '80'
        proto: tcp
      when: ansible_os_family == "Debian"

Ce playbook fait plusieurs choses intéressantes. D'abord, la directive become: yes indique qu'Ansible doit utiliser sudo pour obtenir les privilèges root. Ensuite, chaque tâche est clairement nommée, ce qui rend les logs faciles à lire.

Notez l'utilisation de when: pour exécuter certaines tâches uniquement sur les systèmes Debian/Ubuntu. La variable ansible_os_family est collectée automatiquement par Ansible via ce qu'on appelle les "facts".

Exécutez ce playbook avec :

ansible-playbook -i inventory.ini webserver.yml

Ansible va se connecter à chaque serveur web, vérifier si Nginx est installé, l'installer si nécessaire, s'assurer qu'il tourne, copier votre page HTML et configurer le firewall. La beauté de l'approche déclarative, c'est que vous pouvez relancer ce playbook autant de fois que vous voulez : si tout est déjà dans l'état souhaité, Ansible ne fera rien (on dit que c'est "idempotent").

Les Roles : organiser votre code Ansible

À mesure que vos playbooks grandissent, ils deviennent difficiles à maintenir. Les roles permettent de structurer votre code en composants réutilisables. Un role regroupe des tâches, des variables, des fichiers et des templates liés à une fonction spécifique.

Imaginons que vous voulez créer un role "nginx". La structure serait :

roles/
└── nginx/
    ├── tasks/
    │   └── main.yml
    ├── handlers/
    │   └── main.yml
    ├── templates/
    │   └── nginx.conf.j2
    ├── files/
    │   └── ssl-cert.pem
    ├── vars/
    │   └── main.yml
    └── defaults/
        └── main.yml

Créez cette structure avec :

ansible-galaxy init roles/nginx

Dans roles/nginx/tasks/main.yml, mettez vos tâches d'installation Nginx :

---
- name: Installer Nginx
  apt:
    name: nginx
    state: present
  notify: Redémarrer Nginx

- name: Copier la configuration
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: Redémarrer Nginx

Les handlers dans roles/nginx/handlers/main.yml sont des tâches qui ne s'exécutent que si elles sont notifiées :

---
- name: Redémarrer Nginx
  service:
    name: nginx
    state: restarted

Pour utiliser ce role dans un playbook :

---
- name: Serveurs web avec role
  hosts: webservers
  become: yes

  roles:
    - nginx

L'avantage ? Vous pouvez maintenant réutiliser votre role nginx dans n'importe quel playbook, le partager avec d'autres projets, ou même le publier sur Ansible Galaxy pour que la communauté en bénéficie.

Les Collections : le nouveau format d'empaquetage

Depuis Ansible 2.9, les collections sont devenues le format standard pour distribuer du contenu Ansible. Une collection peut contenir des roles, des modules, des plugins et de la documentation, le tout dans un package cohérent.

Par exemple, pour gérer du matériel réseau Cisco, vous installeriez la collection officielle :

ansible-galaxy collection install cisco.ios

Cette commande télécharge la collection depuis Ansible Galaxy et l'installe dans ~/.ansible/collections. Vous pouvez ensuite utiliser ses modules dans vos playbooks :

---
- name: Configurer un switch Cisco
  hosts: switches
  gather_facts: no

  tasks:
    - name: Configurer un VLAN
      cisco.ios.ios_vlans:
        config:
          - vlan_id: 100
            name: PROD
        state: merged

Les collections apportent plusieurs avantages. Premièrement, elles permettent aux éditeurs (comme Red Hat, Cisco, AWS) de publier leurs propres modules indépendamment du cycle de release d'Ansible. Deuxièmement, elles évitent les conflits de noms grâce aux namespaces (cisco.ios, amazon.aws, etc.).

Vous pouvez créer vos propres collections pour organiser votre code interne :

ansible-galaxy collection init monentreprise.infrastructure

Les Libraries (modules personnalisés)

Ansible fournit des centaines de modules prêts à l'emploi, mais parfois vous avez besoin de quelque chose de spécifique à votre environnement. C'est là que les modules personnalisés entrent en jeu.

Un module Ansible est un script (généralement en Python) qui reçoit des paramètres, effectue une action et retourne un résultat JSON. Créons un module simple qui vérifie si un service Docker particulier tourne.

Dans votre projet, créez un répertoire library/ et ajoutez-y un fichier check_docker_service.py :

#!/usr/bin/python
from ansible.module_utils.basic import AnsibleModule
import subprocess

def check_service(name):
    try:
        result = subprocess.run(
            ['docker', 'service', 'ls', '--filter', f'name={name}', '--format', '{{.Replicas}}'],
            capture_output=True,
            text=True,
            check=True
        )
        return result.stdout.strip()
    except subprocess.CalledProcessError:
        return None

def main():
    module = AnsibleModule(
        argument_spec=dict(
            name=dict(type='str', required=True)
        ),
        supports_check_mode=True
    )

    service_name = module.params['name']
    replicas = check_service(service_name)

    if replicas:
        module.exit_json(changed=False, replicas=replicas, running=True)
    else:
        module.exit_json(changed=False, running=False)

if __name__ == '__main__':
    main()

Utilisez ce module dans un playbook :

- name: Vérifier le service Docker
  check_docker_service:
    name: mon-api
  register: service_status

- name: Afficher le statut
  debug:
    msg: "Le service a {{ service_status.replicas }} réplicas"
  when: service_status.running

Ansible cherche automatiquement les modules dans le répertoire library/ à côté de votre playbook. C'est parfait pour encapsuler des logiques métier complexes que vous réutilisez souvent.

Les Lookup Plugins : récupérer des données externes

Les lookup plugins permettent de récupérer des données depuis des sources externes pendant l'exécution d'un playbook. Ansible en fournit plusieurs par défaut, et vous pouvez créer les vôtres.

Un cas d'usage courant : récupérer des secrets depuis un gestionnaire de mots de passe. Par exemple, avec le lookup password :

- name: Créer un utilisateur avec mot de passe généré
  user:
    name: deployuser
    password: "{{ lookup('password', '/tmp/deployuser-password chars=ascii_letters,digits length=16') | password_hash('sha512') }}"
    update_password: on_create

Le plugin file lit le contenu d'un fichier :

- name: Déployer une clé API
  copy:
    content: "{{ lookup('file', '/secure/api-key.txt') }}"
    dest: /etc/app/api-key
    mode: '0600'

Le lookup env récupère des variables d'environnement :

- name: Utiliser le proxy de l'environnement
  set_fact:
    http_proxy: "{{ lookup('env', 'HTTP_PROXY') }}"

Pour les environnements cloud, le lookup aws_ssm (dans la collection amazon.aws) récupère des paramètres depuis AWS Systems Manager :

- name: Récupérer une chaîne de connexion DB
  debug:
    msg: "{{ lookup('aws_ssm', '/prod/database/connection-string', region='eu-west-1') }}"

Vous pouvez même créer vos propres lookup plugins pour interroger vos systèmes internes, votre CMDB, ou toute API propriétaire. Placez-les dans lookup_plugins/ à côté de votre playbook.

Les Callback Plugins : personnaliser la sortie d'Ansible

Les callback plugins contrôlent comment Ansible affiche les résultats de l'exécution. Par défaut, vous voyez une sortie textuelle colorée dans le terminal, mais vous pouvez complètement personnaliser ce comportement.

Ansible inclut plusieurs callbacks intéressants. Le callback timer affiche combien de temps a pris chaque tâche. Activez-le dans ansible.cfg :

[defaults]
callbacks_enabled = timer

Le callback profile_tasks est encore plus utile : il trie les tâches par durée d'exécution, ce qui aide à identifier les goulots d'étranglement :

[defaults]
callbacks_enabled = profile_tasks

Pour les environnements CI/CD, le callback junit génère un rapport XML au format JUnit que Jenkins ou GitLab CI peuvent interpréter :

[defaults]
callbacks_enabled = junit
junit_output_dir = ./test-results

Vous pouvez créer vos propres callbacks pour envoyer des notifications Slack, écrire dans une base de données, ou générer des rapports personnalisés. Créez un fichier dans callback_plugins/ :

from ansible.plugins.callback import CallbackBase

class CallbackModule(CallbackBase):
    CALLBACK_VERSION = 2.0
    CALLBACK_TYPE = 'notification'
    CALLBACK_NAME = 'custom_notifier'

    def v2_playbook_on_stats(self, stats):
        hosts = sorted(stats.processed.keys())
        summary = []

        for host in hosts:
            s = stats.summarize(host)
            summary.append(f"{host}: ok={s['ok']} changed={s['changed']} unreachable={s['unreachable']} failed={s['failed']}")

        # Ici vous pourriez envoyer ces stats à Slack, DataDog, etc.
        self._display.display("Résumé personnalisé: " + ", ".join(summary))

Les callbacks sont puissants pour intégrer Ansible dans votre écosystème d'outils existant.

Conclusion

Vous voilà maintenant équipé des fondamentaux et de concepts avancés d'Ansible. De l'installation jusqu'aux plugins personnalisés, vous avez vu comment automatiser efficacement la gestion de votre infrastructure.

Ce qui rend Ansible particulièrement intéressant, c'est sa courbe d'apprentissage progressive. Vous pouvez commencer avec de simples playbooks en quelques minutes, puis progressivement adopter les roles pour la réutilisabilité, les collections pour l'organisation à grande échelle, et les plugins pour personnaliser complètement le comportement.

Quelques conseils pour la suite de votre parcours :

  • Versionner systématiquement vos playbooks et roles dans Git
  • Utiliser Ansible Vault pour chiffrer les données sensibles (mots de passe, clés API)
  • Tester vos playbooks avec Molecule avant de les appliquer en production
  • Explorer Ansible Tower/AWX pour une interface web et un contrôle d'accès avancé
  • Rejoindre la communauté sur les forums et contribuer à Ansible Galaxy

L'automatisation avec Ansible transforme radicalement votre manière de travailler. Finis les scripts shell fragiles éparpillés dans différents repos. Tout devient déclaratif, versionné, testable et documenté. Vos configurations deviennent du code, et comme tout code, elles bénéficient des meilleures pratiques du génie logiciel.

N'hésitez pas à expérimenter, à casser des choses dans des environnements de test, et à itérer. C'est comme ça qu'on apprend vraiment. Et surtout, pensez toujours "idempotence" : vos playbooks doivent pouvoir s'exécuter plusieurs fois sans effet de bord indésirable.

Ressources complémentaires

Retour aux articles DevOps