Site icon

Prometheus Grafana Ansible : déployer une stack monitoring complète sur Linux

Ansible · Prometheus · Grafana · Monitoring

Tu veux déployer Prometheus, Grafana et Alertmanager avec Ansible sans passer trois jours à tout câbler à la main ? Dans ce guide, je te montre exactement comment je monte une stack de monitoring complète, reproductible et sécurisée sur des serveurs Linux. Du playbook à la première alerte Slack, tout y est.

Pourquoi Prometheus + Grafana reste le standard du monitoring Linux

Quand je parle monitoring avec mes clients PME, la question revient toujours : « Pourquoi pas Zabbix ? Pourquoi pas Datadog ? ». La réponse est simple : Prometheus + Grafana, c’est le combo qui offre le meilleur rapport puissance/complexité pour des infras Linux de 5 à 200 serveurs.

Prometheus collecte les métriques en mode pull (il va chercher les données lui-même), les stocke dans une base de données time-series ultra-performante, et expose un langage de requêtes (PromQL) qui permet de créer des alertes fines. Grafana se branche dessus pour la visualisation. Alertmanager gère le routage des alertes (email, Slack, PagerDuty…).

Le tout est open source, léger, et surtout : parfaitement automatisable avec Ansible. C’est exactement ce que je fais dans mon repo monitoring-prometheus pour déployer la supervision chez mes clients.

💡

Pourquoi Ansible et pas Docker Compose ?

Docker Compose convient pour un lab. Mais en production multi-serveurs, Ansible te permet de déployer Node Exporter sur 50 machines, de gérer les certificats TLS, et de mettre à jour Prometheus sans toucher manuellement à chaque serveur. C’est un investissement qui se rentabilise dès le deuxième déploiement.

Architecture de la stack : qui fait quoi

Avant de plonger dans le code, posons l’architecture. Voici les composants qu’on va déployer :

En termes de flux réseau : les serveurs cibles exposent le port 9100 (Node Exporter) uniquement vers le serveur Prometheus. Prometheus expose le port 9090, Alertmanager le 9093, et Grafana le 3000. En production, seul Grafana est exposé derrière un reverse proxy Nginx avec HTTPS.

Prérequis et structure du projet Ansible

Ce dont tu as besoin :

✅ Checklist prérequis

Ansible 2.14+ installé sur ta machine de contrôle
Accès SSH par clé sur les serveurs cibles
Serveurs sous Debian 11/12/13 ou Ubuntu 22.04+
Un serveur dédié pour Prometheus + Grafana (2 vCPU, 4 Go RAM minimum)
Ports 9090, 9093, 9100, 3000 ouverts selon les flux nécessaires

Voici la structure de mon projet Ansible :

monitoring-prometheus/
├── inventories/
│   └── production/
│       ├── hosts.yml
│       └── group_vars/
│           ├── all.yml
│           ├── prometheus.yml
│           └── exporters.yml
├── roles/
│   ├── node_exporter/
│   │   ├── tasks/main.yml
│   │   ├── handlers/main.yml
│   │   ├── templates/
│   │   └── defaults/main.yml
│   ├── prometheus/
│   │   ├── tasks/main.yml
│   │   ├── handlers/main.yml
│   │   ├── templates/
│   │   └── defaults/main.yml
│   ├── alertmanager/
│   │   ├── tasks/main.yml
│   │   ├── templates/
│   │   └── defaults/main.yml
│   └── grafana/
│       ├── tasks/main.yml
│       ├── templates/
│       └── defaults/main.yml
├── playbooks/
│   ├── deploy_exporters.yml
│   ├── deploy_prometheus.yml
│   └── deploy_grafana.yml
└── ansible.cfg

L’inventaire sépare deux groupes : exporters (tous les serveurs à superviser) et monitoring (le serveur central qui héberge Prometheus, Alertmanager et Grafana).

# inventories/production/hosts.yml
all:
  children:
    exporters:
      hosts:
        web-01:
          ansible_host: 10.0.1.10
        web-02:
          ansible_host: 10.0.1.11
        db-01:
          ansible_host: 10.0.1.20
    monitoring:
      hosts:
        prometheus-01:
          ansible_host: 10.0.1.100

Déployer Node Exporter sur les serveurs cibles

Node Exporter est le premier composant à installer. C’est un binaire Go statique qui tourne en tant que service systemd. Pas de dépendance, pas de base de données — juste un processus léger qui expose des métriques HTTP.

Voici le rôle Ansible que j’utilise :

# roles/node_exporter/defaults/main.yml
node_exporter_version: "1.8.2"
node_exporter_listen_address: "0.0.0.0:9100"
node_exporter_user: "node_exporter"
node_exporter_group: "node_exporter"
# roles/node_exporter/tasks/main.yml
- name: Create node_exporter system user
  ansible.builtin.user:
    name: "{{ node_exporter_user }}"
    system: true
    shell: /usr/sbin/nologin
    create_home: false

- name: Download node_exporter binary
  ansible.builtin.get_url:
    url: "https://github.com/prometheus/node_exporter/releases/download/v{{ node_exporter_version }}/node_exporter-{{ node_exporter_version }}.linux-amd64.tar.gz"
    dest: "/tmp/node_exporter-{{ node_exporter_version }}.tar.gz"
    mode: "0644"

- name: Extract node_exporter
  ansible.builtin.unarchive:
    src: "/tmp/node_exporter-{{ node_exporter_version }}.tar.gz"
    dest: /tmp/
    remote_src: true

- name: Install node_exporter binary
  ansible.builtin.copy:
    src: "/tmp/node_exporter-{{ node_exporter_version }}.linux-amd64/node_exporter"
    dest: /usr/local/bin/node_exporter
    remote_src: true
    owner: root
    group: root
    mode: "0755"
  notify: Restart node_exporter

- name: Deploy node_exporter systemd unit
  ansible.builtin.template:
    src: node_exporter.service.j2
    dest: /etc/systemd/system/node_exporter.service
    mode: "0644"
  notify: Restart node_exporter

- name: Enable and start node_exporter
  ansible.builtin.systemd:
    name: node_exporter
    enabled: true
    state: started
    daemon_reload: true

Le template systemd :

# roles/node_exporter/templates/node_exporter.service.j2
[Unit]
Description=Prometheus Node Exporter
After=network-online.target
Wants=network-online.target

[Service]
User={{ node_exporter_user }}
Group={{ node_exporter_group }}
Type=simple
ExecStart=/usr/local/bin/node_exporter \
  --web.listen-address={{ node_exporter_listen_address }}
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Pour déployer sur tous les serveurs :

ansible-playbook -i inventories/production/hosts.yml playbooks/deploy_exporters.yml

En moins d’une minute, tous tes serveurs exposent leurs métriques. Tu peux vérifier avec un simple curl :

curl -s http://10.0.1.10:9100/metrics | head -20

Installer et configurer Prometheus avec Ansible

Prometheus est le cœur de la stack. Son rôle Ansible gère le téléchargement du binaire, la création de l’utilisateur système, le fichier de configuration, les règles d’alerte, et le service systemd.

# roles/prometheus/defaults/main.yml
prometheus_version: "2.53.3"
prometheus_retention: "30d"
prometheus_scrape_interval: "15s"
prometheus_listen_address: "0.0.0.0:9090"
prometheus_data_dir: "/var/lib/prometheus"
prometheus_config_dir: "/etc/prometheus"

Le fichier de configuration Prometheus est généré par un template Jinja2 qui itère dynamiquement sur les hosts de l’inventaire :

# roles/prometheus/templates/prometheus.yml.j2
global:
  scrape_interval: {{ prometheus_scrape_interval }}
  evaluation_interval: 15s

rule_files:
  - "rules/*.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - "localhost:9093"

scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]

  - job_name: "node_exporter"
    static_configs:
      - targets:
{% for host in groups['exporters'] %}
          - "{{ hostvars[host]['ansible_host'] }}:9100"
{% endfor %}
        labels:
          env: "production"
💡

Astuce : targets dynamiques

Grâce au template Jinja2, chaque fois que tu ajoutes un serveur dans ton inventaire Ansible et que tu relances le playbook, Prometheus récupère automatiquement la nouvelle cible. Zéro édition manuelle du fichier prometheus.yml.

Les règles d’alerte de base que je déploie systématiquement :

# roles/prometheus/templates/rules/node_alerts.yml.j2
groups:
  - name: node_alerts
    rules:
      - alert: InstanceDown
        expr: up == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Instance {{ '{{ $labels.instance }}' }} down"
          description: "{{ '{{ $labels.instance }}' }} est injoignable depuis plus de 2 minutes."

      - alert: HighCpuUsage
        expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "CPU élevé sur {{ '{{ $labels.instance }}' }}"

      - alert: DiskSpaceLow
        expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 15
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Espace disque faible sur {{ '{{ $labels.instance }}' }}"

      - alert: HighMemoryUsage
        expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 90
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "RAM élevée sur {{ '{{ $labels.instance }}' }}"

Le playbook de déploiement :

# playbooks/deploy_prometheus.yml
---
- name: Deploy Prometheus stack
  hosts: monitoring
  become: true
  roles:
    - prometheus
    - alertmanager
    - grafana
ansible-playbook -i inventories/production/hosts.yml playbooks/deploy_prometheus.yml

Après exécution, tu peux vérifier que Prometheus tourne et scrape correctement tes cibles :

# Vérifier le statut
systemctl status prometheus

# Vérifier les targets dans l'interface web
curl -s http://localhost:9090/api/v1/targets | jq '.data.activeTargets[] | {instance: .labels.instance, health: .health}'

Configurer Alertmanager pour les notifications

Alertmanager reçoit les alertes de Prometheus et se charge de les dédupliquer, les grouper, et les envoyer aux bons destinataires. Je configure systématiquement deux routes : Slack pour les alertes critiques en temps réel, et email pour les récapitulatifs.

# roles/alertmanager/templates/alertmanager.yml.j2
global:
  resolve_timeout: 5m
  smtp_smarthost: "{{ alertmanager_smtp_host }}:{{ alertmanager_smtp_port }}"
  smtp_from: "{{ alertmanager_smtp_from }}"
  smtp_auth_username: "{{ alertmanager_smtp_user }}"
  smtp_auth_password: "{{ alertmanager_smtp_password }}"

route:
  receiver: "slack-critical"
  group_by: ["alertname", "instance"]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - match:
        severity: critical
      receiver: "slack-critical"
    - match:
        severity: warning
      receiver: "email-warning"

receivers:
  - name: "slack-critical"
    slack_configs:
      - api_url: "{{ alertmanager_slack_webhook }}"
        channel: "#alerts-infra"
        title: '{{ "{{ .GroupLabels.alertname }}" }}'
        text: '{{ "{{ range .Alerts }}{{ .Annotations.summary }}\n{{ end }}" }}'
        send_resolved: true

  - name: "email-warning"
    email_configs:
      - to: "{{ alertmanager_email_to }}"
        send_resolved: true
⚠️

Secrets dans la config Alertmanager

Les mots de passe SMTP et webhooks Slack sont des secrets. Je les chiffre systématiquement avec Ansible Vault. Jamais de credentials en clair dans un dépôt Git, même privé. Un ansible-vault encrypt_string prend 10 secondes et t’évite un incident de sécurité.

Les variables sensibles dans le group_vars, chiffrées avec Vault :

# Chiffrer les secrets
ansible-vault encrypt_string 'https://hooks.slack.com/services/xxx' --name 'alertmanager_slack_webhook'
ansible-vault encrypt_string 'mon_mot_de_passe_smtp' --name 'alertmanager_smtp_password'

# Lancer le playbook avec le vault
ansible-playbook -i inventories/production/hosts.yml playbooks/deploy_prometheus.yml --ask-vault-pass

Déployer Grafana et importer les dashboards

Grafana est le dernier maillon de la chaîne. Mon rôle Ansible installe Grafana depuis le dépôt officiel, configure la datasource Prometheus automatiquement, et importe les dashboards dont j’ai besoin.

# roles/grafana/tasks/main.yml (extrait)
- name: Add Grafana GPG key
  ansible.builtin.apt_key:
    url: https://apt.grafana.com/gpg.key
    state: present

- name: Add Grafana repository
  ansible.builtin.apt_repository:
    repo: "deb https://apt.grafana.com stable main"
    state: present

- name: Install Grafana
  ansible.builtin.apt:
    name: grafana
    state: present
    update_cache: true

- name: Configure Grafana datasource
  ansible.builtin.template:
    src: datasource-prometheus.yml.j2
    dest: /etc/grafana/provisioning/datasources/prometheus.yml
    owner: grafana
    group: grafana
    mode: "0640"
  notify: Restart grafana

- name: Configure Grafana dashboard provisioning
  ansible.builtin.template:
    src: dashboard-provider.yml.j2
    dest: /etc/grafana/provisioning/dashboards/default.yml
    owner: grafana
    group: grafana
    mode: "0640"
  notify: Restart grafana

- name: Deploy Node Exporter dashboard
  ansible.builtin.copy:
    src: dashboards/node-exporter-full.json
    dest: /var/lib/grafana/dashboards/node-exporter-full.json
    owner: grafana
    group: grafana
    mode: "0640"

- name: Enable and start Grafana
  ansible.builtin.systemd:
    name: grafana-server
    enabled: true
    state: started

Le template de datasource :

# roles/grafana/templates/datasource-prometheus.yml.j2
apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://localhost:9090
    isDefault: true
    editable: false

Pour le dashboard Node Exporter, je récupère le JSON depuis Grafana Dashboard 1860 que j’intègre directement dans le rôle. Après déploiement, Grafana affiche immédiatement les métriques de tous tes serveurs sans configuration manuelle.

Sécuriser la stack en production

Déployer la stack c’est bien, la sécuriser c’est indispensable. Voici les mesures que j’applique systématiquement chez mes clients :

1. TLS entre Node Exporter et Prometheus

Par défaut, les métriques transitent en HTTP non chiffré. En production, j’active le TLS sur Node Exporter et je configure Prometheus pour scraper en HTTPS :

# Générer les certificats avec Ansible (rôle dédié)
- name: Generate Node Exporter TLS cert
  community.crypto.x509_certificate:
    path: /etc/node_exporter/tls.crt
    privatekey_path: /etc/node_exporter/tls.key
    csr_path: /etc/node_exporter/tls.csr
    provider: selfsigned

# Configuration Node Exporter avec TLS
# /etc/node_exporter/config.yml
tls_server_config:
  cert_file: /etc/node_exporter/tls.crt
  key_file: /etc/node_exporter/tls.key

2. Authentification basique sur Prometheus

# /etc/prometheus/web.yml
basic_auth_users:
  admin: $2y$12$hash_bcrypt_du_mot_de_passe

3. Reverse proxy Nginx devant Grafana

server {
    listen 443 ssl http2;
    server_name grafana.example.com;

    ssl_certificate /etc/letsencrypt/live/grafana.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/grafana.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}

4. Firewall : n’ouvrir que le nécessaire

# Sur les serveurs cibles : Node Exporter accessible uniquement depuis Prometheus
ufw allow from 10.0.1.100 to any port 9100

# Sur le serveur monitoring : Grafana en HTTPS uniquement
ufw allow 443/tcp

Pour une mise en place complète du firewall avec Ansible, j’utilise mon rôle UFW documenté dans mon offre d’infogérance.

Tester les alertes de bout en bout

Un monitoring qui n’alerte pas en cas de problème, ça ne sert à rien. Je teste systématiquement la chaîne complète après chaque déploiement.

Test 1 : Simuler un serveur down

# Sur un serveur cible, arrêter Node Exporter
sudo systemctl stop node_exporter

# Attendre 2 minutes (le seuil configuré dans la règle InstanceDown)
# Vérifier dans Prometheus que l'alerte passe en "firing"
curl -s http://localhost:9090/api/v1/alerts | jq '.data.alerts[] | {alertname: .labels.alertname, instance: .labels.instance, state: .state}'

Test 2 : Vérifier la réception Slack

Après les 2 minutes, tu dois recevoir une notification dans ton channel #alerts-infra. Si ce n’est pas le cas, vérifie les logs d’Alertmanager :

journalctl -u alertmanager -f --no-pager | grep -i "notify"

Test 3 : Simuler une charge CPU

# Générer de la charge CPU pendant 10 minutes
stress-ng --cpu $(nproc) --timeout 600s

# Vérifier dans Grafana que le dashboard reflète bien le pic
💡

Automatiser les tests avec Molecule

Pour les rôles Ansible, j’utilise Molecule avec un driver Docker pour valider que chaque rôle s’installe correctement avant de le pousser en production. Un molecule test lance la création du conteneur, le déploiement du rôle, et les vérifications idempotence.

Aller plus loin : rétention, fédération et haute disponibilité

Pour des infras plus conséquentes, voici les évolutions que je recommande :

Si tu gères une infra de plus de 20 serveurs et que tu veux un accompagnement sur la mise en place, je propose ce type de prestation dans le cadre de ma maintenance corrective et proactive.

FAQ

Prometheus ou Zabbix : lequel choisir pour du monitoring Linux ?
Prometheus excelle pour les métriques time-series et les environnements cloud-native. Zabbix est plus adapté si tu as besoin de monitoring SNMP, de cartographie réseau, ou si ton équipe connaît déjà l’outil. Pour du Linux pur avec Ansible, Prometheus + Grafana est plus léger et plus flexible.
Combien de RAM faut-il pour Prometheus ?
Compte environ 1 à 2 Go de RAM pour 100 séries temporelles actives avec 15 jours de rétention. Pour 50 serveurs avec Node Exporter, 4 Go suffisent largement. Au-delà de 200 serveurs, envisage 8 Go et un SSD pour le stockage.
Comment ajouter un nouveau serveur au monitoring ?
Ajoute le serveur dans le groupe exporters de ton inventaire Ansible, puis relance les deux playbooks : deploy_exporters.yml pour installer Node Exporter, et deploy_prometheus.yml pour régénérer la config Prometheus avec la nouvelle cible. C’est fait en 2 minutes.
Peut-on utiliser Docker Compose au lieu d’Ansible pour Prometheus ?
Oui, Docker Compose fonctionne très bien pour un serveur unique. Mais dès que tu veux déployer Node Exporter sur plusieurs machines, gérer les certificats TLS, et mettre à jour la config Prometheus automatiquement, Ansible devient indispensable. Les deux approches ne sont pas exclusives : tu peux utiliser Ansible pour orchestrer des conteneurs Docker.
Comment monitorer des conteneurs Docker avec Prometheus ?
Ajoute cAdvisor comme exporter. Il expose les métriques de chaque conteneur (CPU, RAM, réseau, I/O). Il suffit de l’ajouter comme target dans la config Prometheus et d’importer le dashboard Grafana dédié (ID 14282).

Tu veux une stack monitoring fiable sans y passer des semaines ?

Je déploie et maintiens des stacks Prometheus + Grafana en production pour des PME. De l’audit initial à la mise en place des alertes, tout est automatisé et documenté.

Contacte-moi →

Quitter la version mobile