Skip to content

Alerting : de la métrique au message Slack

Ce guide explique comment une alerte naît d'une métrique, mûrit, et finit par te prévenir dans Slack. Objectif : comprendre chaque maillon pour ne pas subir une alerte qui se déclenche (ou pire, une qui ne se déclenche pas).

ADR lié (le pourquoi des décisions) : Alerting Alertmanager + Slack. Pré-requis utile : Comprendre l'observabilité.


L'idée en une image

L'observabilité, c'est le cockpit (les cadrans, le journal de bord). L'alerting, c'est le détecteur de fumée : tu ne regardes pas les cadrans en permanence, tu veux être réveillé seulement quand quelque chose cloche vraiment.

Tout l'enjeu d'une bonne alerte tient en une phrase :

Se déclencher quand il faut (sinon on rate la panne), et seulement quand il faut (sinon on coupe le détecteur à force de fausses alertes).


La chaîne complète

métrique (Prometheus)
   │  une PrometheusRule l'évalue en boucle
   ▼
condition vraie ──► Pending ──(for: écoulé)──► Firing
   │                                              │
   │                                              ▼
   │                                        Alertmanager
   │                                  (groupe, route, inhibe)
   │                                              │
   ▼                                              ▼
résolu ──► Resolved ──────────────────────►   Slack

Trois acteurs, trois rôles distincts :

Acteur Rôle Ne fait PAS
Prometheus (via PrometheusRule) évalue les conditions, décide Pending/Firing n'envoie aucune notification
Alertmanager reçoit les alertes déclenchées (firing), les groupe, route, inhibe ne décide pas si une alerte se déclenche
Slack affiche le message rien d'autre

Erreur classique : croire que « Prometheus envoie l'alerte ». Non. Prometheus déclare qu'une alerte se déclenche ; c'est Alertmanager qui décide quoi en faire et où l'envoyer.


Le cycle de vie d'une alerte

C'est le concept central. Une alerte passe par des états :

État Couleur Signification
Normal (Inactive) vert la condition est fausse, tout va bien
Pending orange la condition est vraie, mais on attend pour confirmer
Firing rouge la condition est vraie depuis assez longtemps → on alerte
Resolved la condition est redevenue fausse → on prévient que c'est fini

Le rôle du for: (l'anti-faux-positif)

Le champ for: est le délai de confirmation. Une règle avec for: 5m dit :

« Ne se déclenche que si la condition reste vraie 5 minutes d'affilée. »

Pourquoi ? Parce qu'un pod qui redémarre une fois, un pic de latence d'une seconde, ce n'est pas une panne, c'est la vie normale d'un système. Le for: filtre le bruit transitoire. C'est lui qui transforme Pending (orange, « ça pourrait être un souci ») en Firing (rouge, « c'est un vrai souci »).

condition  : faux ──── vrai ──── vrai ──── vrai ──── vrai
état       : Normal    Pending   Pending   Firing!   Firing
                       └──────── for: 5m ────────┘

Les 4 alertes FastAPI

Elles reprennent exactement les métriques du dashboard RED (#74) : pas de devinette, on alerte sur ce qu'on mesure déjà.

Alerte Question métier for sévérité
FastAPITargetDown « l'appli répond-elle à Prometheus ? » 2m critical
FastAPIPodCrashLooping « un pod redémarre-t-il en boucle ? » 5m critical
FastAPIHighErrorRate « trop de 5xx ? » (Errors du RED) 5m warning
FastAPIHighLatency « trop lent ? » (p99, Duration du RED) 5m warning

Deux familles : la disponibilité (les 2 premières, critical) et la qualité de service (les 2 dernières, warning). La sévérité sert ensuite au routage.


Ce que fait Alertmanager (les 3 verbes)

Une fois qu'une alerte est Firing, elle arrive chez Alertmanager qui applique trois traitements :

1. Grouper (group_wait)

Si 10 pods tombent en même temps, tu ne veux pas 10 messages. Alertmanager attend un court instant (group_wait, ~30s) et regroupe les alertes similaires en une notification. C'est aussi pourquoi le message Slack arrive quelques secondes après le Firing côté Grafana.

2. Router (par sévérité)

Chaque alerte est dirigée selon ses labels. Ici, le routage se fait par severity vers Slack. On pourrait router les critical vers un canal d'astreinte et les warning vers un canal d'équipe.

3. Inhiber (étouffer le redondant)

Si une alerte critical se déclenche, inutile de spammer avec les warning du même namespace : Alertmanager les inhibe (les masque tant que le critical dure).

Les deux alertes qu'on NE veut PAS voir

Alerte Pourquoi elle existe Pourquoi routée null
Watchdog se déclenche en permanence par conception (deadman's switch) si elle s'arrête, c'est que la chaîne d'alerting elle-même est morte. On la surveille ailleurs, pas dans Slack
InfoInhibitor rouage interne du mécanisme d'inhibition des alertes info bruit technique, sans intérêt humain

Le Watchdog est malin : « pas de nouvelles = mauvaises nouvelles ». Une chaîne d'alerting qui ne dit jamais rien peut être cassée sans qu'on le sache. Le Watchdog se déclenche en permanence justement pour prouver que la chaîne est vivante.


⚠️ Le piège qui fait la différence en entretien

FastAPITargetDown s'écrit up == 0. Intuitivement : « si la cible vaut 0, alerte ». Mais tout dépend de comment la cible disparaît.

up est une métrique que Prometheus crée pour chaque cible qu'il scrape. Et une cible n'existe que s'il y a un endpoint (un pod avec une IP derrière le Service).

Situation Ce qui arrive à la série up up == 0 se déclenche ?
Scale à 0 (--replicas=0) plus aucun pod → plus aucune cible → la série disparaît NON
Pod présent mais planté (CrashLoop) la cible existe, le scrape échoue → la série vaut 0 OUI

La nuance : quelque chose qui n'existe plus == 0 ne matche rien (il n'y a aucune série à comparer). Alors que série présente qui vaut 0 matche.

Scale à 0 :   up │ 1 1 1 1 ╳ (la ligne s'ARRÊTE)        → up == 0 : rien à évaluer
CrashLoop  :   up │ 1 1 1 1 0 0 0 (la ligne TOMBE à 0)   → up == 0 : VRAI

Conséquence : up == 0 détecte très bien « un pod mort qui ne répond pas », mais pas la disparition totale (scale-à-0, deployment supprimé). Pour couvrir ce cas, on ajoute la règle FastAPITargetMissing (#93) bâtie sur absent() :

absent(up{namespace="fastapi", endpoint="http"})

absent() se déclenche précisément quand la série n'existe pas. C'est le complément de FastAPITargetDown (up == 0) : les deux règles ensemble couvrent les deux faces de la panne (cible disparue vs cible présente qui ne répond pas).

À retenir : en alerting, « absent » et « égal à zéro » sont deux choses différentes. C'est une des erreurs les plus fréquentes des règles up == 0.


Où est le secret du webhook Slack ?

L'URL du webhook Slack est un secret : elle ne doit jamais être dans Git. Même chaîne que le reste du projet (ESO + IRSA) :

AWS Secrets Manager (fastapi-eks/alertmanager-slack)
        │  ESO + IRSA (least-privilege)
        ▼
Secret K8s (alertmanager-slack)  ──monté dans──►  Alertmanager
        lu via slack_configs.api_url_file (jamais en clair dans Git)

C'est le même pattern que le mot de passe Grafana (#76). Voir Comprendre l'observabilité et l'ADR External Secrets Operator + IRSA.


Bonus : valider sans tout casser

  • Tester le routage sans provoquer de panne : amtool alert add injecte une alerte synthétique → on vérifie qu'elle arrive bien dans Slack (firing puis resolved), et que Watchdog/InfoInhibitor n'y arrivent pas.
  • Valider les règles en local (sans cluster) : promtool check rules vérifie la syntaxe des PrometheusRule avant de merger.
  • Provoquer une vraie alerte (test de bout en bout) : voir le runbook de validation, section Alerting.