Kubernetes ~4 Min.

Velero: Backup, Restore und DR-Tests

Schedules sind einfach — getestete Restores nicht.

von PlatPrep

Es war kein großer Ausfall. Eine fehlerhafte Migration hatte den Namespace des Monitoring-Systems zerstört — alle PersistentVolumes, alle Konfigurationen weg. “Kein Problem, wir haben Velero-Backups.” Stille. Dann: “Wann haben wir das letzte Mal einen Restore getestet?”

Das ist der Moment, den niemand möchte. Die Backups liefen seit drei Monaten, Velero zeigte grüne Status-Meldungen — aber eine tatsächliche Restore-Übung hatte niemand durchgeführt.

Was beim Test auffiel: Velero hatte die PersistentVolumeClaims gesichert, aber die Volume-Daten selbst fehlten. Eine fehlerhafte CSI-Plugin-Konfiguration hatte die Snapshots stillschweigend übersprungen — der Backup-Status blieb trotzdem grün, weil die Kubernetes-Objekte erfolgreich gesichert waren.

Das ist die Lektion: Backup ohne Restore-Test ist kein Backup. Es ist ein Gefühl von Sicherheit.

Zwei getrennte Probleme: Objekte und Daten

Velero sichert zwei fundamental verschiedene Dinge, und die Unterscheidung erklärt die meisten Stolperfallen. Kubernetes-Objekte (Deployments, Services, ConfigMaps, CRDs) exportiert Velero als JSON in einen Object Store — S3, GCS, Azure Blob. Das ist der einfache Teil.

Volume-Daten sind der schwierige Teil, mit zwei Wegen: CSI Volume Snapshots nutzen die Snapshot-Mechanik des Storage-Providers — schnell, effizient, aber provider-gebunden: ein EBS-Snapshot lässt sich nicht in einem GKE-Cluster restoren. File System Backup (mit Kopia als Uploader) kopiert dagegen Dateien aus dem laufenden Pod in den Object Store — langsamer, dafür provider-unabhängig und damit der Weg für Cluster-Migrationen zwischen Clouds.

Bei beiden gilt: das Backup ist crash-consistent, nicht application-consistent. Eine Postgres-Instanz, deren Volume mitten in einer Transaktion gesnapshottet wurde, startet nach dem Restore im Recovery-Modus — meistens gut, garantiert ist es nicht. Velero bietet dafür Backup-Hooks: ein Pre-Hook kann pg_dump ausführen oder Writes flushen, bevor der Snapshot läuft. Für geschäftskritische Datenbanken ist ohnehin ein eigenes, datenbanknatives Backup (WAL-Archiving, Operator-Backups) die robustere Wahl — Velero ergänzt das, ersetzt es nicht.

Das Minimum-Setup, das den Namen verdient

apiVersion: velero.io/v1
kind: Schedule
metadata:
  name: daily-cluster
  namespace: velero
spec:
  schedule: "0 2 * * *"
  template:
    includedNamespaces: ["*"]
    excludedNamespaces: ["kube-system"]
    ttl: 720h

Täglich um 2 Uhr, Retention 30 Tage. Dazu gehört Monitoring, denn Velero exportiert Prometheus-Metriken: ein Alert auf velero_backup_last_successful_timestamp älter als 25 Stunden fängt auch den Fall ab, in dem der Schedule gar nicht mehr feuert — velero_backup_failure_total allein übersieht das, weil ein nie gestartetes Backup nicht fehlschlägt. Und mindestens einmal sollte jemand geprüft haben, dass im Object Store tatsächlich Volume-Daten liegen, nicht nur Objekt-JSON. Genau das hätte im Eingangs-Szenario den Unterschied gemacht.

Restore-Tests, die wirklich etwas beweisen

Ein Restore-Test, der kein laufendes System anfasst:

velero restore create --from-backup daily-cluster-20260410 \
  --include-namespaces monitoring \
  --namespace-mappings monitoring:restore-test

Vor dem Restore lohnt ein Blick in velero backup describe <name> --details: dort steht, welche Ressourcen tatsächlich im Backup sind und ob Volume-Backups durchgelaufen sind — genau die Prüfung, die das stillschweigend übersprungene Snapshot-Problem aus dem Eingangs-Szenario sichtbar gemacht hätte.

Dann nicht nur schauen, ob Pods starten — sondern ob die Daten stimmen: Grafana-Dashboards vorhanden? Prometheus-Historie da? Eine Stichproben-Query gegen die restorte Datenbank? “Pods sind Running” beweist wenig, wenn die Volumes leer sind.

Beim Restore gibt es eigene Überraschungen, die man besser im Test als im Ernstfall trifft: Velero überschreibt per Default keine existierenden Ressourcen — ein Restore in einen halb kaputten Namespace repariert also weniger, als man erwartet. Ressourcen mit cluster-spezifischen Feldern (NodePort-Konflikte, externe IPs, Webhook-Konfigurationen) brauchen Nacharbeit. Und CRDs müssen vor den Custom Resources da sein, was Velero zwar berücksichtigt, was aber bei Operator-verwalteten Ressourcen trotzdem zu Races führen kann, wenn der Operator schneller reconciled als der Restore läuft.

Für Multi-Cluster-DR: Velero schreibt in einen Bucket, auf den auch der Standby-Cluster Zugriff hat — dort als read-only BackupStorageLocation eingebunden, damit der Standby die Backups sieht, aber nicht verändert. Der Standby macht monatliche Probe-Restores, idealerweise als wiederkehrender, geplanter Termin mit Checkliste und dokumentierter Restore-Dauer. Denn auch das ist ein Ergebnis des Tests: ihr kennt eure tatsächliche RTO — “Restore dauert 40 Minuten” ist eine Information, die man vor dem Disaster haben will, nicht währenddessen.

Eine letzte Grenze gehört in jede DR-Planung: Velero sichert, was im Cluster ist — nicht, was drumherum hängt. Externe DNS-Einträge, beim Cloud-Provider verwaltete Load Balancer, IAM-Bindings oder Secrets, die zur Laufzeit aus Vault kommen, sind nicht Teil des Backups. Ein vollständiger DR-Plan kombiniert deshalb Velero für den Cluster-Inhalt mit Infrastructure-as-Code für alles darunter — der Cluster selbst sollte aus Terraform oder vergleichbarem reproduzierbar sein, Velero füllt ihn danach wieder.

Test die Backups. Wirklich. Nicht erst, wenn du sie brauchst. Die Grundlagen zu Backup-Strategien und Velero-Konzepten findest du im Velero-Lernpfad.