Inhalt
Auf dem Raspberry Pi als Homeserver liegt alles auf einer SSD: die Anwendungen, ihre Daten, die Datenbank. Eine
Sicherung auf derselben Platte wäre also suboptimal. Für die Dateien der Anwendungen reicht lediglich ein rsync des
Verzeichnisses, für die Postgres Datenbanken nicht.
Warum ein Dump
Mehrere Anwendungen teilen sich eine Postgres-Instanz, jede mit eigener Datenbank. pg_dumpall
schreibt alle davon inklusive der Rollen in eine einzige SQL-Datei. Kommt also eine neue Datenbank dazu, ist sie
automatisch mit drin.
Die drei Stufen liegen alle in der service.yaml von Postgres:
| Stufe | Dienst | Funktion | Aufbewahrung |
|---|---|---|---|
| 1 | postgres-backup | 02:30 pg_dumpall nach /mnt/ssd/backups/postgres | 7 Tage, 4 Wochen, 6 Monate |
| 2 | postgres-offsite-backup | 03:30 restic-Snapshot nach Azure, danach restic check | 14 Tage, 8 Wochen, 12 Monate |
| 3 | postgres-offsite-backup | Push an Uptime Kuma, up oder down | - |
Stufe 1: Der Dump
Das Image prodrigestivill/postgres-backup-local1 ist genau dafür gemacht.
postgres-backup:
image: prodrigestivill/postgres-backup-local:16
user: "999:999"
environment:
SCHEDULE: "30 2 * * *"
BACKUP_ON_START: "TRUE"
POSTGRES_HOST: postgres
POSTGRES_CLUSTER: "TRUE"
POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
BACKUP_KEEP_DAYS: 7
BACKUP_KEEP_WEEKS: 4
BACKUP_KEEP_MONTHS: 6Im Ergebnis liegen unter daily/, weekly/ und monthly/ die rotierten Dumps und unter last/
immer der Neueste. Diesen Ordner sichert Stufe 2.
Stufe 2: Verschlüsselt nach Azure
Wohin, war schnell entschieden. Eine Azure-Subscription gibt es ohnehin, dieser Blog läuft dort, und beruflich ist Azure natürlich auch mein Alltag. Für ein paar komprimierte SQL-Dateien kostet lokal redundanter Speicher praktisch nichts. Der Storage Account ist eigens dafür angelegt und macht sonst nichts.
restic2 verschlüsselt das Backup auf dem Server, bevor irgendetwas den Homeserver verlässt. Es lädt nur hoch, was sich geändert hat, und definiert die Aufbewahrungsregeln:
restic backup --host homeserver --tag postgres /backups
restic forget --host homeserver --tag postgres --keep-daily 14 --keep-weekly 8 --keep-monthly 12 --prune
restic checkDas check am Ende prüft, ob das Repository in sich stimmig ist. Ein Backup, das sich nicht lesen
lässt, soll schnellstmöglich auffallen. Und nicht erst, wenn man es braucht.
Stufe 3: Wer merkt, wenn es nicht läuft?
Ein Backup, das ohne es zu merken nicht mehr läuft ist das eigentliche Risiko. Deshalb meldet der
Offsite-Dienst nach jedem Lauf das Ergebnis an einen Push-Monitor in Uptime Kuma. Eine URL mit
status=up oder status=down. Bleibt es aus, weil der Container hängt oder der Server aus ist,
geht er von selbst auf Rot und informiert via Slack. Gemeldet wird also diesmal nicht der Erfolg, sondern das Ausbleiben.
Die erste Stufe hängt mit dran: Bevor restic loslegt, prüft das Skript, ob in last/ ein Dump
jünger als 26 Stunden liegt.
Zurückspielen
Ein Backup, das nie getestet wurde, ist kein Backup. Die Probe lief gegen einen frischen Postgres-Container; gegen die echte Instanz ist es derselbe Befehl:
docker run --rm -i --network traefik-public -e PGPASSWORD="<passwort>" \
prodrigestivill/postgres-backup-local:16 \
sh -c 'zcat | psql -h postgres -U <benutzer> -d postgres' \
< /mnt/ssd/backups/postgres/last/postgres-latest.sql.gzSoll nur eine Datenbank zurückgespielt werden, kommt der Dump in einen separaten Container, und pg_dump zieht sie
dann von dort heraus.
Fazit
Drei kleine Dienste, jeder mit einer Aufgabe: dumpen, wegschaffen, melden. Der Dump ist der unspektakuläre Teil. Was die Kette brauchbar macht, ist der Monitor.
