Automatisierte Datenbank-Dumps nach S3
Ziel
Tägliche, versionierte Dumps aus einem Kubernetes-Cluster nach S3-kompatiblem Speicher. Erzeugung per CronJob mit pg_dump und dem MinIO-Client (mc).
Als Beispiel dient eine PostgreSQL-Datenbank. Unveränderbarkeit und Zugriffsschutz werden mitberücksichtigt.
Retention-Schema
Es wird ein Rolling-Prinzip mit exakt N+1 Dateien verwendet. Bei N=7 ergeben sich acht Dateien je Datenbank.
latest.sql.gzals aktuelles Backup1.sql.gzals Backup des Vortags2.sql.gzals Backup von vorgestern- bis
7.sql.gzals ältestes Backup
Die Datei 7.sql.gz wird beim nächsten Lauf gelöscht, sobald die maximale Retention erreicht ist.
Ablauf der Rotation
Die Rotation läuft nur, wenn latest.sql.gz bereits existiert. Folgende Tabelle zeigt das Verhalten über mehrere Läufe.
| Lauf | Aktionen |
|---|---|
| 1 | Neues Backup als latest.sql.gz |
| 2 | latest -> 1, neues Backup als latest.sql.gz |
| 3 | 1 -> 2, latest -> 1, neues Backup als latest.sql.gz |
| 4 | 2 -> 3, 1 -> 2, latest -> 1, neues Backup als latest.sql.gz |
Ab 7.sql.gz wird das jeweils älteste Backup vor der Rotation entfernt.
Algorithmus
Der Ablauf je Lauf folgt einer festen Reihenfolge.
- Dump lokal erzeugen, komprimiert als
.sql.gzin einem temporären Pfad - Existenz von
latest.sql.gzim Bucket prüfen - Fall A:
latest.sql.gzfehlt, neuer Dump wird direkt alslatest.sql.gzhochgeladen - Fall B:
latest.sql.gzexistiert, Rotation wird durchgeführt
Die Rotation in Fall B verläuft so:
- Falls
N.sql.gzvorhanden, dieses löschen - Alle vorhandenen Backups um eine Version hochrücken, von
N-1 -> Nbis1 -> 2 latest.sql.gz -> 1.sql.gz- Neuen Dump als
latest.sql.gzhochladen
Eigenschaften des Verfahrens:
- Stabile, nachvollziehbare Dateistruktur
- Kein versehentliches Überschreiben aktiver Backups
- Maximal
N+1Dateien je Datenbank, planbare Speicherlast - Rotation nur bei bereits vorhandenem
latest.sql.gz
PoC-Skript
Das folgende Skript erzeugt einen PostgreSQL-Dump und führt die Rotation im Bucket aus. Konfiguration über Umgebungsvariablen.
#!/bin/bash
set -euo pipefail
# Konfiguration
TMPFILE="/tmp/backup.sql.gz"
TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S)
DB_NAME="${DB_NAME:?DB_NAME erforderlich}"
BUCKET="${BUCKET_NAME:?BUCKET_NAME erforderlich}"
S3_PATH="$BUCKET/backups/$DB_NAME"
RETENTION="${RETENTION_DAYS:-7}"
MC_ALIAS="${S3_ALIAS:-s3}"
# MinIO-Client konfigurieren
mc alias set "$MC_ALIAS" "$S3_ENDPOINT" "$S3_ACCESS_KEY" "$S3_SECRET_KEY" >/dev/null
# Dump erzeugen
pg_dump -h "$PGHOST" -U "$PGUSER" "$DB_NAME" | gzip > "$TMPFILE"
# Prüfen, ob latest existiert
if mc stat "$MC_ALIAS/$S3_PATH/latest.sql.gz" >/dev/null 2>&1; then
# Ältestes Backup entfernen, falls Retention erreicht
if mc stat "$MC_ALIAS/$S3_PATH/$RETENTION.sql.gz" >/dev/null 2>&1; then
mc rm "$MC_ALIAS/$S3_PATH/$RETENTION.sql.gz" --quiet
fi
# Bestehende Backups um eine Version hochrücken
for ((i=RETENTION-1; i>=1; i--)); do
NEXT=$((i+1))
if mc stat "$MC_ALIAS/$S3_PATH/$i.sql.gz" >/dev/null 2>&1; then
mc mv "$MC_ALIAS/$S3_PATH/$i.sql.gz" "$MC_ALIAS/$S3_PATH/$NEXT.sql.gz" --quiet
fi
done
# latest auf 1 verschieben
mc mv "$MC_ALIAS/$S3_PATH/latest.sql.gz" "$MC_ALIAS/$S3_PATH/1.sql.gz" --quiet
fi
# Neuen Dump als latest hochladen
mc cp "$TMPFILE" "$MC_ALIAS/$S3_PATH/latest.sql.gz" --quiet
Sensible Werte wie S3_ACCESS_KEY, S3_SECRET_KEY und PGPASSWORD stammen aus einem Secret. pg_dump liest das Passwort aus der Umgebungsvariable PGPASSWORD. Für andere Datenbanken wird pg_dump durch mysqldump oder mongodump ersetzt, der restliche Ablauf bleibt gleich.
Unveränderbarkeit und Zugriffsschutz
Backups dürfen nur durch den autorisierten CronJob verändert oder gelöscht werden. Dafür wird der Speicher gegen unbeabsichtigte und unbefugte Schreibzugriffe abgesichert.
Object Lock in Ceph RGW und MinIO
Ceph RGW und MinIO unterstützen S3 Object Lock einschließlich der Modi GOVERNANCE und COMPLIANCE. Objekte lassen sich damit für einen Zeitraum oder dauerhaft gegen Löschung und Überschreibung schützen.
| Eigenschaft | AWS S3 | Ceph RGW / MinIO |
|---|---|---|
| S3 Object Lock (GOVERNANCE) | Ja | Eingeschränkt |
| S3 Versioning | Ja | Ja |
| Write-Once (WORM) | Ja | Eingeschränkt |
| Bucket Policies (readonly) | Ja | Ja |
Object Lock in Ceph RGW und MinIO bietet nicht die vollen Garantien von AWS S3. Ein Admin-Account kann Objekte weiterhin löschen. Für Schutz vor versehentlichem Löschen reicht das aus, für strikte Compliance nicht ohne Zusatzmaßnahmen.
Aktivierung beim Bucket-Erstellen
Object Lock muss beim Erstellen des Buckets aktiviert werden. Eine nachträgliche Aktivierung schlägt fehl.
| HTTP-Status | Status Code | Beschreibung |
|---|---|---|
400 |
MalformedXML | Das XML ist nicht wohlgeformt |
409 |
InvalidBucketState | Object Lock am Bucket ist nicht aktiviert |
Quelle: RadosGW BucketOps.
Konflikt zwischen Object Lock und Rotation
Die Rotation benennt Objekte um. Ein mc mv ist ein Kopieren mit anschließendem Löschen des Quellobjekts. Auf einem Bucket mit Object Lock ist das Löschen gesperrt, die Rotation schlägt damit fehl.
Object Lock und das Rolling-Schema schließen sich auf demselben Pfad gegenseitig aus.
Hybrider Ansatz
Die Lösung trennt veränderliche und unveränderliche Objekte in zwei Pfade.
Veränderliche Rotation
Unter backups/<db>/ liegen die rotierenden Dateien.
latest.sql.gz,1.sql.gzbis7.sql.gz- ohne Object Lock
- Rotation durch das Skript
Unveränderliches Archiv
Unter backups/<db>/archive/ liegen zusätzliche Kopien mit Zeitstempel.
mc cp "$TMPFILE" \
"$MC_ALIAS/$S3_PATH/archive/$(date +%F).sql.gz" --quiet
- Dateien wie
2025-05-20.sql.gz,2025-05-21.sql.gz - mit Object Lock im GOVERNANCE-Modus
- Retention passend zum Rolling-Schema, etwa sieben Tage
Die Archivkopien werden nur geschrieben, nie umbenannt. Damit ist Object Lock kompatibel.
Bucket Policy
Nur ein Service-Account darf in den Bucket schreiben. Alle anderen Prinzipale werden für Schreib- und Löschoperationen verweigert.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam:::user/backup-cronjob"
},
"Action": ["s3:*"],
"Resource": [
"arn:aws:s3:::<bucket>",
"arn:aws:s3:::<bucket>/*"
]
},
{
"Effect": "Deny",
"NotPrincipal": {
"AWS": "arn:aws:iam:::user/backup-cronjob"
},
"Action": ["s3:PutObject", "s3:DeleteObject"],
"Resource": ["arn:aws:s3:::<bucket>/*"]
}
]
}
Der CronJob nutzt einen exklusiven Access Key. Die Rotation läuft damit über einen zentralen Kontrollpunkt.
Logging je Tag
Optional protokolliert das Skript jede Aktion in eine Logdatei je Tag.
LOGFILE="/var/log/db-backup/backup_$(date +%F).log"
mkdir -p "$(dirname "$LOGFILE")"
log() {
echo "[$(date +'%F %T')] $*" | tee -a "$LOGFILE"
}
Im Skript werden die echo-Aufrufe durch log ersetzt. Die Logdatei dokumentiert Rotation und Upload je Lauf.
Zusammenfassung
- Rolling-Retention mit
N+1Dateien,latest.sql.gzplus1..N.sql.gz - Rotation nur, wenn
latest.sql.gzbereits existiert - Dump per
pg_dump, gzip-komprimiert, Upload mitmc mc mvlöscht das Quellobjekt, daher unvereinbar mit Object Lock- Ceph RGW und MinIO unterstützen Object Lock, aber ohne die vollen Garantien von AWS S3
- Object Lock muss beim Bucket-Erstellen aktiviert sein
- Hybrider Ansatz: veränderliche Rotation plus unveränderliches Archiv mit Zeitstempeln
- Bucket Policy beschränkt Schreibzugriff auf einen Service-Account
- Logdatei je Tag dokumentiert jeden Lauf