Leçon 6 / 6
Leçon 06 · Ansible

Projet récap

Ce qu'on va construire

On assemble tout ce qu'on a appris pour déployer un serveur web complet de façon entièrement automatisée avec Ansible. L'architecture cible : Nginx comme reverse proxy devant une application Node.js, le tout géré par systemd.

  • Un inventaire avec le groupe web_servers
  • Deux rôles séparés : nginx et nodejs
  • Des variables pour personnaliser port, domaine et chemin
  • Un playbook principal site.yml qui orchestre tout

Ce projet reprend tous les concepts vus dans le module : inventaires (leçon 03), playbooks (leçon 04), rôles et variables (leçon 05). Si un concept te semble flou, relis la leçon correspondante avant de continuer.

Structure du projet

Voici l'arborescence complète du projet. Crée ces fichiers et dossiers avant de commencer à écrire le contenu.

Arborescence du projet Ansible
deploy-web/
├── inventory/
│   └── hosts.ini               # liste des serveurs cibles
├── group_vars/
│   └── web_servers.yml         # variables communes au groupe
├── roles/
│   ├── nginx/
│   │   ├── tasks/
│   │   │   └── main.yml        # installer + configurer Nginx
│   │   └── templates/
│   │       └── vhost.conf.j2   # template Jinja2 du vhost
│   └── nodejs/
│       ├── tasks/
│       │   └── main.yml        # installer Node + copier l'app
│       └── files/
│           └── app.js          # le code de l'application
└── site.yml                    # playbook principal

L'inventaire

Commence par déclarer ton serveur cible dans inventory/hosts.ini. Remplace l'adresse IP par celle de ton propre serveur.

inventory/hosts.ini
[web_servers]
192.168.1.10 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_ed25519

[web_servers:vars]
ansible_python_interpreter=/usr/bin/python3

Les variables

Les variables communes au groupe sont centralisées dans group_vars/web_servers.yml. Tu peux surcharger n'importe laquelle dans les host_vars/ pour un serveur spécifique.

group_vars/web_servers.yml
---
# Nginx
app_domain:  monapp.exemple.fr
nginx_port:  80

# Node.js
node_app_port:   3000
node_app_path:   /opt/monapp
node_app_user:   deploy
node_service_name: monapp

Le playbook principal — site.yml

Le playbook principal est minimaliste : il délègue tout le travail aux rôles. C'est la bonne pratique — le playbook orchestraire, les rôles font.

site.yml — playbook principal
---
- name: Déployer le serveur web complet
  hosts: web_servers
  become: true

  roles:
    - role: nodejs
    - role: nginx
💡

L'ordre des rôles est important : nodejs avant nginx, car Nginx a besoin que l'application soit déjà en écoute sur le port 3000 pour que le check de santé au démarrage réussisse.

Rôle nodejs — installer et déployer l'application

Ce rôle installe Node.js, crée l'utilisateur système, copie le code de l'application et la démarre comme un service systemd.

roles/nodejs/tasks/main.yml
---
- name: Installer Node.js et npm (via NodeSource)
  ansible.builtin.shell:
    cmd: curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
    creates: /etc/apt/sources.list.d/nodesource.list

- name: Installer le paquet nodejs
  ansible.builtin.apt:
    name: nodejs
    state: present
    update_cache: true

- name: Créer l'utilisateur système 
  ansible.builtin.user:
    name: ""
    system: true
    shell: /usr/sbin/nologin
    create_home: false

- name: Créer le répertoire de l'application
  ansible.builtin.file:
    path:  ""
    state: directory
    owner: ""
    mode:  "0755"

- name: Copier le code de l'application
  ansible.builtin.copy:
    src:   app.js
    dest:  "/app.js"
    owner: ""
    mode:  "0644"
  notify: Redémarrer monapp

- name: Déployer le service systemd
  ansible.builtin.template:
    src:  monapp.service.j2
    dest: "/etc/systemd/system/.service"
    mode: "0644"
  notify: Redémarrer monapp

- name: Activer et démarrer le service
  ansible.builtin.systemd:
    name:           ""
    state:          started
    enabled:        true
    daemon_reload:  true

L'application Node.js — roles/nodejs/files/app.js

Pour ce projet, l'application est volontairement simple : un serveur HTTP qui répond sur le port configuré.

roles/nodejs/files/app.js
const http = require('http');

const PORT = process.env.PORT || 3000;

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Bonjour depuis Node.js — déployé par Ansible !\n');
});

server.listen(PORT, () => {
  console.log(`Serveur démarré sur le port ${PORT}`);
});

Le service systemd — roles/nodejs/templates/monapp.service.j2

roles/nodejs/templates/monapp.service.j2
[Unit]
Description= — Application Node.js
After=network.target

[Service]
Type=simple
User=
WorkingDirectory=
ExecStart=/usr/bin/node /app.js
Environment=PORT=
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

Rôle nginx — reverse proxy devant Node.js

Ce rôle installe Nginx, génère la configuration du virtual host à partir d'un template Jinja2, active le site et recharge Nginx.

roles/nginx/tasks/main.yml
---
- name: Installer Nginx
  ansible.builtin.apt:
    name:         nginx
    state:        present
    update_cache: true

- name: Déployer la configuration du vhost
  ansible.builtin.template:
    src:  vhost.conf.j2
    dest: "/etc/nginx/sites-available/"
    mode: "0644"
  notify: Recharger Nginx

- name: Activer le vhost (lien symbolique)
  ansible.builtin.file:
    src:   "/etc/nginx/sites-available/"
    dest:  "/etc/nginx/sites-enabled/"
    state: link
  notify: Recharger Nginx

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

# Handler — exécuté uniquement si une tâche notifie "Recharger Nginx"
💡

Le fichier handlers/main.yml du rôle nginx doit contenir le handler Recharger Nginx avec ansible.builtin.service: name=nginx state=reloaded. Ansible n'exécute les handlers qu'une seule fois, à la fin du play, même si plusieurs tâches les notifient.

Le template du vhost Nginx — roles/nginx/templates/vhost.conf.j2

roles/nginx/templates/vhost.conf.j2
server {
    listen      ;
    server_name ;

    # Logs
    access_log /var/log/nginx/_access.log;
    error_log  /var/log/nginx/_error.log;

    location / {
        proxy_pass         http://127.0.0.1:;
        proxy_http_version 1.1;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

Déployer le projet

Une fois tous les fichiers en place, lance le playbook depuis la racine du projet :

Lancement du déploiement
# Vérifier la syntaxe avant de lancer
ansible-playbook site.yml -i inventory/hosts.ini --syntax-check

# Mode dry-run : voir ce qui va changer sans rien modifier
ansible-playbook site.yml -i inventory/hosts.ini --check

# Déploiement réel
ansible-playbook site.yml -i inventory/hosts.ini

# Vérifier que l'app répond
curl http://192.168.1.10
🚀

Pour aller plus loin : ajoute un rôle ufw pour configurer le pare-feu, un rôle certbot pour le HTTPS, ou intègre ce playbook dans un pipeline CI/CD avec GitHub Actions. Les rôles tout prêts sont disponibles sur Ansible Galaxy.

Récapitulatif du module

Tu as parcouru les 6 leçons du module Ansible. Voici ce que tu maîtrises maintenant :

// à retenir
  • Leçon 01 : Ansible est un outil agentless, idempotent, qui décrit l'infrastructure en YAML.
  • Leçon 02 : Installation sur la machine de contrôle, configuration SSH et première connexion aux hôtes.
  • Leçon 03 : Les inventaires organisent les hôtes en groupes ; les variables d'inventaire personnalisent la connexion.
  • Leçon 04 : Les playbooks décrivent des plays : hosts, become, tasks, modules natifs, handlers.
  • Leçon 05 : Les rôles structurent le code (tasks/, handlers/, templates/, files/, defaults/) ; les variables s'organisent en niveaux de priorité.
  • Leçon 06 : Projet complet — deux rôles (nginx + nodejs), variables centralisées, templates Jinja2, service systemd.

Ressources pour aller plus loin