Monitoring applicatif : Grafana + Prometheus

Cloud & Déploiement 31/03/2026 21:00:00 angularforall.com
Grafana Prometheus Monitoring Observabilité Kubernetes Alerting
Monitoring applicatif : Grafana + Prometheus

Mettez en place une stack d'observabilité complète avec Prometheus et Grafana : métriques, dashboards, alerting Slack et déploiement Kubernetes via Helm.

Architecture observabilité : Grafana + Prometheus

L'observabilité d'une application repose sur trois piliers : les métriques (données numériques dans le temps), les logs (événements textuels) et les traces (suivi des requêtes distribuées). Prometheus + Grafana couvrent le pilier métriques avec une fiabilité éprouvée en production.

Rôle de Prometheus

Prometheus est une base de données de séries temporelles open source, initialement développée par SoundCloud et aujourd'hui hébergée par la CNCF (Cloud Native Computing Foundation). Son modèle de collecte repose sur le scraping : Prometheus "tire" (pull) régulièrement les métriques exposées par des endpoints HTTP /metrics.

  • TSDB intégrée : stockage optimisé pour les séries temporelles sur disque
  • PromQL : langage de requête puissant pour agréger et filtrer les métriques
  • Service Discovery : détection automatique des cibles (Kubernetes, Consul, EC2…)
  • Alertmanager : composant dédié au routage et à l'envoi des alertes

Rôle de Grafana

Grafana est un outil de visualisation open source qui se connecte à de nombreuses sources de données (Prometheus, Loki, InfluxDB, Elasticsearch, MySQL…) et permet de construire des dashboards interactifs.

  • Dashboards dynamiques : variables, filtres, time ranges
  • Alerting intégré : règles visuelles, notifications multi-canaux
  • Provisioning : dashboards et datasources gérés as-code
  • Plugins : centaines de visualisations et datasources communautaires

Architecture globale

┌────────────────────────────────────────────────────────────┐
│                     Applications                            │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐  │
│  │  App 1   │  │  App 2   │  │  Nginx   │  │  Node    │  │
│  │ :8080    │  │ :8081    │  │ Exporter │  │ Exporter │  │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘  └────┬─────┘  │
│       │ /metrics    │ /metrics    │              │        │
└───────┼─────────────┼─────────────┼──────────────┼────────┘
        │             │             │              │
        ▼             ▼             ▼              ▼
┌──────────────────────────────────────────────────┐
│              Prometheus (scraping)                │
│         Stockage TSDB  +  PromQL                 │
└─────────────────────┬────────────────────────────┘
                      │
          ┌───────────┼───────────┐
          ▼           ▼           ▼
    ┌──────────┐ ┌──────────┐ ┌──────────────┐
    │ Grafana  │ │Alertmgr  │ │  API externe │
    │Dashboards│ │Slack/Mail│ │  PagerDuty   │
    └──────────┘ └──────────┘ └──────────────┘
Prérequis :
  • Docker + Docker Compose installés (version 24+)
  • Ou cluster Kubernetes (k8s 1.26+ recommandé) + kubectl + Helm 3
  • Accès réseau aux ports 9090 (Prometheus), 3000 (Grafana), 9093 (Alertmanager)
  • Minimum 2 Go RAM, 10 Go disque pour la TSDB

Installer Prometheus avec Docker Compose

La méthode la plus rapide pour démarrer localement ou sur un VPS est Docker Compose. On va créer une stack complète incluant Prometheus, Grafana, Alertmanager et deux exporters essentiels.

Structure du projet

monitoring/
├── docker-compose.yml
├── prometheus/
│   ├── prometheus.yml          # config principale
│   └── rules/
│       └── alerts.yml          # règles d'alerte
├── alertmanager/
│   └── alertmanager.yml        # routage des alertes
└── grafana/
    ├── provisioning/
    │   ├── datasources/
    │   │   └── prometheus.yml  # datasource auto
    │   └── dashboards/
    │       └── dashboards.yml  # import auto
    └── dashboards/
        └── node.json           # dashboard importé

docker-compose.yml

# docker-compose.yml
version: '3.8'

networks:
  monitoring:
    driver: bridge

volumes:
  prometheus_data: {}
  grafana_data: {}

services:
  # ─── Prometheus ───────────────────────────────────────
  prometheus:
    image: prom/prometheus:v2.51.0
    container_name: prometheus
    restart: unless-stopped
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - ./prometheus/rules:/etc/prometheus/rules
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=30d'   # garde 30 jours de données
      - '--web.console.libraries=/usr/share/prometheus/console_libraries'
      - '--web.console.templates=/usr/share/prometheus/consoles'
      - '--web.enable-lifecycle'               # rechargement à chaud via API
    ports:
      - '9090:9090'
    networks:
      - monitoring

  # ─── Grafana ──────────────────────────────────────────
  grafana:
    image: grafana/grafana:10.4.0
    container_name: grafana
    restart: unless-stopped
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-changeme}
      - GF_USERS_ALLOW_SIGN_UP=false
      - GF_SERVER_ROOT_URL=https://monitoring.exemple.com
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
      - ./grafana/dashboards:/var/lib/grafana/dashboards
    ports:
      - '3000:3000'
    networks:
      - monitoring
    depends_on:
      - prometheus

  # ─── Alertmanager ─────────────────────────────────────
  alertmanager:
    image: prom/alertmanager:v0.27.0
    container_name: alertmanager
    restart: unless-stopped
    volumes:
      - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml
    command:
      - '--config.file=/etc/alertmanager/alertmanager.yml'
      - '--storage.path=/alertmanager'
    ports:
      - '9093:9093'
    networks:
      - monitoring

  # ─── Node Exporter (métriques OS) ─────────────────────
  node-exporter:
    image: prom/node-exporter:v1.7.0
    container_name: node-exporter
    restart: unless-stopped
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.rootfs=/rootfs'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    ports:
      - '9100:9100'
    networks:
      - monitoring

  # ─── cAdvisor (métriques Docker) ──────────────────────
  cadvisor:
    image: gcr.io/cadvisor/cadvisor:v0.49.1
    container_name: cadvisor
    restart: unless-stopped
    privileged: true
    devices:
      - /dev/kmsg:/dev/kmsg
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker:/var/lib/docker:ro
    ports:
      - '8080:8080'
    networks:
      - monitoring

Démarrage et vérification

# Lancer la stack en arrière-plan
docker compose up -d

# Vérifier que tous les services sont "Up"
docker compose ps

# Voir les logs Prometheus
docker compose logs -f prometheus

# Vérifier que Prometheus scrape bien ses targets
curl http://localhost:9090/api/v1/targets | jq '.data.activeTargets[].health'

Configurer Prometheus

La configuration de Prometheus se fait via prometheus.yml. Ce fichier définit les intervalles de scraping, les cibles à surveiller, les règles d'alerte et l'intégration avec Alertmanager.

prometheus/prometheus.yml

# prometheus/prometheus.yml
global:
  scrape_interval: 15s        # collecte toutes les 15 secondes
  evaluation_interval: 15s    # évalue les règles toutes les 15 secondes
  scrape_timeout: 10s

# Intégration avec Alertmanager
alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - alertmanager:9093

# Fichiers de règles d'alerte
rule_files:
  - 'rules/*.yml'

# Cibles à scraper
scrape_configs:
  # Prometheus se surveille lui-même
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  # Métriques système (CPU, RAM, disque, réseau)
  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node-exporter:9100']
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance

  # Métriques conteneurs Docker
  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']

  # Votre application Node.js (exemple)
  - job_name: 'mon-api'
    metrics_path: '/metrics'
    static_configs:
      - targets: ['mon-api:3001']
    # Labels additionnels sur toutes les métriques
    params:
      collect[]:
        - http_requests_total
        - process_cpu_seconds_total

  # Découverte automatique Kubernetes (si applicable)
  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      # Scraper uniquement les pods annotés
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)
      - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
        action: replace
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
        target_label: __address__

Exposer les métriques d'une app Node.js

Utilisez la librairie prom-client pour exposer des métriques personnalisées depuis votre application :

# Installation
npm install prom-client
// metrics.js — instrumentation de l'application
const client = require('prom-client');

// Collecte automatique des métriques Node.js (GC, event loop, heap…)
const register = new client.Registry();
client.collectDefaultMetrics({ register });

// Compteur de requêtes HTTP
const httpRequestsTotal = new client.Counter({
  name: 'http_requests_total',
  help: 'Nombre total de requêtes HTTP',
  labelNames: ['method', 'route', 'status_code'],
  registers: [register],
});

// Histogramme de latence
const httpRequestDuration = new client.Histogram({
  name: 'http_request_duration_seconds',
  help: 'Durée des requêtes HTTP en secondes',
  labelNames: ['method', 'route'],
  buckets: [0.01, 0.05, 0.1, 0.3, 0.5, 1, 2, 5],
  registers: [register],
});

// Middleware Express pour instrumenter toutes les routes
function metricsMiddleware(req, res, next) {
  const end = httpRequestDuration.startTimer({ method: req.method, route: req.path });
  res.on('finish', () => {
    httpRequestsTotal.inc({ method: req.method, route: req.path, status_code: res.statusCode });
    end();
  });
  next();
}

// Endpoint /metrics exposé à Prometheus
async function metricsHandler(req, res) {
  res.set('Content-Type', register.contentType);
  res.end(await register.metrics());
}

module.exports = { metricsMiddleware, metricsHandler };
// app.js — intégration dans Express
const express = require('express');
const { metricsMiddleware, metricsHandler } = require('./metrics');

const app = express();
app.use(metricsMiddleware);
app.get('/metrics', metricsHandler);
app.listen(3001);

Requêtes PromQL essentielles

# Taux de requêtes HTTP par seconde (5 dernières minutes)
rate(http_requests_total[5m])

# Latence P95 des requêtes
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, route))

# Utilisation CPU (toutes cores)
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# Mémoire disponible en Mo
node_memory_MemAvailable_bytes / 1024 / 1024

# Espace disque utilisé en %
(node_filesystem_size_bytes - node_filesystem_free_bytes) / node_filesystem_size_bytes * 100

# Taux d'erreurs HTTP 5xx
sum(rate(http_requests_total{status_code=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) * 100

Installer et configurer Grafana

Grafana se configure idéalement via le provisioning : les datasources et dashboards sont définis en YAML/JSON, versionnés dans Git et chargés automatiquement au démarrage.

Datasource Prometheus automatique

# grafana/provisioning/datasources/prometheus.yml
apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: false
    jsonData:
      httpMethod: POST
      manageAlerts: true
      prometheusType: Prometheus
      prometheusVersion: 2.51.0
      # Intervalle de scraping (doit correspondre à prometheus.yml)
      scrapeInterval: 15s
      # Délai pour compenser le lag de scraping
      queryOffset: 15s

Import automatique de dashboards

# grafana/provisioning/dashboards/dashboards.yml
apiVersion: 1

providers:
  - name: 'default'
    orgId: 1
    folder: 'Auto-imported'
    type: file
    disableDeletion: true       # ne pas supprimer via UI
    updateIntervalSeconds: 30
    options:
      path: /var/lib/grafana/dashboards

Télécharger des dashboards communautaires

# Dashboard Node Exporter Full (ID: 1860)
curl -o grafana/dashboards/node-exporter.json \
  "https://grafana.com/api/dashboards/1860/revisions/latest/download"

# Dashboard Docker + cAdvisor (ID: 893)
curl -o grafana/dashboards/docker.json \
  "https://grafana.com/api/dashboards/893/revisions/latest/download"

# Dashboard Kubernetes Cluster (ID: 6417)
curl -o grafana/dashboards/kubernetes.json \
  "https://grafana.com/api/dashboards/6417/revisions/latest/download"

Variables de sécurité (.env)

# .env — ne jamais committer ce fichier
GRAFANA_PASSWORD=MonMotDePasseSecurise123!

# Charger dans Docker Compose
docker compose --env-file .env up -d
Sécurité : En production, activez HTTPS via un reverse proxy Nginx + Certbot. Ne jamais exposer Prometheus (port 9090) directement sur Internet sans authentification.

Créer des dashboards Grafana

Un dashboard efficace répond à une question précise. Organisez-les par couche : infrastructure → conteneurs → application → business.

Panels essentiels pour un dashboard applicatif

Panel Type Requête PromQL
Req/s actuelles Stat sum(rate(http_requests_total[1m]))
Latence P95 Stat histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
Taux d'erreurs Gauge sum(rate(http_requests_total{status_code=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))
CPU Usage Time Series 100 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100
Mémoire utilisée Time Series node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes
Disque I/O Time Series rate(node_disk_io_time_seconds_total[5m])

Dashboard as Code (JSON exportable)

Exportez toujours vos dashboards en JSON pour les versionner dans Git. Dans Grafana : Dashboard → Share → Export → Save to file.

# Exporter via l'API Grafana
curl -s http://admin:${GRAFANA_PASSWORD}@localhost:3000/api/dashboards/uid/mon-dashboard-uid \
  | jq '.dashboard' > grafana/dashboards/mon-dashboard.json

# Importer un dashboard via l'API
curl -X POST \
  -H "Content-Type: application/json" \
  -d @grafana/dashboards/mon-dashboard.json \
  http://admin:${GRAFANA_PASSWORD}@localhost:3000/api/dashboards/import

Variables dans les dashboards

Les variables permettent de filtrer dynamiquement les données. Ajoutez dans Dashboard Settings → Variables :

# Variable "instance" — liste les nodes disponibles
Query: label_values(node_cpu_seconds_total, instance)

# Variable "job" — liste les jobs Prometheus
Query: label_values(job)

# Utilisation dans un panel
rate(http_requests_total{instance="$instance", job="$job"}[5m])

Alerting avec Alertmanager

Prometheus évalue des règles d'alerte et délègue l'envoi des notifications à Alertmanager, qui gère le routage, la déduplication, le silençage et le groupement.

Règles d'alerte (prometheus/rules/alerts.yml)

# prometheus/rules/alerts.yml
groups:
  - name: infrastructure
    interval: 30s
    rules:
      # Alerte si CPU > 80% pendant 5 minutes
      - alert: HighCPUUsage
        expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 5m
        labels:
          severity: warning
          team: ops
        annotations:
          summary: "CPU élevé sur {{ $labels.instance }}"
          description: "CPU à {{ $value | humanize }}% depuis 5 minutes."

      # Alerte si mémoire disponible < 10%
      - alert: LowMemory
        expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100 < 10
        for: 2m
        labels:
          severity: critical
          team: ops
        annotations:
          summary: "Mémoire critique sur {{ $labels.instance }}"
          description: "Seulement {{ $value | humanize }}% de mémoire disponible."

      # Alerte si disque > 85%
      - alert: DiskSpaceLow
        expr: (node_filesystem_size_bytes - node_filesystem_free_bytes) / node_filesystem_size_bytes * 100 > 85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Espace disque faible sur {{ $labels.instance }}"
          description: "Partition {{ $labels.mountpoint }} à {{ $value | humanize }}%."

  - name: application
    rules:
      # Alerte si taux d'erreurs HTTP > 5%
      - alert: HighErrorRate
        expr: sum(rate(http_requests_total{status_code=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) * 100 > 5
        for: 2m
        labels:
          severity: critical
          team: dev
        annotations:
          summary: "Taux d'erreurs élevé"
          description: "{{ $value | humanize }}% des requêtes retournent une erreur 5xx."

      # Alerte si latence P95 > 2 secondes
      - alert: SlowResponseTime
        expr: histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) > 2
        for: 3m
        labels:
          severity: warning
          team: dev
        annotations:
          summary: "Latence P95 élevée"
          description: "P95 à {{ $value | humanize }}s (seuil: 2s)."

      # Alerte si le service est down
      - alert: ServiceDown
        expr: up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Service {{ $labels.job }} indisponible"
          description: "L'instance {{ $labels.instance }} ne répond plus."

Configuration Alertmanager avec Slack

# alertmanager/alertmanager.yml
global:
  resolve_timeout: 5m
  slack_api_url: 'https://hooks.slack.com/services/VOTRE/WEBHOOK/URL'

# Modèles de notification
templates:
  - '/etc/alertmanager/templates/*.tmpl'

# Arbre de routage
route:
  group_by: ['alertname', 'cluster']
  group_wait: 30s        # attendre 30s pour grouper les alertes similaires
  group_interval: 5m     # délai entre notifications d'un même groupe
  repeat_interval: 4h    # rappel si l'alerte persiste
  receiver: 'slack-ops'

  # Routes spécifiques par équipe
  routes:
    - match:
        severity: critical
      receiver: 'pagerduty-oncall'
      continue: true

    - match:
        team: dev
      receiver: 'slack-dev'

    - match:
        team: ops
      receiver: 'slack-ops'

# Destinataires
receivers:
  - name: 'slack-ops'
    slack_configs:
      - channel: '#alertes-ops'
        send_resolved: true
        title: '{{ template "slack.title" . }}'
        text: '{{ template "slack.text" . }}'
        color: '{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}'

  - name: 'slack-dev'
    slack_configs:
      - channel: '#alertes-dev'
        send_resolved: true

  - name: 'pagerduty-oncall'
    pagerduty_configs:
      - routing_key: 'VOTRE_PAGERDUTY_KEY'
        severity: '{{ .CommonLabels.severity }}'

# Silençage (ex: maintenance planifiée)
inhibit_rules:
  - source_match:
      severity: 'critical'
    target_match:
      severity: 'warning'
    equal: ['alertname', 'instance']

Gérer les silences

# Créer un silence via l'API (maintenance 2h)
curl -X POST http://localhost:9093/api/v2/silences \
  -H "Content-Type: application/json" \
  -d '{
    "matchers": [{"name": "instance", "value": "srv-01:9100", "isRegex": false}],
    "startsAt": "2026-05-01T10:00:00Z",
    "endsAt": "2026-05-01T12:00:00Z",
    "comment": "Maintenance planifiée",
    "createdBy": "ops-team"
  }'

# Lister les silences actifs
curl http://localhost:9093/api/v2/silences | jq '.[] | select(.status.state == "active")'

Stack complète sur Kubernetes avec Helm

En production sur Kubernetes, le kube-prometheus-stack (anciennement prometheus-operator) est la solution de référence. Il déploie Prometheus, Grafana, Alertmanager et tous les exporters Kubernetes en une seule commande Helm.

Installation via Helm

# Ajouter le repo Helm
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# Créer le namespace dédié
kubectl create namespace monitoring

# Installer la stack complète
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --set grafana.adminPassword="MonMotDePasseSecurise!" \
  --set prometheus.prometheusSpec.retention=30d \
  --set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage=50Gi

values.yaml personnalisé

# values.yaml
grafana:
  adminPassword: "ChangeMeInProduction"
  ingress:
    enabled: true
    ingressClassName: nginx
    annotations:
      cert-manager.io/cluster-issuer: letsencrypt-prod
    hosts:
      - grafana.mondomaine.com
    tls:
      - secretName: grafana-tls
        hosts:
          - grafana.mondomaine.com
  persistence:
    enabled: true
    size: 10Gi
  # Dashboards additionnels depuis grafana.com
  dashboards:
    default:
      node-exporter:
        gnetId: 1860
        revision: 29
        datasource: Prometheus

prometheus:
  prometheusSpec:
    # Scraper TOUS les ServiceMonitors du cluster
    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false
    retention: 30d
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: standard
          resources:
            requests:
              storage: 50Gi

alertmanager:
  alertmanagerSpec:
    storage:
      volumeClaimTemplate:
        spec:
          resources:
            requests:
              storage: 5Gi
# Appliquer le fichier values
helm upgrade kube-prometheus-stack prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --values values.yaml

Annoter vos Pods pour être scrapés

# deployment.yaml — votre application
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mon-api
spec:
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"       # activer le scraping
        prometheus.io/port: "3001"         # port de l'app
        prometheus.io/path: "/metrics"     # chemin des métriques
    spec:
      containers:
        - name: mon-api
          image: mon-registry/mon-api:latest
          ports:
            - containerPort: 3001

ServiceMonitor pour un scraping propre

# servicemonitor.yaml — méthode recommandée avec Prometheus Operator
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: mon-api-monitor
  namespace: monitoring
  labels:
    release: kube-prometheus-stack    # doit matcher le label du stack Helm
spec:
  selector:
    matchLabels:
      app: mon-api                    # label du Service Kubernetes
  namespaceSelector:
    matchNames:
      - production
  endpoints:
    - port: http                      # nom du port dans le Service
      path: /metrics
      interval: 15s

Vérifier l'état de la stack

# Voir tous les pods du monitoring
kubectl get pods -n monitoring

# Port-forward Grafana en local
kubectl port-forward -n monitoring svc/kube-prometheus-stack-grafana 3000:80

# Port-forward Prometheus en local
kubectl port-forward -n monitoring svc/kube-prometheus-stack-prometheus 9090:9090

# Voir les ServiceMonitors actifs
kubectl get servicemonitors -n monitoring

# Vérifier les targets Prometheus
kubectl port-forward -n monitoring svc/kube-prometheus-stack-prometheus 9090:9090 &
curl http://localhost:9090/api/v1/targets | jq '.data.activeTargets | length'

Bonnes pratiques et coûts

Optimisation des performances Prometheus

# Limiter la cardinalité des labels (CRITIQUE)
# ❌ Mauvais : cardinalité explosive (un label par user_id)
http_requests_total{user_id="12345", ...}

# ✅ Bon : labels à cardinalité faible
http_requests_total{method="GET", route="/api/users", status="200"}

# Réduire la rétention si l'espace disque est limité
--storage.tsdb.retention.time=15d   # 15 jours au lieu de 30

# Activer la compression TSDB
--storage.tsdb.wal-compression

# Estimer la taille des données
# Règle empirique : ~1-2 Mo par série temporelle par jour
# 1000 séries × 30 jours = 30-60 Go → prévoir un volume adapté

Checklist de mise en production

  • HTTPS obligatoire : Grafana derrière Nginx + Certbot ou Ingress avec cert-manager
  • Authentification : désactiver l'accès anonyme Grafana (GF_AUTH_ANONYMOUS_ENABLED=false)
  • Secrets externalisés : mots de passe dans Kubernetes Secrets ou Vault, jamais en clair
  • Rétention calibrée : 30 jours en TSDB locale, long terme via Thanos ou Cortex
  • Backups Grafana : exporter régulièrement dashboards + alertes en JSON
  • Alertes testées : simuler chaque alerte avant la mise en prod
  • Cardinalité surveillée : prometheus_tsdb_head_series doit rester sous 1M
  • Ressources limitées : définir resources.limits dans Kubernetes
  • High availability : 2 replicas Prometheus avec Thanos Sidecar en prod critique
  • Runbooks liés : chaque alerte doit pointer vers un runbook d'actions correctives

Estimation des coûts

Environnement Ressources Coût mensuel estimé
Dev / Staging Docker Compose (VPS 2 vCPU / 4 Go) ~12 €/mois (DigitalOcean Droplet)
Petite prod K8s 2 nœuds + 50 Go stockage ~80-120 €/mois (AWS EKS ou GKE)
Grafana Cloud Free tier (10K series, 50 Go logs) Gratuit jusqu'à 10 000 séries
Self-hosted HA Thanos + Object Storage S3 +5-15 €/mois pour S3 (1 To)
Alternative gratuite : Grafana Cloud Free offre 10 000 séries Prometheus, 50 Go de logs et 14 jours de rétention sans frais. Idéal pour démarrer sans infrastructure.

Alternatives et compléments

Outil Rôle Quand l'utiliser
Thanos HA + long-term storage Prometheus Rétention > 30 jours, multi-cluster
Loki Logs (même stack Grafana) Centraliser les logs Docker/K8s
Tempo Tracing distribué Deboguer les microservices lents
Victoria Metrics TSDB drop-in replacement Prometheus Meilleure compression, moins de RAM
Datadog / New Relic Observabilité SaaS complète Budget disponible, zéro ops

Conclusion

Grafana + Prometheus forment la stack d'observabilité la plus adoptée dans l'écosystème cloud-native. Avec Docker Compose, vous êtes opérationnel en 15 minutes ; avec Kubernetes et Helm, vous bénéficiez d'une solution production-ready, scalable et maintenable.

L'investissement dans une bonne observabilité se rembourse rapidement : réduction du MTTR, détection précoce des anomalies et confiance lors des déploiements. Complétez la stack avec Loki pour les logs et Tempo pour le tracing afin d'atteindre l'observabilité complète.

Pour aller plus loin :

Partager