feat(monitoring): scrape alertmanager, alert on failed delivery; fix stale README

Alertmanager was configured as an alerting target but never scraped, so its own
metrics were absent: a silently breaking alert chain could not report itself —
the same blind spot as a missing series, now at the end of the chain. Adds the
operating_alertmanager scrape job and AlertDeliveryFailing on
alertmanager_notifications_failed_total.

The README still claimed alert delivery was deliberately muted via a
room=security null receiver. That route is gone; alertmanager.yml routes
everything to the matrix receiver, so the backup alerts added yesterday do get
delivered. Documentation asserting the opposite is dangerous in both directions,
so it now states the current wiring and keeps the alert-storm history as
background.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
sorb
2026-08-14 12:00:00 +00:00
co-authored by Claude Opus 4.8
parent 4cb9bfb2bc
commit e9c13dcf22
3 changed files with 39 additions and 23 deletions
+19 -23
View File
@@ -99,32 +99,28 @@ State (`cve_exporter_state`-Volume) -- daher kommt die Spalte
Bei Stack-Aenderungen `cve/images.txt` nachziehen, sonst scannt die Pipeline
an der Realitaet vorbei.
### Bekanntes Problem: Alarm-Zustellung ist stummgeschaltet
### Alarm-Zustellung (frueher stummgeschaltet — seit gitops#51 wieder scharf)
**Die CVE-Regeln laufen, die Alarme werden aber bewusst nicht zugestellt.**
In `alertmanager.yml` routet `room="security"` derzeit auf den Null-Receiver.
**Stand 2026-08-15: Alarme werden zugestellt.** `alertmanager.yml` hat nur noch die
Default-Route auf den `matrix`-Receiver; die frueher hier beschriebene
`room="security"`-Route auf den Null-Receiver existiert nicht mehr. Alarme **ohne**
`room`-Label (u.a. die `axion-basics`- und `axion-backup`-Gruppen) laufen ueber
diese Default-Route.
Grund: `TrivyCriticalVuln` und `TrivyHighVuln` erzeugen eine Alarm-Instanz
**pro CVE pro Image**. Beim ersten Scan-Durchlauf waren das bei 14 von 29
Images bereits 59 CRITICAL und 445 HIGH. Da `group_by: [alertname, instance]`
alle in *eine* Gruppe legt und `matrix-alerts.py` **eine Matrix-Nachricht pro
Alarm** schickt, wird daraus ein Nachrichten-Schwall. Verschaerfend:
`save_state()` steht in `do_POST` hinter der Sende-Schleife -- bricht ein Send
ab (Synapse rate-limitet nach ~10 Nachrichten mit 429), wird der State nicht
gespeichert, der Receiver antwortet 502 und Alertmanager wiederholt die
komplette Gruppe. Die Fingerprint-Deduplizierung ist beim Retry noch leer,
also wiederholt sich das.
Historie, damit der Grund der damaligen Stummschaltung nicht verlorengeht: die
CVE-Regeln erzeugten je eine Alarm-Instanz **pro CVE pro Image** (beim ersten Lauf
59 CRITICAL + 445 HIGH), `matrix-alerts.py` schickte eine Nachricht pro Alarm,
Synapse rate-limitete nach ~10 Nachrichten mit 429, und weil `save_state()` hinter
der Sende-Schleife stand, wiederholte Alertmanager die komplette Gruppe mit noch
leerer Deduplizierung. Behoben durch Aggregation auf `count by (target, severity)`
(eine Instanz je Image statt je CVE); Details im Dashboard `cve-overview`.
Vollstaendige Historie: gitops#51 / AAR 2026-08-01.
Zum Scharfschalten sind **beide** Punkte noetig:
1. Zustellung buendeln -- entweder `matrix-alerts.py` auf eine Sammelnachricht
pro Webhook-Batch umbauen (alle fuenf Pflichtfelder je CVE als eine Zeile),
oder die Regeln auf `count by (target, severity)` aggregieren und die
CVE-Details im Dashboard lassen.
2. `save_state()` inkrementell nach jedem erfolgreichen Send, plus
429-Behandlung mit `Retry-After`.
Danach die `room="security"`-Route aus `alertmanager.yml` entfernen.
**Zustellfehler sind seit 2026-08-15 selbst ueberwacht:** Prometheus scrapt
Alertmanager (`operating_alertmanager`) und `AlertDeliveryFailing` schlaegt an,
wenn `alertmanager_notifications_failed_total` steigt. Vorher war Alertmanager zwar
Alarm-Ziel, aber kein Scrape-Target — eine reissende Alarmkette haette sich also
selbst nicht melden koennen.
### Kleinere offene Punkte der Pipeline
+12
View File
@@ -147,3 +147,15 @@ groups:
severity: critical
annotations:
summary: "Ein Backup-/Probe-CronJob fehlt im Cluster — Sicherung oder Wiederherstellungs-Nachweis laeuft nicht mehr"
# Die Alarmkette meldet ihr eigenes Reissen. Ironie inklusive: schlaegt die
# Zustellung komplett fehl, kommt auch dieser Alarm nicht an - er ist dann
# aber in Prometheus/Grafana sichtbar, statt dass gar nichts existiert.
# Teilausfaelle (ein Receiver von mehreren, Rate-Limit 429) meldet er sauber.
- alert: AlertDeliveryFailing
expr: increase(alertmanager_notifications_failed_total[15m]) > 0
labels:
severity: critical
annotations:
summary: "Alertmanager konnte Benachrichtigungen nicht zustellen ({{ $labels.integration }}) — Alarme laufen ins Leere"
+8
View File
@@ -26,6 +26,14 @@ scrape_configs:
static_configs:
- targets: ["cadvisor:8080"]
# Alertmanager-Eigenmetriken. Ohne diesen Job sind ZUSTELLfehler unsichtbar:
# Prometheus kennt alertmanager zwar als Alarm-Ziel (oben unter alerting:),
# scrapt ihn aber nicht - eine still reissende Alarmkette waere also genau das,
# was niemand meldet. Liefert u.a. alertmanager_notifications_failed_total.
- job_name: "operating_alertmanager"
static_configs:
- targets: ["alertmanager:9093"]
- job_name: "operating_traefik"
metrics_path: "/metrics"
static_configs: