Tp slides
Introduction¶
Qu'est-ce qu'Alertmanager ?¶
Alertmanager est le composant de gestion des alertes de l'écosystème Prometheus
Il prend en charge :
- La réception des alertes envoyées par Prometheus (ou d'autres sources)
- Le regroupement (grouping) d'alertes similaires
- Le routage vers les bons destinataires
- La déduplication pour éviter le spam
- La suppression via inhibitions et silences
Alertmanager ne génère pas d'alertes — il les gère.
Ce qu'Alertmanager n'est PAS¶
- ❌ Un moteur de règles (c'est Prometheus)
- ❌ Un outil de visualisation (c'est Grafana)
- ❌ Un système de stockage de métriques
- ❌ Un remplacement à un on-call management (OpsGenie, PagerDuty ont plus de fonctionnalités)
Concepts Fondamentaux¶
Le cycle de vie d'une alerte¶
Prometheus évalue une règle
│
▼
État : PENDING ◄── for: 5m (délai avant firing)
│
▼
État : FIRING ──► envoi à Alertmanager
│
▼
État : RESOLVED ──► envoi de résolution à Alertmanager
Les alertes sont envoyées toutes les evaluation_interval tant qu'elles sont actives
Les Labels¶
Les alertes sont identifiées par des labels (paires clé/valeur)
# Exemple de règle Prometheus
groups:
- name: infra
rules:
- alert: HighCPU
expr: cpu_usage_percent > 90
for: 5m
labels:
severity: critical
team: platform
annotations:
summary: "CPU élevé sur {{ $labels.instance }}"
description: "CPU à {{ $value }}% depuis 5 min"
Les labels servent au routing. Les annotations servent aux messages
Grouping (Regroupement)¶
Regroupe des alertes similaires en une seule notification
Alertes reçues :
- InstanceDown{instance="web-01", env="prod"}
- InstanceDown{instance="web-02", env="prod"}
- InstanceDown{instance="db-01", env="prod"}
Avec group_by: [alertname, env]
│
▼
Une seule notification : "3 instances down en prod"
Réduit drastiquement le bruit lors d'incidents majeurs
Les trois délais de groupe¶
| Paramètre | Rôle | Défaut |
|---|---|---|
group_wait | Attendre d'autres alertes avant le 1er envoi | 30s |
group_interval | Délai entre deux notifications d'un même groupe | 5m |
repeat_interval | Délai avant de re-notifier si rien n'a changé | 4h |
t=0s → Alerte A reçue → attendre group_wait...
t=30s → Notification envoyée (A)
t=35s → Alerte B reçue (même groupe)
t=5m35s → Notification envoyée (A + B) ← group_interval
t=4h35m → Re-notification si toujours actif ← repeat_interval
Inhibition¶
Supprime des alertes moins importantes quand une alerte critique est active
Si NodeDown{env="prod"} est FIRING
→ Inhiber toutes les alertes avec {env="prod"}
sauf NodeDown elle-même
Exemple concret : Un nœud est down → inutile de notifier que ses services sont aussi down
Silences¶
Mute temporairement des alertes manuellement (maintenance, incidents connus)
- Définis via l'UI web ou l'API
- Ciblés par matchers (labels)
- Associés à un auteur et un commentaire
- Durée limitée avec expiration automatique
Routing des Alertes¶
L'arbre de routage¶
Le routing est une arborescence de nœuds :
route (root)
├── route (match: severity=critical)
│ ├── route (match: team=platform)
│ └── route (match: team=backend)
└── route (match: severity=warning)
└── route (match: env=staging)
Chaque alerte descend l'arbre et est envoyée au premier nœud correspondant (sauf si continue: true)
Nœud de route — Tous les paramètres¶
route:
receiver: 'default' # Receiver si aucun enfant ne correspond
group_by: ['alertname'] # Labels de groupement
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- matchers: # Nouvelle syntaxe (v0.22+)
- name: severity
value: critical
isRegex: false
receiver: 'pagerduty'
continue: false # Stoppe la descente après match
Matchers — Syntaxes¶
Syntaxe moderne (recommandée)¶
Syntaxe PromQL-like (dans match et match_re)¶
Exemple complet de routage¶
route:
receiver: 'slack-general'
group_by: ['alertname', 'env']
group_wait: 30s
group_interval: 5m
repeat_interval: 12h
routes:
- matchers:
- name: severity
value: critical
receiver: 'pagerduty-critical'
group_wait: 10s
repeat_interval: 1h
routes:
- matchers:
- name: team
value: database
receiver: 'pagerduty-dba'
- matchers:
- name: env
value: staging
receiver: 'slack-staging'
continue: true # Notifie staging ET continue
- matchers:
- name: severity
value: warning
receiver: 'slack-warnings'
continue: true — Cas d'usage¶
Par défaut, la descente s'arrête au premier match
continue: true permet de matcher plusieurs routes :
Alerte: {severity=critical, team=platform, env=prod}
route:
routes:
- match: {env: prod}
receiver: audit-log ◄── continue: true
continue: true
- match: {severity: critical}
receiver: pagerduty ◄── match aussi !
Utile pour logger toutes les alertes prod tout en routant vers les bons receivers
Receivers & Intégrations¶
Vue d'ensemble des receivers¶
| Receiver | Usage |
|---|---|
email_configs | Notifications par email |
slack_configs | Messages Slack |
pagerduty_configs | Incidents PagerDuty |
opsgenie_configs | Incidents OpsGenie |
webhook_configs | HTTP générique |
victorops_configs | VictorOps / Splunk On-Call |
wechat_configs | WeChat Enterprise |
msteams_configs | Microsoft Teams (v0.26+) |
sns_configs | Amazon SNS |
telegram_configs | Telegram |
Email¶
receivers:
- name: 'email-ops'
email_configs:
- to: 'ops-team@example.com'
from: 'alertmanager@example.com'
smarthost: 'smtp.example.com:587'
auth_username: 'alertmanager'
auth_password: 'secret'
require_tls: true
# HTML ou texte
html: '{{ template "email.html" . }}'
headers:
Subject: '[{{ .Status | toUpper }}] {{ .GroupLabels.alertname }}'
# Envoyer aussi les résolutions
send_resolved: true
Slack¶
receivers:
- name: 'slack-critical'
slack_configs:
- api_url: 'https://hooks.slack.com/services/T.../B.../XXX'
channel: '#alerts-critical'
username: 'Alertmanager'
icon_emoji: ':fire:'
send_resolved: true
title: '{{ template "slack.title" . }}'
text: |
{{ range .Alerts }}
*Alert:* {{ .Annotations.summary }}
*Severity:* `{{ .Labels.severity }}`
*Instance:* {{ .Labels.instance }}
{{ end }}
# Couleur selon le statut
color: '{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}'
PagerDuty¶
receivers:
- name: 'pagerduty-prod'
pagerduty_configs:
- routing_key: '<INTEGRATION_KEY>'
severity: '{{ .CommonLabels.severity }}'
description: >
{{ .CommonAnnotations.summary }}
# Détails supplémentaires
details:
firing: '{{ .Alerts.Firing | len }}'
resolved: '{{ .Alerts.Resolved | len }}'
env: '{{ .CommonLabels.env }}'
send_resolved: true
Webhook (générique)¶
receivers:
- name: 'custom-webhook'
webhook_configs:
- url: 'https://my-service.example.com/alerts'
send_resolved: true
http_config:
bearer_token: 'my-secret-token'
max_alerts: 0 # 0 = illimité
Microsoft Teams (v0.26+)¶
receivers:
- name: 'teams-ops'
msteams_configs:
- webhook_url: 'https://outlook.office.com/webhook/...'
title: '{{ template "teams.title" . }}'
text: '{{ template "teams.text" . }}'
send_resolved: true
Templates de messages¶
Alertmanager utilise le moteur de templates Go text/template
# templates/slack.tmpl
{{ define "slack.title" }}
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}]
{{ .GroupLabels.SortedPairs.Values | join " " }}
{{ if gt (len .CommonLabels) (len .GroupLabels) }}
({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }})
{{ end }}
{{ end }}
{{ define "slack.text" }}
{{ range .Alerts }}
• *{{ .Annotations.summary }}*
{{ .Annotations.description }}
{{ end }}
{{ end }}
Fonctions de templates utiles¶
| Fonction | Description |
|---|---|
toUpper / toLower | Changement de casse |
title | Première lettre en maj |
join " " | Jointure de liste |
len | Longueur |
humanizeDuration | Durée lisible |
humanize | Nombre lisible |
.Alerts.Firing | Alertes actives |
.Alerts.Resolved | Alertes résolues |
.CommonLabels | Labels communs |
.GroupLabels | Labels de groupe |
Inhibition & Silences¶
Inhibition — Principe¶
inhibit_rules:
- source_matchers: # L'alerte qui inhibe
- name: alertname
value: NodeDown
- name: env
value: prod
target_matchers: # Les alertes inhibées
- name: env
value: prod
# Labels qui doivent correspondre entre source et target
equal:
- 'instance'
- 'env'
equal: la règle ne s'applique que si ces labels ont la même valeur dans source et target
Exemples d'inhibition¶
Cluster down → ignorer les pods¶
inhibit_rules:
- source_matchers:
- name: alertname
value: KubernetesNodeNotReady
target_matchers:
- name: alertname
value: KubernetesPodNotReady
equal: ['node']
Critique inhibe warning¶
inhibit_rules:
- source_matchers:
- name: severity
value: critical
target_matchers:
- name: severity
value: warning
equal: ['alertname', 'service', 'env']
Silences¶
Via l'UI Web (port 9093)¶
- Ouvrir
http://alertmanager:9093 - Cliquer "New Silence"
- Définir les matchers
- Choisir la durée
- Ajouter auteur + commentaire
Silences via API¶
# Créer un silence
curl -X POST http://alertmanager:9093/api/v2/silences \
-H "Content-Type: application/json" \
-d '{
"matchers": [
{"name": "env", "value": "staging", "isRegex": false},
{"name": "severity", "value": "warning", "isRegex": false}
],
"startsAt": "2024-01-15T22:00:00Z",
"endsAt": "2024-01-16T06:00:00Z",
"createdBy": "john.doe",
"comment": "Maintenance nuit"
}'
# Lister les silences
curl http://alertmanager:9093/api/v2/silences
# Supprimer un silence
curl -X DELETE http://alertmanager:9093/api/v2/silences/{silenceID}
Silences via amtool¶
# Installer amtool
go install github.com/prometheus/alertmanager/cmd/amtool@latest
# Configurer
cat ~/.config/amtool/config.yml
# alertmanager.url: http://alertmanager:9093
# Créer un silence
amtool silence add \
alertname=HighCPU \
env=staging \
--duration=2h \
--author="john.doe" \
--comment="Tests de charge"
# Lister
amtool silence query
# Expirer un silence
amtool silence expire <id>
Bonnes Pratiques & Troubleshooting¶
L'API d'Alertmanager¶
| Endpoint | Méthode | Description |
|---|---|---|
/api/v2/alerts | GET | Lister les alertes actives |
/api/v2/alerts | POST | Pousser des alertes manuellement |
/api/v2/alertgroups | GET | Lister les groupes |
/api/v2/silences | GET/POST | Gérer les silences |
/api/v2/silences/{id} | GET/PUT/DELETE | Opérations sur un silence |
/api/v2/status | GET | État du cluster |
/-/healthy | GET | Health check |
/-/ready | GET | Readiness check |
/-/reload | POST | Rechargement config |
Valider la configuration¶
# Vérifier la syntaxe du fichier de config
./amtool check-config alertmanager.yml
# Résultat attendu
Checking 'alertmanager.yml' SUCCESS
Found:
- global config
- route
- 3 inhibit rules
- 5 receivers
- 2 templates
# Rechargement à chaud (sans redémarrage)
curl -X POST http://alertmanager:9093/-/reload
Tester le routage¶
# Simuler une alerte et voir quel receiver sera notifié
amtool config routes test \
--config.file=alertmanager.yml \
severity=critical \
team=platform \
env=prod
# Résultat
Routing tree:
.
└── default-route receiver: slack-general
└── child-route receiver: pagerduty-critical ← MATCH
Envoyer une alerte manuellement (test)¶
# Via amtool
amtool alert add \
alertname=TestAlert \
severity=warning \
env=staging \
--annotation=summary="Test depuis amtool" \
--end=$(date -d '+1h' --iso-8601=seconds)
# Via API
curl -X POST http://alertmanager:9093/api/v2/alerts \
-H "Content-Type: application/json" \
-d '[{
"labels": {
"alertname": "TestAlert",
"severity": "warning",
"env": "staging"
},
"annotations": {
"summary": "Test manuel"
},
"endsAt": "2024-01-15T12:00:00Z"
}]'