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 Bei Stack-Aenderungen `cve/images.txt` nachziehen, sonst scannt die Pipeline
an der Realitaet vorbei. 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.** **Stand 2026-08-15: Alarme werden zugestellt.** `alertmanager.yml` hat nur noch die
In `alertmanager.yml` routet `room="security"` derzeit auf den Null-Receiver. 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 Historie, damit der Grund der damaligen Stummschaltung nicht verlorengeht: die
**pro CVE pro Image**. Beim ersten Scan-Durchlauf waren das bei 14 von 29 CVE-Regeln erzeugten je eine Alarm-Instanz **pro CVE pro Image** (beim ersten Lauf
Images bereits 59 CRITICAL und 445 HIGH. Da `group_by: [alertname, instance]` 59 CRITICAL + 445 HIGH), `matrix-alerts.py` schickte eine Nachricht pro Alarm,
alle in *eine* Gruppe legt und `matrix-alerts.py` **eine Matrix-Nachricht pro Synapse rate-limitete nach ~10 Nachrichten mit 429, und weil `save_state()` hinter
Alarm** schickt, wird daraus ein Nachrichten-Schwall. Verschaerfend: der Sende-Schleife stand, wiederholte Alertmanager die komplette Gruppe mit noch
`save_state()` steht in `do_POST` hinter der Sende-Schleife -- bricht ein Send leerer Deduplizierung. Behoben durch Aggregation auf `count by (target, severity)`
ab (Synapse rate-limitet nach ~10 Nachrichten mit 429), wird der State nicht (eine Instanz je Image statt je CVE); Details im Dashboard `cve-overview`.
gespeichert, der Receiver antwortet 502 und Alertmanager wiederholt die Vollstaendige Historie: gitops#51 / AAR 2026-08-01.
komplette Gruppe. Die Fingerprint-Deduplizierung ist beim Retry noch leer,
also wiederholt sich das.
Zum Scharfschalten sind **beide** Punkte noetig: **Zustellfehler sind seit 2026-08-15 selbst ueberwacht:** Prometheus scrapt
Alertmanager (`operating_alertmanager`) und `AlertDeliveryFailing` schlaegt an,
1. Zustellung buendeln -- entweder `matrix-alerts.py` auf eine Sammelnachricht wenn `alertmanager_notifications_failed_total` steigt. Vorher war Alertmanager zwar
pro Webhook-Batch umbauen (alle fuenf Pflichtfelder je CVE als eine Zeile), Alarm-Ziel, aber kein Scrape-Target — eine reissende Alarmkette haette sich also
oder die Regeln auf `count by (target, severity)` aggregieren und die selbst nicht melden koennen.
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.
### Kleinere offene Punkte der Pipeline ### Kleinere offene Punkte der Pipeline
+12
View File
@@ -147,3 +147,15 @@ groups:
severity: critical severity: critical
annotations: annotations:
summary: "Ein Backup-/Probe-CronJob fehlt im Cluster — Sicherung oder Wiederherstellungs-Nachweis laeuft nicht mehr" 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: static_configs:
- targets: ["cadvisor:8080"] - 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" - job_name: "operating_traefik"
metrics_path: "/metrics" metrics_path: "/metrics"
static_configs: static_configs: