From 1bbff5e74974615538c8e3c781529a322b8b64cf Mon Sep 17 00:00:00 2001 From: sorb Date: Fri, 14 Aug 2026 12:00:00 +0000 Subject: [PATCH] feat(alerts): alert on backup failure, stalled backups and stale restore drill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There was no rule covering backups at all: a failed nightly job would have gone unnoticed, which is precisely the silent failure management #0030 is about. BackupJobFailed catches a failed run, BackupNotRunning catches a CronJob that stopped scheduling, and RestoreDrillStale fires when the monthly drill stops — an unverified backup is an assumption again, so the absence of the check is itself worth alerting on. Co-Authored-By: Claude Opus 4.8 --- monitoring/prometheus/alerts.yml | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/monitoring/prometheus/alerts.yml b/monitoring/prometheus/alerts.yml index 13dbc79..3c7d937 100644 --- a/monitoring/prometheus/alerts.yml +++ b/monitoring/prometheus/alerts.yml @@ -85,3 +85,38 @@ groups: room: security annotations: summary: "CVE-Scan fuer {{ $labels.target }} ist aelter als 2 Tage — Scanner pruefen (ein still gestorbener Scanner macht blind)" + + # Sicherungen (management #0030). Bis 2026-08-14 gab es hierzu KEINE Regel: ein + # fehlgeschlagenes naechtliches Backup waere unbemerkt geblieben - genau der stille + # Ausfall, den das Issue beschreibt. Metriken kommen aus kube-state-metrics im + # Matrix-Cluster (Alloy -> remote_write, kube_job_*/kube_cronjob_* passieren den + # Filter, der nur go_.*|process_.* verwirft). + - name: axion-backup + rules: + # Ein Backup- oder Probe-Job ist fehlgeschlagen. + - alert: BackupJobFailed + expr: max by (namespace, job_name) (kube_job_status_failed{job_name=~"(synapse|authentik|wikijs)-backup.*|restore-drill.*"}) > 0 + for: 15m + labels: + severity: critical + annotations: + summary: "Backup-Job {{ $labels.namespace }}/{{ $labels.job_name }} ist fehlgeschlagen — ohne Sicherung ist der naechste Ausfall ein Datenverlust" + + # Der CronJob plant gar nicht mehr (ausgesetzt, geloescht, Cluster-Problem). + # 26h Toleranz fuer die taeglichen Laeufe um 03:00/03:15/03:30. + - alert: BackupNotRunning + expr: time() - kube_cronjob_status_last_schedule_time{cronjob=~"(synapse|authentik|wikijs)-backup"} > 93600 + labels: + severity: critical + annotations: + summary: "Seit ueber 26h kein Lauf von {{ $labels.cronjob }} — CronJob ausgesetzt oder verschwunden?" + + # 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. + - alert: RestoreDrillStale + expr: time() - kube_cronjob_status_last_schedule_time{cronjob="restore-drill"} > 3456000 + labels: + severity: warning + annotations: + summary: "Restore-Probe seit ueber 40 Tagen nicht gelaufen — Wiederherstellbarkeit ist wieder unbewiesen (Verfahren: notfallhandbuch)"