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 │
└──────────┘ └──────────┘ └──────────────┘
- 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
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_seriesdoit rester sous 1M - Ressources limitées : définir
resources.limitsdans 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) |
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.