docs(issues): #0030 — alerts live; record two 'reports success but blind' findings

Alert rules deployed and verified on CFGMON. Verification surfaced two variants
of the same failure class: a missing time series silences an alert instead of
firing it (fixed with a created-time fallback aggregated via max by, plus an
absent() alert for a vanished CronJob), and a SIGHUP reload that reported success
while serving the old file from a stale inode.

The second one matters most: that trap was already documented in detail, with the
right command and a check, and it still bit — so it was removed structurally
(directory mounts) rather than documented harder.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Thore Cimbal
2026-08-14 12:00:00 +00:00
co-authored by Claude Opus 4.8
parent d73a3b7658
commit 0ecacceb2d
@@ -234,3 +234,43 @@ Nur noch **Phase A/B**: Wiederanlauf auf einem leeren Host (K3s, die zwei Bootst
Flux) und die Rückspielung der Synapse-**Medien** sind weiterhin abgeleitet, nicht erprobt — Flux) und die Rückspielung der Synapse-**Medien** sind weiterhin abgeleitet, nicht erprobt —
dafür braucht es eine Wegwerf-Umgebung. Ebenso ungeprüft: die Homelab-Seite dafür braucht es eine Wegwerf-Umgebung. Ebenso ungeprüft: die Homelab-Seite
(GitLab/Overmind → MinIO/DSM). (GitLab/Overmind → MinIO/DSM).
## Nachtrag 2026-08-15 — Alarme ausgerollt, zwei Fehlalarme derselben Klasse gefunden
Der Rollout auf CFGMON ist durch und **verifiziert**: alle vier Regeln der Gruppe
`axion-backup` sind geladen, evaluieren mit `health=ok` und stehen auf `inactive`. Die
Fallback-Ausdrücke liefern echte Serien — die drei Backup-CronJobs mit
`last_schedule_time` von heute Nacht, `restore-drill` greift wie vorgesehen auf
`kube_cronjob_created` zurück, bis der erste geplante Lauf am 4.9. eine Schedule-Zeit setzt.
Beim Verifizieren kamen **zwei Befunde derselben Fehlerklasse** heraus — beide sind
Varianten von „meldet Erfolg, ist aber blind":
**1. Fehlende Serie = Stille statt Alarm.** `RestoreDrillStale` hätte nie feuern können:
ein manuell ausgelöster Job setzt keine `last_schedule_time`, und ein Ausdruck über eine
nicht existierende Serie liefert nichts. Dieselbe Lücke in `BackupNotRunning`: wird ein
CronJob *gelöscht* — exakt der Fall, den der Alarm abdecken soll — verschwindet die Serie,
und der Alarm verstummt. Behoben (`threadnet-operating` `e0808ba`) durch Fallback auf
`kube_cronjob_created`, **zusammengefasst per `max by (namespace, cronjob)`**: `or` matcht
inklusive `__name__`, ein blanker Fallback hätte beide Serien zurückgegeben und die nie
aktualisierte `created`-Zeit hätte nach Fristablauf dauerhaft falsch gefeuert. Ergänzt um
`BackupCronJobMissing` (`absent()`), damit ein verschwundener CronJob **selbst** der Alarm ist.
**2. Reload meldet Erfolg und lädt den alten Stand.** `prometheus_config_last_reload_successful=1`
nach SIGHUP — geladen waren trotzdem die alten Regeln. Ursache: Docker hängt
Einzeldatei-Bindmounts am Inode auf, `git pull` ersetzt Dateien per Rename. Host-Datei 13
Regeln, Container-Datei 12, unterschiedliche md5-Summen. Sichtbar wurde das **nur** durch
den Vergleich Host↔Container; ein Neustart löste es.
⚠️ **Die eigentliche Lehre aus (2):** Diese Falle war in `monitoring/README.md` bereits
ausführlich dokumentiert — mit Mechanik, richtigem Kommando (`--force-recreate`) und sogar
dem Prüfbefehl — und hat trotzdem zugeschlagen. **Dokumentation hat den Fehler nicht
verhindert.** Deshalb strukturell beseitigt statt besser beschrieben
(`threadnet-operating` `4cb9bfb`): Prometheus und Alertmanager mounten jetzt ihr
Config-**Verzeichnis**; Verzeichnis-Mounts lösen bei jedem Zugriff über den Pfad auf.
Config-Pfade unverändert. Die verbliebenen Einzeldatei-Mounts (loki, alloy, die Skripte)
sind in der README benannt.
⚠️ **Deploy offen:** `4cb9bfb` ist gepusht, aber noch nicht auf CFGMON aktiv — die
Mount-Änderung greift erst, wenn die Container neu erstellt werden (`docker compose up -d`
genügt hier, da sich die Service-Definition ändert).