From e0808bad90adcebea409e5df1a39fee57d30730e Mon Sep 17 00:00:00 2001 From: sorb Date: Fri, 14 Aug 2026 12:00:00 +0000 Subject: [PATCH] fix(alerts): a missing series must not silence the backup alerts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verification on CFGMON showed RestoreDrillStale could never fire: restore-drill has no last_schedule_time series until its first scheduled run (a manually triggered job does not set it), and an expression over a missing series yields nothing. BackupNotRunning shares the flaw — deleting a CronJob removes the very series the alert reads, so it goes quiet instead of firing. Fall back to kube_cronjob_created, but aggregate with max by(namespace, cronjob): 'or' matches including __name__, so a bare fallback would return BOTH series and the never-updating created timestamp would fire permanently once the window elapsed. Add BackupCronJobMissing so a vanished CronJob is itself the alert. Co-Authored-By: Claude Opus 4.8 --- monitoring/prometheus/alerts.yml | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/monitoring/prometheus/alerts.yml b/monitoring/prometheus/alerts.yml index 3c7d937..9d26d2c 100644 --- a/monitoring/prometheus/alerts.yml +++ b/monitoring/prometheus/alerts.yml @@ -104,8 +104,14 @@ groups: # Der CronJob plant gar nicht mehr (ausgesetzt, geloescht, Cluster-Problem). # 26h Toleranz fuer die taeglichen Laeufe um 03:00/03:15/03:30. + # Fallback auf die Anlagezeit: ein frisch (neu) angelegter CronJob hat noch + # keine last_schedule_time-Serie - ein Ausdruck ueber eine fehlende Serie + # liefert nichts, der Alarm koennte also nie feuern. max by(...) fasst beide + # Metriken zu EINER Serie je CronJob zusammen; ohne das wuerde die nie + # aktualisierte created-Serie nach Ablauf der Frist dauerhaft falsch feuern + # ('or' matcht inkl. __name__, ergibt also eine Vereinigung beider Serien). - alert: BackupNotRunning - expr: time() - kube_cronjob_status_last_schedule_time{cronjob=~"(synapse|authentik|wikijs)-backup"} > 93600 + expr: time() - max by (namespace, cronjob) (kube_cronjob_status_last_schedule_time{cronjob=~"(synapse|authentik|wikijs)-backup"} or kube_cronjob_created{cronjob=~"(synapse|authentik|wikijs)-backup"}) > 93600 labels: severity: critical annotations: @@ -114,9 +120,30 @@ groups: # Die monatliche Restore-Probe laeuft nicht mehr. Eine Sicherung, die nie # zurueckgespielt wurde, ist eine Vermutung — deshalb ist auch das Ausbleiben # der PRUEFUNG ein Alarm, nicht nur ihr Fehlschlag. 40 Tage = Monatsrhythmus + Puffer. + # Bis zum ersten REGULAEREN Lauf gibt es keine last_schedule_time-Serie (ein + # manuell ausgeloester Job setzt sie nicht) - ohne Fallback waere dieser + # Alarm bis dahin blind. Gleiche max-by-Konstruktion wie oben. - alert: RestoreDrillStale - expr: time() - kube_cronjob_status_last_schedule_time{cronjob="restore-drill"} > 3456000 + expr: time() - max by (namespace, cronjob) (kube_cronjob_status_last_schedule_time{cronjob="restore-drill"} or kube_cronjob_created{cronjob="restore-drill"}) > 3456000 labels: severity: warning annotations: summary: "Restore-Probe seit ueber 40 Tagen nicht gelaufen — Wiederherstellbarkeit ist wieder unbewiesen (Verfahren: notfallhandbuch)" + + # Ein Backup-CronJob existiert gar nicht mehr (geloescht, Kustomization + # entfernt, Namespace weg). Bei den Alarmen oben verschwindet dann die + # Serie - und eine verschwundene Serie ist in Prometheus kein Alarm, + # sondern Stille. Genau das war der Fehler, den dieses Regelwerk + # verhindern soll, also ist das Fehlen selbst der Alarm. + # for: 30m puffert kurze Luecken der Metrik-Pipeline ab. + - alert: BackupCronJobMissing + expr: >- + absent(kube_cronjob_info{namespace="matrix", cronjob="synapse-backup"}) + or absent(kube_cronjob_info{namespace="matrix", cronjob="wikijs-backup"}) + or absent(kube_cronjob_info{namespace="authentik", cronjob="authentik-backup"}) + or absent(kube_cronjob_info{namespace="matrix", cronjob="restore-drill"}) + for: 30m + labels: + severity: critical + annotations: + summary: "Ein Backup-/Probe-CronJob fehlt im Cluster — Sicherung oder Wiederherstellungs-Nachweis laeuft nicht mehr"