TP21 : Observabilité (Prometheus, Grafana, Loki, Tempo, OpenTelemetry, SLO)

Surveiller (monitoring) répond à « est-ce que ça marche ? ». L’observabilité permet de répondre à « pourquoi ça ne marche pas ? », y compris pour une panne jamais vue, grâce à trois signaux reliés entre eux : les métriques (combien, à quelle vitesse), les logs (ce qui s’est passé) et les traces (le parcours d’une requête à travers les services). Ce TP instrumente une petite application et monte la pile open source de référence : Prometheus + Grafana + Loki + Tempo + OpenTelemetry, puis définit un SLO et l’alerte associée.


Fichiers de départ : demo-app.zip, k8s.zip, runbook-template.md, values.zip

1. Objectifs pédagogiques

À l’issue du TP, vous serez capable de :

  • installer kube-prometheus-stack et lire les dashboards du cluster ;
  • exposer des métriques applicatives avec prom-client et les faire collecter par un ServiceMonitor ;
  • écrire des requêtes PromQL (taux, ratio d’erreurs, percentile de latence) et un dashboard Grafana ;
  • centraliser les logs avec Loki + Grafana Alloy et les interroger en LogQL ;
  • tracer les requêtes avec OpenTelemetry et Tempo, et passer d’un log à sa trace ;
  • définir SLI, SLO, budget d’erreur, écrire une alerte et son runbook.

Les images de cette page sont des schémas explicatifs, pas des captures d’écran.

2. Environnement du TP

  • Un cluster K3s (TP14) avec au moins 6 Go de RAM disponibles pour la pile d’observabilité (le plus simple : K3s mono-nœud sur une VM de lab de 16 Go), kubectl et Helm.

  • Construire l’image de demo-app/ et la rendre disponible au cluster, au choix :

    docker build -t <utilisateur>/observability-demo:1.0.0 demo-app && docker push <utilisateur>/observability-demo:1.0.0
    # ou, sans registre, sur un K3s mono-nœud :
    docker save observability-demo:1.0.0 | sudo k3s ctr images import -

    Reconstruire l’image après chaque partie qui modifie le code (2 et 4), avec un nouveau tag.

Pile d’observabilité : métriques, logs et traces vers Grafana Pile d’observabilité : métriques, logs et traces vers Grafana

Signal Question Outil du TP Langage de requête
Métriques Combien ? À quelle vitesse ? Prometheus PromQL
Logs Que s’est-il passé ? Loki (+ Alloy) LogQL
Traces Où le temps est-il passé ? Tempo (+ OpenTelemetry) TraceQL
Visualisation et alertes — Grafana, Alertmanager —

3. Partie 1 : kube-prometheus-stack

  • Installer la pile (Prometheus Operator, Prometheus, Alertmanager, Grafana, node-exporter, kube-state-metrics) avec les valeurs de values/kube-prometheus-stack.yaml (changer le mot de passe Grafana) :

    helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
    helm repo add grafana https://grafana.github.io/helm-charts && helm repo update
    helm install kps prometheus-community/kube-prometheus-stack -n monitoring --create-namespace \
      -f values/kube-prometheus-stack.yaml
    kubectl -n monitoring get pods
    kubectl -n monitoring port-forward svc/kps-grafana 3000:80 --address 0.0.0.0 &
  • Dans Grafana (http://<IP-VM-lab>:3000), ouvrir les dashboards Kubernetes / Compute Resources / Namespace (Pods) et Node Exporter / Nodes : relever CPU, mémoire, nombre de Pods et redémarrages.

  • Dans Explore → Prometheus, tester : up, kube_pod_container_status_restarts_total, sum by (namespace) (container_memory_working_set_bytes).

4. Partie 2 : Métriques applicatives

  • Compléter le TODO de demo-app/src/server.js : métriques par défaut, histogramme http_request_duration_seconds (labels method, route, status_code) et route /metrics.

    Cardinalité : n’utilisez jamais une valeur libre (identifiant utilisateur, URL complète avec paramètres) comme label. Chaque combinaison de labels crée une série temporelle.

  • Déployer l’application (k8s/app.yaml, en remplaçant l’image), compléter et appliquer k8s/servicemonitor.yaml, puis vérifier la cible dans Prometheus (Status → Targets) :

    kubectl -n monitoring port-forward svc/kps-kube-prometheus-stack-prometheus 9090 &
  • Générer du trafic :

    kubectl -n tp21 run load --image=busybox:1.37 --restart=Never -- sh -c \
      'while true; do wget -qO- http://demo/ >/dev/null; wget -qO- http://demo/api/orders >/dev/null; wget -qO- http://demo/slow >/dev/null; sleep 0.2; done'
  • Écrire les requêtes PromQL suivantes et en faire un dashboard Grafana « demo » (variables route comprises) :

Panneau Requête à écrire
Requêtes par seconde sum by (route) (rate(http_request_duration_seconds_count[1m]))
Taux d’erreur (%) ratio des status_code=~"5.." sur le total
Latence p50 / p95 / p99 histogram_quantile(0.95, sum by (le) (rate(..._bucket[5m])))
Mémoire des Pods container_memory_working_set_bytes{namespace="tp21"}
Redémarrages kube_pod_container_status_restarts_total{namespace="tp21"}

5. Partie 3 : Logs avec Loki et Alloy

  • Installer Loki (mode monolithique) puis Alloy :

    helm install loki grafana/loki -n monitoring -f values/loki.yaml
    helm install alloy grafana/alloy -n monitoring -f values/alloy.yaml
  • Dans Explore → Loki, interroger les logs de l’application :

    {namespace="tp21", app="demo"}
    {namespace="tp21"} | json | res_statusCode >= 500
    sum by (pod) (count_over_time({namespace="tp21"} |= "échec simulé" [5m]))
  • Retrouver tous les logs d’une requête à partir de son x-request-id (le correlation ID déjà généré par l’application) :

    kubectl -n tp21 run curl --rm -it --image=curlimages/curl:8.15.0 --restart=Never -- -si http://demo/api/orders

6. Partie 4 : Traces avec OpenTelemetry et Tempo

  • Installer Tempo :

    helm install tempo grafana/tempo -n monitoring -f values/tempo.yaml
  • Compléter demo-app/src/tracing.js (SDK Node, exporteur OTLP HTTP, instrumentation automatique), reconstruire l’image et redéployer. La variable OTEL_EXPORTER_OTLP_ENDPOINT du Deployment pointe déjà vers Tempo.

  • Appeler /api/checkout (appel HTTP sortant vers /slow) puis, dans Explore → Tempo, ouvrir une trace : identifier le span serveur Express et le span client HTTP enfant, et leurs durées.

  • Rechercher les traces lentes ou en erreur avec TraceQL :

    { resource.service.name = "observability-demo" && duration > 1s }
    { status = error }
  • Corrélation : ajouter le trace_id dans les logs (bonus du TODO), puis, depuis une trace dans Grafana, cliquer sur Logs for this span pour afficher les logs correspondants dans Loki.

7. Partie 5 : SLI, SLO, alerte et runbook

SLI, SLO et budget d’erreur SLI, SLO et budget d’erreur

Terme Définition Exemple
SLI Indicateur mesuré du niveau de service part de requêtes non-5xx
SLO Objectif interne sur ce SLI, sur une période ≥ 99 % sur 30 jours
Budget d’erreur Ce que le SLO autorise à échouer 1 % des requêtes
SLA Engagement contractuel envers un client, avec pénalités 98 %, remboursement sinon
  • Appliquer k8s/prometheusrule.yaml : la règle d’enregistrement demo:availability:ratio_rate5m calcule le SLI. Compléter l’alerte DemoAvailabilitySLOBreach (SLI < 0,99 pendant 5 min).
  • Rédiger le runbook à partir de runbook-template.md et mettre son lien dans runbook_url.
  • Provoquer l’incident : kubectl -n tp21 set env deploy/demo ERROR_RATE=0.3, puis suivre l’alerte dans Prometheus (Alerts : pending puis firing) et dans Alertmanager (kubectl -n monitoring port-forward svc/kps-kube-prometheus-stack-alertmanager 9093).
  • Mener le diagnostic en suivant le runbook (métriques → logs → traces), puis rétablir ERROR_RATE=0.05 et observer la résolution de l’alerte.
  • Calculer : avec un SLO de 99 % sur 30 jours, combien de minutes d’indisponibilité totale le budget d’erreur autorise-t-il ? Et avec 99,9 % ?

Défi

  1. Remplacer l’alerte à seuil par une alerte multi-window, multi-burn-rate (fenêtres 1 h / 5 min et 6 h / 30 min) comme recommandé par le Google SRE Workbook.
  2. Configurer Alertmanager pour envoyer les alertes vers un webhook de test (par exemple un conteneur mendhak/http-https-echo) et afficher le JSON reçu.

8. Livrables

Captures à déposer dans captures/ :

Fichier Contenu attendu
cluster-dashboard.png Dashboard Kubernetes : CPU, mémoire, Pods, redémarrages
target-up.png Cible demo à l’état UP dans Prometheus
demo-dashboard.png Dashboard « demo » : débit, erreurs, latences p50 / p95 / p99
loki-logql.png Logs filtrés par LogQL et par x-request-id
tempo-trace.png Trace /api/checkout avec span serveur et span client
trace-to-logs.png Passage d’une trace à ses logs
slo-alert-firing.png Alerte DemoAvailabilitySLOBreach en état firing

Ainsi que : le code instrumenté, les manifests, l’export JSON du dashboard « demo » et le runbook rempli.