Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

Alerting : Alertmanager et notifications

Par mis à jour le

Améliorer cette page

Vous avez maintenant des métriques dans Prometheus et des dashboards dans Grafana. Mais personne ne regarde les dashboards 24/7. Vous avez besoin d'être notifié automatiquement quand quelque chose ne va pas.

C'est le rôle de l'alerting. Dans ce module, vous allez apprendre à créer des règles d'alerte pertinentes et à configurer Alertmanager pour envoyer les notifications aux bonnes personnes.

À la fin de ce module, vous saurez :

  1. Expliquer l'architecture de l'alerting Prometheus (qui fait quoi)
  2. Créer des règles d'alerte dans Prometheus et les tester avec promtool test rules
  3. Configurer Alertmanager pour router et notifier
  4. Envoyer des alertes vers Slack, email, ou webhook
  5. Gérer les silences (maintenance) et les inhibitions
  6. Appliquer les bonnes pratiques d'alerting
  1. Prometheus fonctionne

    Fenêtre de terminal
    kubectl get pods -n observability | grep -E 'prometheus-server|kube-state-metrics|node-exporter'

    Les pods doivent être en Running.

  2. L'application OpenTelemetry Demo génère du trafic

    Fenêtre de terminal
    kubectl get pods -n otel-demo | head -10

    Plusieurs pods en Running.

  3. Vous êtes dans le répertoire du lab

    Fenêtre de terminal
    cd ~/lab-observability

L'alerting Prometheus implique deux composants distincts :

Chaîne d'alerte : Prometheus évalue les règles et détecte les alertes firing, puis les pousse à Alertmanager, qui déduplique, groupe, route et notifie les receivers e-mail, Slack, PagerDuty ou webhook.PrometheusAlertmanagerReceivers1. Évalue les règles2. Détecte les firing3. Déduplique4. Groupe5. Route6. NotifieE-mailSlackPagerDutyWebhookpush des alertes
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

Pourquoi deux composants ?

Prometheus (les règles)Alertmanager (les notifications)
"Quelque chose ne va pas""Qui prévenir et comment"
Évalue des conditions PromQLGère les destinataires
Détecte que la latence > 500msEnvoie un message Slack à l'équipe backend
Ne sait pas qui contacterNe sait pas ce qui ne va pas

Cette séparation permet :

  • De changer les destinataires sans modifier les règles
  • D'avoir un seul Alertmanager pour plusieurs Prometheus
  • De gérer les silences et regroupements indépendamment

Comprendre le cycle de vie vous évitera beaucoup de confusion :

Cycle de vie d'une alerte : INACTIVE passe à PENDING quand la condition devient vraie, PENDING passe à FIRING quand la durée for est atteinte, et une condition redevenue fausse ramène PENDING ou FIRING à INACTIVE.condition vraiefor: atteintcondition faussecondition fausseINACTIVEPENDINGFIRING
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %
ÉtatSignificationLes notifications partent ?
InactiveLa condition est fausseNon
PendingLa condition est vraie, mais pas depuis assez longtemps (for pas atteint)Non
FiringLa condition est vraie depuis la durée forOui
ResolvedLa condition redevient fausse après avoir été firingOui (optionnel)

Exemple concret :

alert: ExempleLatence
expr: http_latency > 500
for: 5m # ← L'alerte doit être vraie pendant 5 min avant de "firing"
  • T+0min : latence passe à 600ms → Pending
  • T+3min : latence toujours à 600ms → Toujours Pending (pas encore 5 min)
  • T+5min : latence toujours à 600ms → Firing (notification envoyée)
  • T+6min : latence redescend à 200ms → Resolved (notification "resolved")

Le chart prometheus-community/prometheus inclut Alertmanager. Vérifions qu'il est activé et configurons-le pour notre lab.

  1. Examinez la configuration

    Fenêtre de terminal
    cat 04-alerting/helm-values/alertmanager.yaml

    Ce que vous voyez :

    alertmanager:
    enabled: true
    persistentVolume:
    enabled: true
    size: 2Gi
    config:
    global:
    resolve_timeout: 5m
    route:
    receiver: 'default'
    group_by: ['alertname', 'severity']
    group_wait: 30s
    group_interval: 5m
    repeat_interval: 4h
    receivers:
    - name: 'default'
    webhook_configs:
    - url: 'https://webhook.site/VOTRE-UUID'
    send_resolved: true

    Cette configuration :

    • Active Alertmanager
    • Configure un receiver webhook pour les tests
    • Groupe les alertes par nom et sévérité
  2. Appliquez la configuration

    Fenêtre de terminal
    helm upgrade prometheus prometheus-community/prometheus \
    -n observability \
    -f 02-prometheus/helm-values/prometheus-minimal.yaml \
    -f 04-alerting/helm-values/alertmanager.yaml \
    --wait

    Ce que vous devez voir :

    Release "prometheus" has been upgraded.
  3. Vérifiez qu'Alertmanager tourne

    Fenêtre de terminal
    kubectl get pods -n observability | grep alertmanager

    Ce que vous devez voir :

    prometheus-alertmanager-0 1/1 Running 0 1m
  4. Accédez à l'interface Alertmanager

    Fenêtre de terminal
    kubectl port-forward svc/prometheus-alertmanager 9093:9093 -n observability

    Ouvrez http://localhost:9093

    Ce que vous voyez : L'interface Alertmanager avec (probablement) aucune alerte pour l'instant.

Avant de créer des règles, comprenons leur structure :

groups:
- name: nom_du_groupe # Groupe logique de règles
interval: 30s # Fréquence d'évaluation (optionnel)
rules:
- alert: NomAlerte # ① Nom unique de l'alerte
expr: up == 0 # ② Expression PromQL (condition)
for: 2m # ③ Durée avant firing
labels: # ④ Labels ajoutés à l'alerte
severity: critical
team: platform
annotations: # ⑤ Informations humaines
summary: "Target {{ $labels.instance }} is down"
description: "The target {{ $labels.job }}/{{ $labels.instance }} has been down for more than 2 minutes."
runbook_url: "https://runbooks.infra.internal/target-down"

Décortiquons chaque partie :

PartieRôleBonnes pratiques
① alertIdentifie l'alerteUtiliser PascalCase, être descriptif : HighLatencyCheckout
② exprCondition PromQLDoit retourner des séries (pas une valeur scalaire)
③ forDélai anti-faux-positifValeurs raccourcies dans ce lab (voir ci-dessous), plus longues en production
④ labelsMétadonnées pour le routingseverity, team, service
⑤ annotationsTexte pour les humainssummary (court), description (détaillé), runbook_url (lien vers la procédure)

Les durées for de ce lab sont volontairement raccourcies : 2 à 5 minutes pour les règles critical, 5 à 10 minutes pour les warning. Elles permettent de voir l'alerte passer de Pending à Firing pendant l'exercice. En production, la leçon Alerting efficace recommande plutôt 5 minutes pour une alerte critical et 15 à 30 minutes pour une warning, afin d'absorber les pics sans réveiller personne. Le nom de l'annotation runbook_url suit la convention de Prometheus et Alertmanager : les receivers peuvent la réutiliser avec {{ .Annotations.runbook_url }}.

Les templates dans les annotations :

Vous pouvez utiliser des variables dans les annotations :

VariableDescriptionExemple
{{ $labels.xyz }}Valeur du label xyz{{ $labels.instance }} → 10.0.1.5:9090
{{ $value }}Valeur de l'expression{{ $value | printf "%.2f" }} → 0.85
{{ .Labels }}Tous les labels
{{ .Annotations }}Toutes les annotations

Nous allons créer un fichier de règles et l'ajouter à Prometheus.

Ces règles d'infrastructure surveillent des causes possibles (processeur, mémoire, disque), pas ce que vit l'utilisateur. HighCPUUsage et HighMemoryUsage sont des alertes de capacité : elles sont en sévérité warning, ne réveillent personne et servent à anticiper une saturation avant qu'elle ne dégrade le service. Le réveil de nuit reste réservé aux symptômes comme la latence ou le taux d'erreur : HighLatency et HighErrorRate, écrites plus bas, sont donc en critical. Dans ce fichier d'infrastructure, seules TargetDown et DiskSpaceLow sont en critical, parce qu'une cible muette ou un disque presque plein appellent une action immédiate.

Créez le fichier 04-alerting/rules/infrastructure.yaml :

groups:
- name: infrastructure
rules:
# ============================================
# Alerte 1 : Un target ne répond plus
# ============================================
- alert: TargetDown
expr: up == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Target {{ $labels.job }}/{{ $labels.instance }} is down"
description: |
The target {{ $labels.instance }} in job {{ $labels.job }}
has been down for more than 2 minutes.
Possible causes:
- The application has crashed
- Network issue between Prometheus and target
- The /metrics endpoint is not responding
runbook_url: "https://runbooks.infra.internal/target-down"
# ============================================
# Alerte 2 : Mémoire haute
# ============================================
- alert: HighMemoryUsage
expr: |
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
for: 5m
labels:
severity: warning
annotations:
summary: "Memory usage above 85% on {{ $labels.instance }}"
description: |
The node {{ $labels.instance }} is using more than 85% of its memory.
Current usage: {{ $value | printf "%.1f" }}%
Check for memory leaks or consider scaling.
value: "{{ $value | printf \"%.1f\" }}%"
# ============================================
# Alerte 3 : CPU haute
# ============================================
- alert: HighCPUUsage
expr: |
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 10m
labels:
severity: warning
annotations:
summary: "CPU usage above 80% on {{ $labels.instance }}"
description: |
The node {{ $labels.instance }} has been using more than 80% CPU
for the last 10 minutes.
value: "{{ $value | printf \"%.1f\" }}%"
# ============================================
# Alerte 4 : Disque bientôt plein
# ============================================
- alert: DiskSpaceLow
expr: |
(node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes) < 0.10
for: 5m
labels:
severity: critical
annotations:
summary: "Disk space below 10% on {{ $labels.instance }}:{{ $labels.mountpoint }}"
description: |
The filesystem {{ $labels.mountpoint }} on {{ $labels.instance }}
has less than 10% free space.
Consider cleaning up or expanding the disk.
runbook_url: "https://runbooks.infra.internal/disk-space-low"

Avant de créer des règles sur l'application OTel Demo, découvrons les métriques disponibles.

Créez le fichier 04-alerting/rules/application.yaml :

groups:
- name: otel-demo-application
rules:
# ============================================
# Alerte : Collector OTel down
# ============================================
- alert: OtelCollectorDown
expr: up{job="otel-collector"} == 0
for: 2m
labels:
severity: critical
team: platform
annotations:
summary: "OpenTelemetry Collector is down"
description: |
The OpenTelemetry Collector is not responding.
All application metrics may be missing.
Check: kubectl get pods -n otel-demo -l app.kubernetes.io/name=opentelemetry-collector
runbook_url: "https://runbooks.infra.internal/otel-collector-down"
# ============================================
# Alerte : Latence P99 trop haute (symptôme : critical)
# ============================================
- alert: HighLatency
expr: |
histogram_quantile(0.99,
sum by (service_name, le) (
rate(otel_http_server_duration_seconds_bucket[5m])
)
) > 0.5
for: 2m
labels:
severity: critical
team: backend
annotations:
summary: "P99 latency above 500ms for {{ $labels.service_name }}"
description: |
The service {{ $labels.service_name }} has a P99 latency above 500ms.
This means 1% of users experience latency > 500ms.
Current P99: {{ $value | printf "%.3f" }}s
runbook_url: "https://runbooks.infra.internal/high-latency"
value: "{{ $value | printf \"%.3f\" }}s"
# ============================================
# Alerte : Taux d'erreur trop haut (calcul en %)
# ============================================
- alert: HighErrorRate
expr: |
(
sum by (service_name) (
rate(otel_http_server_duration_seconds_count{http_status_code=~"5.."}[5m])
)
/
sum by (service_name) (
rate(otel_http_server_duration_seconds_count[5m])
)
) * 100 > 1
for: 5m
labels:
severity: critical
team: backend
annotations:
summary: "Error rate above 1% for {{ $labels.service_name }}"
description: |
The service {{ $labels.service_name }} returns more than 1% of 5xx errors.
This directly impacts users.
Current error rate: {{ $value | printf "%.2f" }}%
runbook_url: "https://runbooks.infra.internal/high-error-rate"
value: "{{ $value | printf \"%.2f\" }}%"
# ============================================
# Alerte : Plus aucune requête (service cassé ?)
# ============================================
- alert: NoTraffic
expr: |
sum by (service_name) (
rate(otel_http_server_duration_seconds_count[5m])
) == 0
for: 10m
labels:
severity: warning
team: platform
annotations:
summary: "No HTTP traffic for {{ $labels.service_name }}"
description: |
The service {{ $labels.service_name }} has not received any HTTP request
in the last 10 minutes. Either the service is broken or traffic
is being routed elsewhere.

Comment tester ses règles d'alerte avant de les déployer ?

Section intitulée « Comment tester ses règles d'alerte avant de les déployer ? »

Tester une règle d'alerte, c'est la rejouer sur des séries fabriquées à la main avec promtool test rules, avant qu'elle n'atteigne Prometheus. Une règle qui ne se déclenche jamais ne se voit pas : elle ne produit ni erreur ni notification, et ce silence passe pour de la bonne santé. promtool est l'outil en ligne de commande livré avec Prometheus, et il offre deux contrôles qui répondent à deux questions différentes.

CommandeQuestion poséeCe qu'elle ne voit pas
promtool check rulesLe fichier est-il syntaxiquement valide ?Une expression valide mais fausse, un seuil inversé, un for trop long
promtool test rulesLa règle se comporte-t-elle comme prévu sur des données données ?Les données réelles de votre cluster

Le geste est le même dans d'autres contextes du site : la leçon Exploiter : journaux, alertes, incident et changements teste une règle de quota cloud, et le guide KPIs sécurité DevSecOps teste des règles de mesure de posture. Ici, on l'applique aux règles TargetDown et HighErrorRate que vous venez d'écrire.

Créez un dossier de travail avec le fichier rules.yml. C'est une copie resserrée des règles du lab : expr, for et labels sont identiques, mais les descriptions longues et les annotations value sont retirées ou raccourcies pour garder le test lisible. Seules deux des trois règles seront testées ici ; HighLatency s'appuie sur un histogramme et reste hors de cet exercice.

groups:
- name: demo-app
rules:
- alert: TargetDown
expr: up == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Target {{ $labels.job }}/{{ $labels.instance }} is down"
runbook_url: "https://runbooks.infra.internal/target-down"
- alert: HighLatency
expr: |
histogram_quantile(0.99,
sum by (service_name, le) (
rate(otel_http_server_duration_seconds_bucket[5m])
)
) > 0.5
for: 2m
labels:
severity: critical
team: backend
annotations:
summary: "P99 latency above 500ms for {{ $labels.service_name }}"
runbook_url: "https://runbooks.infra.internal/high-latency"
- alert: HighErrorRate
expr: |
(
sum by (service_name) (
rate(otel_http_server_duration_seconds_count{http_status_code=~"5.."}[5m])
)
/
sum by (service_name) (
rate(otel_http_server_duration_seconds_count[5m])
)
) * 100 > 1
for: 5m
labels:
severity: critical
team: backend
annotations:
summary: "Error rate above 1% for {{ $labels.service_name }}"
description: |
The service {{ $labels.service_name }} returns more than 1% of 5xx errors.
Current error rate: {{ $value | printf "%.2f" }}%
runbook_url: "https://runbooks.infra.internal/high-error-rate"

Un fichier de test se compose de trois niveaux : l'en-tête qui désigne les règles, les séries d'entrée que vous fabriquez, puis les vérifications de ce que doit produire la règle à un instant donné. Créez tests.yml à côté de rules.yml.

rule_files:
- rules.yml
evaluation_interval: 1m
tests:
# TargetDown : up == 0 pendant plus de 2 minutes
- interval: 1m
input_series:
- series: 'up{job="demo-app", instance="demo-app:8080"}'
values: '1 1 0 0 0 0 0'
alert_rule_test:
- eval_time: 3m # down depuis 1 min : encore pending
alertname: TargetDown
exp_alerts: []
- eval_time: 5m # down depuis 3 min : firing
alertname: TargetDown
exp_alerts:
- exp_labels:
severity: critical
job: demo-app
instance: "demo-app:8080"
exp_annotations:
summary: "Target demo-app/demo-app:8080 is down"
runbook_url: "https://runbooks.infra.internal/target-down"
# HighErrorRate : 5 % de 5xx pendant plus de 5 minutes
- interval: 1m
input_series:
# compteur 5xx : +5 par minute ; compteur total : +100 par minute (5 %)
- series: 'otel_http_server_duration_seconds_count{service_name="api", http_status_code="500"}'
values: '0+5x20'
- series: 'otel_http_server_duration_seconds_count{service_name="api", http_status_code="200"}'
values: '0+95x20'
alert_rule_test:
- eval_time: 5m # pas assez longtemps : pending
alertname: HighErrorRate
exp_alerts: []
- eval_time: 15m # for de 5 m écoulé : firing
alertname: HighErrorRate
exp_alerts:
- exp_labels:
severity: critical
team: backend
service_name: api
exp_annotations:
summary: "Error rate above 1% for api"
description: "The service api returns more than 1% of 5xx errors.\nCurrent error rate: 5.00%\n"
runbook_url: "https://runbooks.infra.internal/high-error-rate"

rule_files liste les fichiers de règles à charger, et evaluation_interval fixe le rythme d'évaluation, ici une minute. Dans chaque test, interval est le pas entre deux valeurs d'une série, et input_series déclare les séries avec leurs étiquettes. La notation '1 1 0 0 0 0 0' donne une valeur par pas : la cible répond aux deux premières minutes, puis tombe. La notation '0+5x20' est un raccourci : on part de 0 et on ajoute 5 à chaque pas, 20 fois, soit 21 valeurs au total départ compris, ce qui fabrique un compteur qui croît de façon régulière.

Chaque entrée d'alert_rule_test pose une question : à eval_time, l'alerte alertname produit-elle exactement les alertes de exp_alerts ? Une alerte attendue se décrit par ses exp_labels et ses exp_annotations. Les deux compteurs du second test avancent à +5 et +95 par minute : les erreurs pèsent donc 5 % du trafic, au-dessus du seuil de 1 %, et l'alerte ne part qu'une fois le for: 5m écoulé, d'où le contraste entre 5m et 15m.

Le test négatif compte autant que le positif. À 3m, la cible est tombée depuis une minute seulement : la condition est vraie, mais le for: 2m n'est pas écoulé, l'alerte est donc encore pending et exp_alerts: [] affirme qu'aucune alerte n'est active. À 5m, la cible est tombée depuis trois minutes et l'alerte est firing. Sans ce test négatif, une règle qui se déclencherait trop tôt, par exemple après la suppression d'un for, passerait inaperçue.

Le binaire promtool est présent dans l'image officielle de Prometheus : il suffit de remplacer son point d'entrée. Le script ci-dessous reprend l'image épinglée par digest de la leçon Prometheus et monte le dossier courant en lecture seule. Si vous avez déjà promtool sur votre poste, lancez directement les deux commandes sans l'enveloppe Docker.

Fenêtre de terminal
IMG=prom/prometheus:v3.13.0@sha256:c6b27ea434f8389bfe233fbc7be381cf50587c286e871bc842008f5a1b1908a7
pt() { docker run --rm --entrypoint promtool -v "$PWD:/work:ro" -w /work "$IMG" "$@"; }
pt check rules rules.yml
pt test rules tests.yml

Ce que vous devez voir : la première commande valide la syntaxe et compte les règles chargées, la seconde rejoue les deux scénarios de tests.yml. Dans les deux cas le code de retour est 0, ce qui permet d'en faire une étape de CI qui bloque un déploiement.

Checking rules.yml
SUCCESS: 3 rules found

La sortie de pt test rules tests.yml tient en une seule ligne, sans autre détail tant que tout passe.

SUCCESS

Un test qui ne sait jamais échouer ne prouve rien. Modifiez donc volontairement tests.yml et lisez la sortie. Premier cas : attendez severity: warning au lieu de critical pour TargetDown à 5m. La commande sort en code 1 et affiche côte à côte ce que vous attendiez (exp) et ce que la règle a produit (got).

FAILED:
alertname: TargetDown, time: 5m,
exp:[
0:
Labels:{alertname="TargetDown", instance="demo-app:8080", job="demo-app", severity="warning"}
Annotations:{runbook_url="https://runbooks.infra.internal/target-down", summary="Target demo-app/demo-app:8080 is down"}
],
got:[
0:
Labels:{alertname="TargetDown", instance="demo-app:8080", job="demo-app", severity="critical"}
Annotations:{runbook_url="https://runbooks.infra.internal/target-down", summary="Target demo-app/demo-app:8080 is down"}
]

La différence tient en un mot : severity="warning" d'un côté, severity="critical" de l'autre. Le bloc got indique la vérité de la règle, et le bloc exp votre erreur de test.

Second cas : attendez que TargetDown soit firing à 3m, c'est-à-dire avant la fin du for: 2m. Cette fois, got:[] montre que la règle n'a produit aucune alerte à cet instant. C'est exactement ce que le for doit garantir, et le test échoue parce que votre attente était fausse, pas la règle.

FAILED:
alertname: TargetDown, time: 3m,
exp:[
0:
Labels:{alertname="TargetDown", instance="demo-app:8080", job="demo-app", severity="critical"}
Annotations:{runbook_url="https://runbooks.infra.internal/target-down", summary="Target demo-app/demo-app:8080 is down"}
],
got:[]

Remettez ensuite les valeurs d'origine pour retrouver un SUCCESS.

Quels pièges rencontre-t-on en écrivant ces tests ?

Section intitulée « Quels pièges rencontre-t-on en écrivant ces tests ? »

Trois pièges ont été mesurés en rejouant ces tests. Le premier : toutes les étiquettes de l'alerte doivent figurer dans exp_labels, y compris celles qui viennent de la série et pas de la règle ; seul alertname est ajouté automatiquement. Le deuxième : job et instance apparaissent dans l'alerte uniquement parce que vous les avez écrites dans input_series, car aucun scrape n'a lieu pendant un test.

Le troisième est le saut de ligne final. Une annotation écrite en bloc | dans YAML se termine par un \n : la description de HighErrorRate produit donc "...\nCurrent error rate: 5.00%\n", et ce dernier \n est nécessaire dans exp_annotations. Notez aussi que HighErrorRate agrège avec sum by (service_name) : l'alerte ne garde que cette étiquette de la série, d'où service_name: api et aucun http_status_code dans exp_labels.

La méthode la plus fiable pour un lab Helm est d'inclure les règles directement dans les values. Les deux fichiers rules.yml et tests.yml créés plus haut ne sont pas lus par le chart : ils servent de référence complète, et vous reportez dans les values les trois règles ci-dessous, suffisantes pour suivre la suite de l'exercice.

  1. Ajoutez les règles dans les values Helm

    Éditez 02-prometheus/helm-values/prometheus-minimal.yaml et ajoutez la section serverFiles :

    serverFiles:
    alerting_rules.yml:
    groups:
    - name: infrastructure
    rules:
    - alert: TargetDown
    expr: up == 0
    for: 2m
    labels:
    severity: critical
    annotations:
    summary: "Target {{ $labels.job }}/{{ $labels.instance }} is down"
    - alert: HighMemoryUsage
    expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
    for: 5m
    labels:
    severity: warning
    annotations:
    summary: "Memory usage above 85% on {{ $labels.instance }}"
    - name: otel-demo-application
    rules:
    - alert: OtelCollectorDown
    expr: up{job="otel-collector"} == 0
    for: 2m
    labels:
    severity: critical
    annotations:
    summary: "OpenTelemetry Collector is down"
  2. Appliquez avec Helm upgrade

    Fenêtre de terminal
    helm upgrade prometheus prometheus-community/prometheus \
    -n observability \
    -f 02-prometheus/helm-values/prometheus-minimal.yaml \
    -f 04-alerting/helm-values/alertmanager.yaml \
    --wait

    Helm déclenche un rollout du pod Prometheus qui charge automatiquement les nouvelles règles.

  3. Vérifiez que les règles sont chargées

    Ouvrez l'interface Prometheus (http://localhost:9090) et allez dans Status → Rules.

    Ce que vous devez voir : Vos groupes infrastructure et otel-demo-application avec leurs règles.

Avant de brancher la moindre notification, vérifiez que Prometheus charge et évalue vos règles. Deux pages de son interface suffisent, et elles répondent à deux questions distinctes : Status → Rules dit si la règle existe et si elle s'évalue sans erreur, Alerts dit si elle se déclenche. Une règle absente de la première page ne se déclenchera jamais, quel que soit l'état du cluster.

Cette page montre toutes les règles chargées :

  • État de chaque règle : inactive, pending, firing
  • Dernière évaluation : quand la règle a été vérifiée
  • Durée d'évaluation : combien de temps le calcul a pris

Cliquez sur Alerts dans le menu.

Cette page montre les alertes non inactives (pending ou firing) :

Ce que vous pourriez voir :

AlerteÉtatSignification
HighMemoryUsagePending (2m30s)La mémoire est haute depuis 2m30s, mais le seuil for: 5m n'est pas atteint
TargetDownFiringUn target est down depuis plus de 2 minutes

Alertmanager reçoit les alertes de Prometheus. Maintenant, configurons-le pour envoyer des notifications utiles.

Le routing détermine quel receiver reçoit quelle alerte.

route:
receiver: 'default' # Receiver par défaut
group_by: ['alertname'] # Grouper les alertes de même nom
group_wait: 30s # Attendre 30s avant d'envoyer (grouper les alertes)
group_interval: 5m # Intervalle minimum entre groupes
repeat_interval: 4h # Répéter si toujours firing après 4h
routes: # Sous-routes (ordre important !)
- matchers:
- severity="critical"
receiver: 'critical-alerts' # Les critiques vont ici
continue: false # Ne pas continuer aux autres routes
- matchers:
- team="backend"
receiver: 'backend-team' # Les alertes de l'équipe backend vont ici

Une note de syntaxe qui coûte cher en migration : les clés match et match_re, que l'on croise encore dans beaucoup d'exemples en ligne, sont dépréciées dans la configuration d'Alertmanager. La forme actuelle est matchers, une liste d'expressions au format label="valeur" qui accepte aussi !=, =~ et !~. Une seule clé remplace donc les deux anciennes.

Dans cet exemple, HighLatency porte severity: critical : elle est donc routée vers critical-alerts, et non vers backend-team, bien qu'elle porte aussi team: backend. La première correspondance gagne et continue: false arrête la recherche : l'ordre des routes est déterminant. Pour que l'équipe backend reçoive aussi ses alertes critiques, ajoutez continue: true à la route critical-alerts.

Comment le routing fonctionne :

  1. Une alerte arrive avec ses labels
  2. Alertmanager parcourt les routes dans l'ordre
  3. Première correspondance (matchers) trouvée → utilise ce receiver
  4. Si continue: true, continue à chercher d'autres correspondances
  5. Si aucune correspondance, utilise le receiver par défaut

Voici comment envoyer les alertes vers Slack :

  1. Créez un webhook Slack

    • Allez sur https://api.slack.com/apps
    • Créez une nouvelle app ou utilisez une existante
    • Activez "Incoming Webhooks"
    • Créez un webhook pour votre channel (ex: #alerts)
    • Copiez l'URL webhook
  2. Mettez à jour la configuration Alertmanager

    Éditez 04-alerting/helm-values/alertmanager.yaml :

    alertmanager:
    enabled: true
    config:
    global:
    resolve_timeout: 5m
    # Si vous utilisez Slack via webhook
    slack_api_url: 'https://hooks.slack.com/services/XXX/YYY/ZZZ'
    route:
    receiver: 'slack-default'
    group_by: ['alertname', 'severity']
    group_wait: 30s
    group_interval: 5m
    repeat_interval: 4h
    routes:
    # Les alertes critiques vont dans un channel dédié
    - matchers:
    - severity="critical"
    receiver: 'slack-critical'
    receivers:
    - name: 'slack-default'
    slack_configs:
    - channel: '#alerts'
    send_resolved: true
    title: '{{ if eq .Status "firing" }}[FIRING]{{ else }}[RESOLVED]{{ end }} {{ .CommonAnnotations.summary }}'
    text: |
    {{ range .Alerts }}
    *Alert:* {{ .Annotations.summary }}
    *Description:* {{ .Annotations.description }}
    *Severity:* {{ .Labels.severity }}
    {{ end }}
    - name: 'slack-critical'
    slack_configs:
    - channel: '#alerts-critical'
    send_resolved: true
    title: '[CRITICAL] {{ .CommonAnnotations.summary }}'
    text: |
    {{ range .Alerts }}
    *Alert:* {{ .Annotations.summary }}
    *Description:* {{ .Annotations.description }}
    {{ end }}
  3. Appliquez

    Fenêtre de terminal
    helm upgrade prometheus prometheus-community/prometheus \
    -n observability \
    -f 02-prometheus/helm-values/prometheus-minimal.yaml \
    -f 04-alerting/helm-values/alertmanager.yaml \
    --wait

Parfait pour tester. Utilisez webhook.site pour voir les requêtes en temps réel :

receivers:
- name: 'webhook-test'
webhook_configs:
- url: 'https://webhook.site/VOTRE-UUID-UNIQUE'
send_resolved: true
  1. Allez sur https://webhook.site
  2. Copiez l'URL unique générée
  3. Les alertes apparaîtront sur cette page en temps réel

Pendant une maintenance planifiée, vous ne voulez pas recevoir des centaines d'alertes pour des choses que vous savez déjà en cours.

Les silences suppriment temporairement les notifications.

Un silence se définit par des matchers de labels et une fenêtre temporelle, jamais par un identifiant d'alerte. C'est ce qui le rend puissant et dangereux à la fois : un matcher trop large étouffe des alertes que vous n'aviez pas prévu de couvrir. Les champs Creator et Comment ne sont pas décoratifs, ce sont eux qui permettent à l'astreinte suivante de savoir si un silence encore actif est légitime ou oublié.

  1. Ouvrez Alertmanager (http://localhost:9093)

  2. Cliquez sur Silences → New Silence

  3. Configurez le silence :

    ChampValeurExplication
    StartMaintenantQuand le silence commence
    EndDans 2 heuresQuand le silence se termine
    Matchersalertname=HighMemoryUsageQuelles alertes silencer
    CreatorVotre nomQui a créé le silence
    Comment"Maintenance RAM node-1"Pourquoi
  4. Cliquez sur Create

Ce qui se passe : Les alertes HighMemoryUsage ne génèrent plus de notifications, mais sont toujours visibles dans Prometheus et Alertmanager (avec la mention "Silenced").

Utile pour les scripts de maintenance automatisés :

Fenêtre de terminal
# Créer un silence d'une heure pour toutes les alertes du service checkout
curl -X POST http://localhost:9093/api/v2/silences \
-H "Content-Type: application/json" \
-d '{
"matchers": [
{"name": "service_name", "value": "checkout", "isRegex": false}
],
"startsAt": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
"endsAt": "'$(date -u -d '+1 hour' +%Y-%m-%dT%H:%M:%SZ)'",
"createdBy": "maintenance-script",
"comment": "Déploiement checkout v2.3.1"
}'

La colonne de gauche tient en une idée : un silence est une exception tracée et bornée, pas un réglage permanent. Les silences oubliés sont la première cause de panne non détectée dans une équipe qui a pourtant une bonne couverture d'alertes. Prenez l'habitude de relire la liste des silences actifs au début de chaque astreinte.

À faireÀ éviter
Documenter la raison du silenceSilencer sans commentaire
Mettre une durée limitéeSilencer indéfiniment
Silencer au plus précisSilencer tout severity=warning
Vérifier après la maintenanceOublier de supprimer le silence

Les inhibitions, supprimer le bruit automatiquement

Section intitulée « Les inhibitions, supprimer le bruit automatiquement »

Scénario : Un node entier tombe en panne. Vous recevez :

  • 1 alerte "NodeDown"
  • 10 alertes "PodDown" (pour chaque pod sur ce node)
  • 15 alertes "ServiceUnavailable"

C'est du bruit. Si le node est down, évidemment les pods aussi.

Les inhibitions suppriment automatiquement les alertes redondantes.

inhibit_rules:
# Si un node est down, ne pas alerter sur ses pods
- source_matchers:
- alertname="NodeDown"
target_matchers:
- alertname="PodDown"
equal: ['node'] # L'inhibition s'applique si le label 'node' correspond
# Si un service est down, ne pas alerter sur la latence de ce service
- source_matchers:
- alertname="ServiceDown"
target_matchers:
- alertname="HighLatency"
equal: ['service_name']

Comment ça marche :

  1. source_matchers : l'alerte "parente" qui déclenche l'inhibition
  2. target_matchers : les alertes qui seront supprimées
  3. equal : les labels qui doivent correspondre entre source et target

Comme pour le routing, source_match et target_match sont dépréciés au profit de source_matchers et target_matchers. Le champ equal est le plus subtil des trois : sans lui, une seule alerte NodeDown étoufferait les alertes PodDown de tout le cluster, y compris celles des machines en bonne santé.

Testons tout le pipeline de bout en bout.

  1. Créez une alerte de test qui se déclenche immédiatement

    Ajoutez cette règle temporaire :

    - alert: TestAlert
    expr: vector(1) == 1 # Toujours vrai
    for: 1m
    labels:
    severity: warning
    team: platform
    annotations:
    summary: "This is a test alert"
    description: "Testing the alerting pipeline. Please ignore."

    Ajoutez-la au groupe infrastructure de la clé serverFiles des values, puis appliquez avec le même helm upgrade que pour les autres règles.

  2. Vérifiez dans Prometheus → Alerts

    • T+0 : L'alerte apparaît en Pending
    • T+1min : L'alerte passe en Firing

    Ce que vous devez voir : L'alerte TestAlert en rouge (firing).

  3. Vérifiez dans Alertmanager

    Ouvrez http://localhost:9093

    Ce que vous devez voir : L'alerte TestAlert dans la liste.

  4. Vérifiez la notification

    • Si webhook.site : Rafraîchissez la page, l'alerte doit apparaître
    • Si Slack : Vérifiez votre channel
    • Si autre : Vérifiez la destination configurée
  5. Créez un silence

    • Dans Alertmanager → Silences → New Silence
    • Matcher : alertname=TestAlert
    • Durée : 10 minutes
    • Commentaire : "Testing silences"
  6. Vérifiez que le silence fonctionne

    L'alerte doit maintenant afficher "Silenced" dans Alertmanager.

  7. Supprimez la règle de test

    Retirez la règle TestAlert des values et appliquez avec helm upgrade.

Les quatre règles ci-dessous visent un objectif unique : maintenir la confiance dans les alertes. Une alerte que l'équipe ignore par réflexe est pire qu'une alerte absente, parce qu'elle donne l'illusion d'une couverture. Elles se lisent dans l'ordre, chacune réduisant le volume ou augmentant l'utilité de ce qui reste.

Mauvais pour réveiller (cause)Bon pour réveiller (symptôme)
"CPU > 80%""Latence P99 > 500ms"
"Mémoire > 85%""Taux d'erreur > 1%"
"Connexions DB > 100""Temps de réponse DB > 200ms"

Pourquoi ? Les utilisateurs ne se soucient pas de votre CPU. Ils se soucient que le site soit lent.

Ces causes ne sont pas à jeter pour autant : une alerte « CPU > 80 % » ou « Mémoire > 85 % » reste utile en sévérité warning, comme celles écrites plus haut. Elle sert d'alerte de capacité, lue en journée pour anticiper une saturation. Elle devient mauvaise seulement quand elle page quelqu'un la nuit : un processeur à 80 % peut être un fonctionnement normal.

Comparez les deux blocs sur un seul critère : la personne réveillée à trois heures du matin sait-elle quoi taper après avoir lu la notification ? Le champ description porte les commandes de diagnostic, le champ runbook_url porte le lien vers la procédure complète. Le second exemple utilise aussi {{ $labels.instance }} pour nommer la machine concernée, ce qui évite d'avoir à ouvrir Prometheus pour savoir où regarder.

# Mauvais : pas actionnable - que dois-je faire ?
annotations:
summary: "CPU is high"
# Bon : actionnable - je sais quoi vérifier
annotations:
summary: "CPU above 85% on {{ $labels.instance }}"
description: |
Check for:
- Runaway processes: top -c
- Recent deployments: kubectl rollout history
- Memory pressure leading to swapping
runbook_url: "https://runbooks.infra.internal/high-cpu"
  • 5-10 alertes critiques maximum
  • Trop d'alertes = fatigue d'alerte = alertes ignorées

La règle : "Si je reçois cette alerte à 3h du matin, dois-je me lever ?"

Une chaîne d'alerting se dégrade silencieusement : un webhook révoqué, un canal Slack archivé, une astreinte qui a changé de main. Rien de tout cela ne produit d'erreur visible tant qu'aucune alerte réelle ne survient. Le test périodique est le seul moyen de détecter ces ruptures avant l'incident.

  • Rejouez les tests de règles de la section promtool test rules à chaque modification d'une règle
  • Déclenchez manuellement au moins une fois par mois
  • Vérifiez que les bonnes personnes reçoivent
  • Vérifiez que le runbook est à jour

Le diagnostic suit toujours le sens du flux : Prometheus évalue, Alertmanager route, le receiver notifie. Déterminez d'abord à quelle étape la chaîne casse, en regardant si l'alerte apparaît dans Prometheus, puis dans Alertmanager. Les trois tableaux ci-dessous correspondent aux trois ruptures possibles.

Commencez par la première ligne : neuf fois sur dix, l'expression PromQL ne renvoie aucune série. Copiez le contenu du champ expr dans l'onglet Graph de Prometheus, sans le reste de la règle. Si la requête ne remonte rien, aucun réglage de for ou de labels ne fera apparaître l'alerte.

CauseDiagnosticSolution
Expression incorrecteTestez expr dans Prometheus GraphCorrigez la syntaxe
Délai for trop longL'alerte est en PendingRéduisez ou attendez
Règle non chargéeStatus → RulesVérifiez les values Helm
Labels/métriques inexistantsTestez dans ExploreDécouvrez les vrais labels avec count by (...)

Ici, l'alerte existe bien côté Prometheus mais s'arrête en chemin. Le partage se fait sur un seul test : l'alerte est-elle visible dans l'interface Alertmanager ? Si non, le problème est en amont, dans la section alerting de la configuration Prometheus. Si oui, il est dans le routing ou dans le receiver, et les journaux d'Alertmanager le disent explicitement.

CauseDiagnosticSolution
Alertmanager ne reçoit pasUI Alertmanager videVérifiez la config Prometheus alerting
Routing incorrectConfig AlertmanagerTestez avec receiver default
Receiver mal configuréLogs Alertmanagerkubectl logs -n observability -l app.kubernetes.io/name=alertmanager
URL webhook invalidewebhook.site ne reçoit rienVérifiez que l'URL est accessible depuis le cluster

Une tempête d'alertes n'est presque jamais un problème de seuils seul : c'est un défaut de regroupement. Une panne unique qui produit quarante notifications signale un group_by mal choisi ou des inhibitions absentes. Traitez ces deux points avant de toucher aux seuils, sous peine de masquer de vrais incidents.

CauseSolution
Seuils trop basAjustez les seuils
Pas de groupageAjoutez group_by
Pas d'inhibitionsConfigurez les inhibitions
Métriques/labels mal ciblésVérifiez avec count by (...)
  1. Alertmanager est déployé et accessible

    Fenêtre de terminal
    kubectl get pods -n observability | grep alertmanager

    Le pod est en Running.

  2. Les règles sont testées puis chargées

    promtool test rules tests.yml affiche SUCCESS, puis Prometheus → Status → Rules montre vos groupes.

  3. Le receiver fonctionne

    Déclenchez une alerte test et vérifiez la notification.

  4. Vous savez créer un silence

    Créez et supprimez un silence depuis l'UI.

Ces points constituent le socle de l'alerting. Si l'un d'eux reste flou, reprenez la section correspondante : les modules suivants réutilisent ce vocabulaire sans le redéfinir.

  1. Prometheus évalue les règles d'alerte, Alertmanager route et notifie
  2. Une alerte passe par les états : Inactive → Pending → Firing → Resolved
  3. Le routing dirige les alertes vers les bons receivers
  4. Les silences gèrent les maintenances planifiées
  5. Les inhibitions réduisent le bruit automatiquement
  6. Une bonne alerte est actionnable avec un runbook
  7. On réveille sur les symptômes vécus par l'utilisateur (latence, erreurs) ; les causes (CPU, mémoire) restent des alertes de capacité en warning, qui préviennent sans réveiller
  8. promtool check rules valide la syntaxe, promtool test rules valide le comportement d'une règle sur des séries fabriquées, y compris l'absence d'alerte avant la fin du for

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

8 questions
8 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn