Monitoring

Monitoring K8s : Prometheus + Grafana de zéro

05.03.2026 10 min de lecture Nicolas Wibart

Tu peux avoir le plus beau cluster Kubernetes du monde, sans observabilité, tu pilotes à l'aveugle. Voici comment j'ai mis en place une stack Prometheus + Grafana complète sur mon cluster on-premise, et surtout comment j'évite les fausses alertes à 3h du matin.

L'architecture en bref

namespace: monitoring node-exporter kube-state-metrics app /metrics Prometheus scrape · store · alert Grafana Alertmanager
Stack complète : exporters → Prometheus → Grafana / Alertmanager

Installation : kube-prometheus-stack

Plutôt que de gérer 12 manifests à la main, j'utilise le chart Helm kube-prometheus-stack de la communauté Prometheus. Il installe d'un coup : Prometheus Operator, Grafana, Alertmanager, node-exporter, kube-state-metrics, et tous les ServiceMonitors qui vont bien.

helm repo add prometheus-community \
  https://prometheus-community.github.io/helm-charts
helm repo update

helm install kps prometheus-community/kube-prometheus-stack \
  --namespace monitoring --create-namespace \
  --values values.yaml

Le fichier values.yaml est l'endroit où tout se joue :

prometheus:
  prometheusSpec:
    retention: 15d
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: longhorn
          resources:
            requests:
              storage: 20Gi

grafana:
  adminPassword: "{{ vault_grafana_pwd }}"
  ingress:
    enabled: true
    ingressClassName: traefik
    hosts:
      - grafana.lab.local

alertmanager:
  config:
    route:
      receiver: discord
      group_wait: 30s
      repeat_interval: 4h
Persistence, par pitié Si tu skip le storageSpec, ton Prometheus perd toutes ses métriques au prochain restart de pod. C'est un coup classique. Toujours du PVC.

Premiers dashboards

Le chart pré-installe ~15 dashboards "canoniques" : Cluster Overview, Node Exporter Full, Pods, API Server. Pour 90% des usages quotidiens, ça suffit. Mes 3 préférés :

Écrire une PrometheusRule (la bonne manière)

Le piège quand on débute, c'est d'écrire des alertes qui se déclenchent pour rien. La règle d'or : une alerte doit être actionnable. Si tu reçois un ping et que tu ne sais pas quoi faire, c'est qu'elle ne devrait pas exister.

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: app-rules
  namespace: monitoring
spec:
  groups:
    - name: fastapi.rules
      rules:
        - alert: FastAPIHighErrorRate
          expr: |
            sum(rate(http_requests_total{status=~"5.."}[5m]))
              /
            sum(rate(http_requests_total[5m])) > 0.05
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: "FastAPI: >5% d'erreurs 5xx"
            runbook: "https://wiki.lab.local/runbooks/fastapi-5xx"

Trois trucs essentiels dans cette règle :

  1. for: 10m ; l'alerte doit rester vraie 10 minutes avant de claquer. Ça filtre 90% du bruit.
  2. severity: warning ; pas tout en critical. Réserve le pour ce qui réveille à 3h.
  3. runbook ; un lien vers la procédure. Si t'as pas de procédure, écris-en une avant l'alerte.
L'alerte qui m'a appris la patience Au début, j'avais une alerte "Pod restarted" sans for:. Résultat : 47 notifications le jour où ArgoCD a déployé une nouvelle version. Depuis, je mets toujours un for: et je me demande "est-ce que je veux être réveillé pour ça ?".

Routage Alertmanager : silencieux en journée, sérieux la nuit

Tu peux router les alertes selon leur sévérité, leur namespace, ou même l'heure. J'ai mis en place un routage simple : warning → Discord (canal #alerts), critical → Discord + ping personnel.

route:
  receiver: discord-warn
  routes:
    - match:
        severity: critical
      receiver: discord-crit
      continue: true

Ce que cette stack m'a appris

"Tu ne peux pas opérer ce que tu ne mesures pas. Et tu ne peux pas dormir avec des alertes qui crient pour rien."
Retour au journal $ echo "you build it, you run it"