Aller au contenu
Outils medium

Alertmanager : Notifications et routage

16 min de lecture

Prometheus détecte un problème, mais qui est prévenu ? Alertmanager reçoit les alertes de Prometheus et les route vers les bons canaux. Ce guide vous accompagne de bout en bout : créer une alerte → la router → recevoir 1 notif (pas 50) → la muter pendant une maintenance.

Avant de configurer, comprenez ce qui se passe :

  1. Prometheus évalue une règle PromQL → détecte un problème → produit une alerte

  2. L’alerte contient des labels (routing) et des annotations (affichage)

  3. Alertmanager reçoit l’alerte et applique 4 mécanismes :

    • Routing : où envoyer (Slack, PagerDuty, email…)
    • Grouping : anti-spam (50 pods down = 1 notification)
    • Inhibition : supprime les alertes redondantes (cluster down → pas d’alerte pods)
    • Silence : mute volontaire pendant une maintenance
  4. Le receiver notifie via Slack, email, PagerDuty, Teams, Webhook…

Architecture Alertmanager : Prometheus envoie les alertes vers Alertmanager qui route vers Slack, PagerDuty et Email

TypeRôleExemplesUtilisé pour
LabelsIdentifient et routentseverity, team, env, serviceRouting, grouping, inhibition
AnnotationsExpliquent et documententsummary, description, runbook_urlContenu de la notification

Suivez ce parcours pour envoyer votre première notification.

Une bonne alerte inclut des labels de routing ET des annotations utiles.

rules/app.yml
groups:
- name: app
rules:
- alert: ApiHighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/ sum(rate(http_requests_total[5m])) by (service) > 0.05
for: 5m
labels:
severity: critical
team: backend
env: production
service: api
annotations:
summary: "Taux d'erreurs 5xx > 5% ({{ $labels.service }})"
description: "Erreur 5xx élevée depuis 5 minutes. Vérifier logs + traces."
runbook_url: "https://wiki.example.com/runbooks/api-high-error-rate"
dashboard_url: "https://grafana.example.com/d/api-errors"
prometheus.yml
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
rule_files:
- "rules/*.yml"
alertmanager.yml
global:
resolve_timeout: 5m
route:
receiver: slack-default
group_by: [alertname, service, env]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receivers:
- name: slack-default
slack_configs:
- api_url: "https://hooks.slack.com/services/XXX/YYY/ZZZ"
channel: "#alerts"
send_resolved: true
title: '[{{ .Status | toUpper }}] {{ .CommonLabels.alertname }}'
text: >-
*Service:* {{ .CommonLabels.service }}
*Env:* {{ .CommonLabels.env }}
*Severity:* {{ .CommonLabels.severity }}
*Résumé:* {{ .CommonAnnotations.summary }}
*Runbook:* {{ .CommonAnnotations.runbook_url }}

Ne déployez jamais sans valider — sinon vous debuggez à l’aveugle.

Fenêtre de terminal
# Côté Prometheus : valider config + rules
promtool check config prometheus.yml
promtool check rules rules/*.yml
# Côté Alertmanager : valider config
amtool check-config alertmanager.yml
  1. Déclencher l’alerte : injectez des erreurs 5xx ou attendez un vrai incident

  2. Vérifier dans Prometheus : http://prometheus:9090/alerts

  3. Vérifier dans Alertmanager : http://alertmanager:9093

  4. Recevoir la notification Slack avec les infos + runbook

Le routage est un arbre : l’alerte entre à la racine, descend, et s’arrête au premier match (sauf continue: true).

alertmanager.yml
route:
receiver: slack-default
group_by: [alertname, service, env]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
# Prod + critical → PagerDuty (astreinte)
- matchers:
- env="production"
- severity="critical"
receiver: pagerduty-prod
# Prod + warning → Slack prod
- matchers:
- env="production"
- severity="warning"
receiver: slack-prod
# Staging → Slack staging
- matchers:
- env="staging"
receiver: slack-staging
# Team backend → copie en plus au canal backend
- matchers:
- team="backend"
receiver: slack-backend
continue: true # continue le matching
receivers:
- name: slack-default
slack_configs:
- api_url: "https://hooks.slack.com/services/..."
channel: "#alerts"
- name: pagerduty-prod
pagerduty_configs:
- routing_key: "$PAGERDUTY_ROUTING_KEY"
send_resolved: true
- name: slack-prod
slack_configs:
- api_url: "https://hooks.slack.com/services/..."
channel: "#alerts-prod"
- name: slack-staging
slack_configs:
- api_url: "https://hooks.slack.com/services/..."
channel: "#alerts-staging"
- name: slack-backend
slack_configs:
- api_url: "https://hooks.slack.com/services/..."
channel: "#backend-alerts"
Fenêtre de terminal
# Simuler une alerte et voir où elle irait
amtool config routes test --config.file=alertmanager.yml \
severity=critical env=production team=backend

Le grouping répond à : combien de notifications pour 50 pods qui tombent ?

ParamètreRôleValeur typique
group_byLabels qui définissent “un incident”[alertname, service, env]
group_waitAttente initiale pour regrouper30s
group_intervalCadence pour updates d’un groupe5m
repeat_intervalRappel si toujours actif4h

Règle pratique :

  • Si vous groupez par instance, vous allez spammer (1 notif par pod)
  • Si vous groupez par service + env, vous recevez “1 incident par service”
route:
group_by: [alertname, service, env] # 1 incident = 1 service en 1 env
group_wait: 30s # Attendre 30s pour grouper
group_interval: 5m # Màj toutes les 5m
repeat_interval: 4h # Rappel toutes les 4h si toujours actif
QuandExemple
Maintenance planifiée”Upgrade MySQL, mute les alertes DB 2h”
Incident connu”On sait que l’API est lente, on travaille dessus”
Fenêtre de terminal
# Via amtool
amtool silence add alertname=HighCPU instance=web-01 \
--comment="Maintenance planifiée JIRA-1234" \
--duration=2h
Fenêtre de terminal
# Via API
curl -X POST http://localhost:9093/api/v2/silences -d '{
"matchers": [
{"name": "alertname", "value": "HighCPU", "isRegex": false},
{"name": "instance", "value": "web-01", "isRegex": false}
],
"startsAt": "2026-02-09T10:00:00Z",
"endsAt": "2026-02-09T12:00:00Z",
"createdBy": "admin",
"comment": "Maintenance planifiée JIRA-1234"
}'
QuandExemple
Incident global”ClusterDown → inutile d’alerter sur chaque pod”
Hiérarchie de sévérité”Critical firing → supprimer les warnings”
alertmanager.yml
inhibit_rules:
# Si ClusterDown est firing, supprimer les alertes Pod*
- source_matchers:
- alertname="ClusterDown"
- env="production"
target_matchers:
- alertname=~"Pod.*"
- env="production"
equal: [cluster] # Même cluster seulement
# Si critical actif, supprimer les warning du même service
- source_matchers:
- severity="critical"
target_matchers:
- severity="warning"
equal: [alertname, service]
LabelValeursRôle
severitycritical, warning, infoPriorité (routing + astreinte)
teambackend, frontend, sre, dataOwnership (routing)
envproduction, staging, devEnvironnement (routing)
serviceapi, auth, payments, dbDécoupage produit (grouping)
cluster / regioneu-west-1, us-east-1Opérations multi-cluster

Une alerte sans action = bruit. Incluez toujours :

annotations:
summary: "Résumé en 1 ligne ({{ $labels.service }})"
description: "Contexte : quoi vérifier en premier"
runbook_url: "https://wiki.example.com/runbooks/alert-name"
dashboard_url: "https://grafana.example.com/d/service-dashboard"
logs_url: "https://loki.example.com/explore?query={service=\"{{ $labels.service }}\"}"

Template Slack avec liens :

text: >-
*Service:* {{ .CommonLabels.service }}
*Résumé:* {{ .CommonAnnotations.summary }}
*Runbook:* {{ .CommonAnnotations.runbook_url }}
*Dashboard:* {{ .CommonAnnotations.dashboard_url }}
  1. Télécharger Alertmanager v0.31.0

    Fenêtre de terminal
    cd /tmp
    VERSION="0.31.0"
    wget https://github.com/prometheus/alertmanager/releases/download/v${VERSION}/alertmanager-${VERSION}.linux-amd64.tar.gz
    wget https://github.com/prometheus/alertmanager/releases/download/v${VERSION}/sha256sums.txt
    grep "alertmanager-${VERSION}.linux-amd64.tar.gz" sha256sums.txt | sha256sum -c -
  2. Installer

    Fenêtre de terminal
    tar xvfz alertmanager-${VERSION}.linux-amd64.tar.gz
    sudo cp alertmanager-${VERSION}.linux-amd64/{alertmanager,amtool} /usr/local/bin/
    sudo mkdir -p /etc/alertmanager /var/lib/alertmanager
    sudo useradd --no-create-home --shell /usr/sbin/nologin alertmanager
    sudo chown -R alertmanager:alertmanager /etc/alertmanager /var/lib/alertmanager
  3. Créer le service systemd (hardened)

    /etc/systemd/system/alertmanager.service
    [Unit]
    Description=Alertmanager
    Wants=network-online.target
    After=network-online.target
    [Service]
    User=alertmanager
    Group=alertmanager
    Type=simple
    ExecStart=/usr/local/bin/alertmanager \
    --config.file=/etc/alertmanager/alertmanager.yml \
    --storage.path=/var/lib/alertmanager \
    --web.listen-address=127.0.0.1:9093
    ExecReload=/bin/kill -HUP $MAINPID
    Restart=always
    # Hardening
    NoNewPrivileges=true
    PrivateTmp=true
    ProtectSystem=strict
    ProtectHome=true
    ReadWritePaths=/var/lib/alertmanager
    [Install]
    WantedBy=multi-user.target
  4. Démarrer

    Fenêtre de terminal
    sudo systemctl daemon-reload
    sudo systemctl enable alertmanager
    sudo systemctl start alertmanager

Alertmanager écoute sur 127.0.0.1:9093 (sécurisé par défaut).

Alertmanager supporte nativement :

ReceiverUsage typique
SlackNotifications équipe
EmailBackup / management
PagerDutyAstreinte, escalade
OpsgenieAstreinte, on-call
Microsoft TeamsEntreprises Microsoft
WebhookIntégrations custom (CMDB, ticketing)

Voir la liste complète des intégrations.

Stratégie recommandée :

  1. Slack + Email : commencer simple
  2. + PagerDuty/Opsgenie : pour l’astreinte
  3. + Webhook : pour intégrer votre ticketing (Jira, ServiceNow)
VariableDescription
.Status”firing” ou “resolved”
.AlertsListe des alertes
.CommonLabelsLabels communs à toutes les alertes du groupe
.CommonAnnotationsAnnotations communes
.ExternalURLURL d’Alertmanager

Pour chaque alerte dans .Alerts :

VariableDescription
.LabelsTous les labels de l’alerte
.AnnotationsToutes les annotations
.StartsAtTimestamp de début
.EndsAtTimestamp de fin (si resolved)
alertmanager.yml
templates:
- '/etc/alertmanager/templates/*.tmpl'
/etc/alertmanager/templates/slack.tmpl
{{ define "slack.title" }}
[{{ .Status | toUpper }}] {{ .CommonLabels.alertname }}
{{ end }}
{{ define "slack.text" }}
{{ range .Alerts }}
*Instance:* {{ .Labels.instance }}
*Severity:* {{ .Labels.severity }}
*Summary:* {{ .Annotations.summary }}
*Runbook:* {{ .Annotations.runbook_url }}
---
{{ end }}
{{ end }}

Alertmanager supporte le clustering :

Fenêtre de terminal
# Instance 1
alertmanager --cluster.listen-address=0.0.0.0:9094 \
--cluster.peer=alertmanager-2:9094
# Instance 2
alertmanager --cluster.listen-address=0.0.0.0:9094 \
--cluster.peer=alertmanager-1:9094

Prometheus doit pointer vers toutes les instances :

alerting:
alertmanagers:
- static_configs:
- targets:
- 'alertmanager-1:9093'
- 'alertmanager-2:9093'

Avant de mettre en prod, vérifiez :

  • Routes : prod/staging/dev ont des receivers distincts
  • Grouping : group_by évite le spam (pas par instance)
  • Inhibition : incident global supprime les alertes enfants
  • Silences documentés : qui peut créer, avec quel commentaire
  • Templates : incluent runbook_url et dashboard_url
  • Tests : promtool check rules + amtool check-config avant reload
  • HA : clustering si critique
  • Sécurité : bind sur localhost ou reverse proxy authentifiant
SymptômeCause probableSolution
Pas de notificationReceiver mal configuréamtool config routes test
Doublon de notificationsPas de clusteringConfigurer la HA
Trop d’alertesgroup_by trop finGrouper par service, pas instance
Silence ne marche pasMauvais matchersVérifier labels exacts
Alerte ne route pasMauvais matchers routeamtool config routes test

Commandes utiles :

Fenêtre de terminal
# Tester la config
amtool check-config alertmanager.yml
# Simuler le routing
amtool config routes test --config.file=alertmanager.yml severity=critical env=production
# Lister les silences actifs
amtool silence query
# Logs
journalctl -u alertmanager -f
  • Labels = routing (qui doit agir), Annotations = contenu (quoi faire)
  • Routing = arbre de décision, utilisez matchers (pas match)
  • Grouping = anti-spam, groupez par service pas par instance
  • Silence = mute volontaire (maintenance)
  • Inhibition = suppression automatique (cluster down)
  • Validez toujours : amtool check-config avant de déployer

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.