Stack de monitoring Prometheus + Grafana

Stack de monitoring Prometheus + Grafana

Mettez en place le monitoring de référence open-source : Prometheus pour collecter les métriques, Node Exporter sur chaque VPS, Grafana pour visualiser. La stack la plus utilisée au monde pour le monitoring sysadmin.

Introduction

Prometheus est devenu le standard de facto pour le monitoring : utilisé par SoundCloud (ses créateurs), CNCF, Kubernetes natif, et 90% des startups tech. Le combo gagnant :

  • Prometheus : scrape les métriques toutes les 15s, stockage TSDB efficient
  • Node Exporter : agent système (CPU, RAM, disque, réseau) sur chaque VPS
  • Grafana : visualisation et alerting
  • Alertmanager : routing des alertes (email, Discord, Slack)

Bénéfices vs des outils SaaS payants (Datadog, New Relic) :

  • Gratuit, self-hosted, chez vous
  • Pas de limite de métriques
  • Données souveraines

Prérequis

  • 1 VPS "central" pour Prometheus + Grafana (2 Go RAM)
  • N VPS à monitorer (Node Exporter)
  • Docker sur le VPS central
  • Réseau entre VPS (WireGuard recommandé)

Étape 1 : Architecture

   ┌──────────────────┐  ┌──────────────────┐
   │ VPS Web 1        │  │ VPS DB           │
   │  + Node Exporter │  │  + Node Exporter │
   │  + Nginx Exporter│  │  + MySQL Exporter│
   └────────┬─────────┘  └────────┬─────────┘
            │                     │
            └──────────┬──────────┘
                       │
              ┌────────▼────────┐
              │  VPS Central    │
              │  ┌───────────┐  │
              │  │ Prometheus│  │
              │  └───────────┘  │
              │  ┌───────────┐  │
              │  │  Grafana  │  │
              │  └───────────┘  │
              │  ┌───────────┐  │
              │  │Alertmanager│ │
              │  └───────────┘  │
              └─────────────────┘

Étape 2 : Déployer la stack centrale

sudo mkdir -p /opt/monitoring
cd /opt/monitoring
sudo nano docker-compose.yml
services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - ./alerts.yml:/etc/prometheus/alerts.yml:ro
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=90d'
      - '--web.console.libraries=/usr/share/prometheus/console_libraries'
      - '--web.console.templates=/usr/share/prometheus/consoles'
    networks:
      - monitoring

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=UnMotDePasseFort
      - GF_USERS_ALLOW_SIGN_UP=false
    volumes:
      - grafana_data:/var/lib/grafana
    networks:
      - monitoring
    depends_on:
      - prometheus

  alertmanager:
    image: prom/alertmanager:latest
    container_name: alertmanager
    restart: unless-stopped
    ports:
      - "9093:9093"
    volumes:
      - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
    networks:
      - monitoring

  node_exporter:
    image: prom/node-exporter:latest
    container_name: node_exporter
    restart: unless-stopped
    pid: host
    network_mode: host
    volumes:
      - /:/host:ro,rslave
    command:
      - '--path.rootfs=/host'

volumes:
  prometheus_data:
  grafana_data:

networks:
  monitoring:

Étape 3 : Config Prometheus

sudo nano /opt/monitoring/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - alertmanager:9093

rule_files:
  - "alerts.yml"

scrape_configs:
  # Prometheus lui-même
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  # VPS central (Node Exporter)
  - job_name: 'node_central'
    static_configs:
      - targets: ['host.docker.internal:9100']
        labels:
          host: 'srv-central'

  # VPS Web 1
  - job_name: 'node_web1'
    static_configs:
      - targets: ['10.66.66.2:9100']
        labels:
          host: 'srv-web-01'

  # VPS Web 2
  - job_name: 'node_web2'
    static_configs:
      - targets: ['10.66.66.3:9100']
        labels:
          host: 'srv-web-02'

  # VPS DB
  - job_name: 'node_db'
    static_configs:
      - targets: ['10.66.66.4:9100']
        labels:
          host: 'srv-db-01'
  
  # Nginx Exporter
  - job_name: 'nginx'
    static_configs:
      - targets: ['10.66.66.2:9113']
        labels:
          host: 'srv-web-01'

  # MySQL Exporter
  - job_name: 'mysql'
    static_configs:
      - targets: ['10.66.66.4:9104']
        labels:
          host: 'srv-db-01'

Étape 4 : Règles d'alerte de base

sudo nano /opt/monitoring/alerts.yml
groups:
  - name: system_alerts
    interval: 30s
    rules:
      - alert: HighCPU
        expr: 100 - (avg by(host) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "CPU > 90% sur {{ $labels.host }}"
          description: "CPU à {{ $value }}% depuis 5 minutes."

      - alert: HighMemory
        expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100 > 90
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "RAM > 90% sur {{ $labels.host }}"

      - alert: DiskFull
        expr: (node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) / node_filesystem_size_bytes{mountpoint="/"} * 100 > 90
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Disque > 90% sur {{ $labels.host }}"

      - alert: NodeDown
        expr: up{job=~"node_.*"} == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Host {{ $labels.host }} est DOWN"

      - alert: HighLoad
        expr: node_load5 > 4
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Load > 4 sur {{ $labels.host }}"

Étape 5 : Config Alertmanager (Discord/email)

sudo nano /opt/monitoring/alertmanager.yml
global:
  resolve_timeout: 5m

route:
  group_by: ['alertname', 'host']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 12h
  receiver: 'discord'

receivers:
  - name: 'discord'
    webhook_configs:
      - url: 'https://discord.com/api/webhooks/VOTRE_WEBHOOK'
        send_resolved: true

  - name: 'email'
    email_configs:
      - to: '[email protected]'
        from: '[email protected]'
        smarthost: 'smtp.example.com:587'
        auth_username: '[email protected]'
        auth_password: 'password'
        require_tls: true

Étape 6 : Démarrer la stack

sudo docker compose up -d
sudo docker compose ps

Vérifiez :

Étape 7 : Installer Node Exporter sur chaque VPS source

Sur chaque VPS à monitorer :

# Télécharger
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.1/node_exporter-1.8.1.linux-amd64.tar.gz
tar xvf node_exporter-1.8.1.linux-amd64.tar.gz
sudo mv node_exporter-1.8.1.linux-amd64/node_exporter /usr/local/bin/
sudo chown root:root /usr/local/bin/node_exporter

# User dédié
sudo useradd --no-create-home --shell /bin/false node_exporter
sudo chown node_exporter:node_exporter /usr/local/bin/node_exporter

Service systemd :

sudo nano /etc/systemd/system/node_exporter.service
[Unit]
Description=Node Exporter
After=network.target

[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter \
    --collector.systemd \
    --collector.processes \
    --web.listen-address=0.0.0.0:9100

Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter

Testez :

curl http://localhost:9100/metrics | head -30

Doit afficher les métriques node_cpu_seconds_total, node_memory_MemTotal_bytes, etc.

Étape 8 : Sécuriser Node Exporter

⚠️ Ne pas exposer le port 9100 publiquement (révèle de la donnée sensible). Solutions :

  1. WireGuard entre VPS (recommandé)
  2. UFW : sudo ufw allow from IP_VPS_CENTRAL to any port 9100
  3. Reverse proxy avec auth basique sur le central

Étape 9 : Ajouter Grafana

Tunnel SSH :

ssh -L 3000:127.0.0.1:3000 user@IP_VPS_CENTRAL

http://localhost:3000admin / UnMotDePasseFort

Datasource Prometheus

  1. Configuration → Data sources → Add data source
  2. Prometheus
  3. URL : http://prometheus:9090
  4. Save & test

Dashboard Node Exporter

  1. Dashboards → Import
  2. ID : 1860 (Node Exporter Full, le plus populaire)
  3. Datasource : Prometheus
  4. Import

Vous obtenez instantanément CPU, RAM, disque, réseau, load, processus pour tous vos VPS.

Étape 10 : Exporters additionnels

Selon vos services :

Nginx Exporter

docker run -d \
  --restart unless-stopped \
  --name nginx-exporter \
  -p 9113:9113 \
  nginx/nginx-prometheus-exporter \
  -nginx.scrape-uri=http://localhost/nginx_status

Activez stub_status dans Nginx :

server {
    listen 127.0.0.1:80;
    location /nginx_status {
        stub_status;
        allow 127.0.0.1;
        deny all;
    }
}

Dashboard Grafana : ID 12708.

MySQL Exporter

docker run -d \
  --restart unless-stopped \
  --name mysql-exporter \
  -p 9104:9104 \
  -e DATA_SOURCE_NAME='exporter:password@(localhost:3306)/' \
  prom/mysqld-exporter

Créez un user MySQL dédié :

CREATE USER 'exporter'@'localhost' IDENTIFIED BY 'password' WITH MAX_USER_CONNECTIONS 3;
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'localhost';
FLUSH PRIVILEGES;

Dashboard Grafana : ID 7362.

Redis Exporter

docker run -d \
  --restart unless-stopped \
  --name redis-exporter \
  -p 9121:9121 \
  oliver006/redis_exporter \
  --redis.addr=redis://localhost:6379 \
  --redis.password=YOUR_REDIS_PASSWORD

Dashboard : ID 763.

Blackbox Exporter (HTTP/TCP probes)

Pour monitorer des URLs externes (uptime de votre site) :

docker run -d \
  --name blackbox-exporter \
  -p 9115:9115 \
  prom/blackbox-exporter

Étape 11 : Dashboards recommandés

Importer dans Grafana via Dashboards → Import :

  • 1860 : Node Exporter Full
  • 12708 : Nginx Exporter
  • 7362 : MySQL Overview
  • 763 : Redis Dashboard
  • 13639 : Loki + Promtail (logs)
  • 893 : Docker and Host
  • 9628 : PostgreSQL

Étape 12 : Alerting via Grafana (alternative à Alertmanager)

Grafana 10+ a son propre alerting natif :

  1. Alerting → Alert rules → New alert rule
  2. Query : 100 - (avg by(host) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90
  3. Add contact point : Discord, Slack, Telegram, etc.
  4. Notification policy : groupage, throttling

Plus pratique qu'Alertmanager pour des setups simples.

Dépannage

Targets en DOWN dans Prometheus

http://localhost:9090/targets → vérifiez la connectivité :

curl http://IP_VPS:9100/metrics

Causes : firewall, IP fausse dans prometheus.yml, node_exporter pas démarré.

Pas de métriques dans Grafana

Vérifiez la datasource (Configuration → Data sources → test). Puis dans Explore, testez up{job="node_web1"}.

Prometheus bouffe trop d'espace

Limitez la rétention :

command:
  - '--storage.tsdb.retention.time=30d'  # au lieu de 90d

Alertes ne sont pas envoyées

sudo docker compose logs alertmanager

Testez le webhook Discord manuellement :

curl -X POST -H "Content-Type: application/json" \
  -d '{"content": "test"}' \
  https://discord.com/api/webhooks/VOTRE_WEBHOOK

Commandes utiles

# État des targets
curl http://localhost:9090/api/v1/targets

# Requêter Prometheus en CLI
curl "http://localhost:9090/api/v1/query?query=up"

# Reload de la config sans restart
curl -X POST http://localhost:9090/-/reload

# Tester les alertes
curl http://localhost:9090/api/v1/alerts

# Vérifier la config Prometheus
sudo docker run --rm -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \
    prom/prometheus promtool check config /etc/prometheus/prometheus.yml

# Vérifier les règles d'alerte
sudo docker run --rm -v $(pwd)/alerts.yml:/etc/prometheus/alerts.yml \
    prom/prometheus promtool check rules /etc/prometheus/alerts.yml

Conclusion

Avec cette stack vous avez :

  • Monitoring CPU/RAM/disque/réseau/charge sur tous vos VPS
  • Alerting automatique en cas de problème
  • Dashboards visuels pour debug rapide
  • Stockage long terme des métriques (90 jours par défaut)

Pour aller plus loin :

  • Ajoutez Loki pour les logs (déjà couvert dans un autre tuto)
  • Ajoutez Tempo pour le tracing distribué
  • Migrez vers Thanos ou Cortex pour de la haute dispo Prometheus
  • Utilisez Prometheus Operator si vous passez à Kubernetes

Ressources

Rejoignez notre serveur communautaire Discord

Pour toute question, suggestion ou simplement pour discuter avec la communauté, rejoignez-nous sur Discord !

900+Membres