fix(alerts): a missing series must not silence the backup alerts
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user