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.
📋 Au programme
- Pourquoi Prometheus + Grafana reste le standard du monitoring Linux
- Architecture de la stack : qui fait quoi
- Prérequis et structure du projet Ansible
- Déployer Node Exporter sur les serveurs cibles
- Installer et configurer Prometheus avec Ansible
- Configurer Alertmanager pour les notifications
- Déployer Grafana et importer les dashboards
- Sécuriser la stack en production
- Tester les alertes de bout en bout
- FAQ
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 :
- Node Exporter — installé sur chaque serveur à superviser. Expose les métriques système (CPU, RAM, disque, réseau) sur le port 9100.
- Prometheus — le serveur central. Il scrape les Node Exporters à intervalle régulier (15s par défaut), stocke les métriques, et évalue les règles d’alerte.
- Alertmanager — reçoit les alertes de Prometheus et les route vers les bons canaux (email, Slack, webhook).
- Grafana — interface de visualisation. Se connecte à Prometheus comme datasource et affiche des dashboards.
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
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 :
- Rétention longue durée — Coupler Prometheus avec Thanos ou VictoriaMetrics pour stocker des métriques au-delà de 30 jours sans exploser le disque local.
- Fédération — Si tu as plusieurs sites ou clusters, utilise la fédération Prometheus pour agréger les métriques dans une instance centrale.
- Haute disponibilité — Déployer deux instances Prometheus qui scrapent les mêmes cibles. Alertmanager gère nativement le clustering pour dédupliquer les alertes.
- Service discovery — Remplacer les
static_configspar du service discovery (Consul, DNS, fichiers JSON générés) pour les environnements très dynamiques.
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
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é.