Upgrade des PostgreSQL-Dogus
Bei der Weiterentwicklung des PostgreSQL-Dogus muss sichergestellt werden, dass bestehende Instanzen auf neue Versionen aktualisiert werden können.
Der Upgrade-Pfad besteht aus dem Skript pre-upgrade.sh vor dem Image-Wechsel und post-upgrade.sh nach dem Image-Wechsel.
Allgemeiner Ablauf
Beim Dogu-Upgrade wird zuerst im alten Container pre-upgrade.sh ausgeführt.
Danach wird das neue Image gestartet und post-upgrade.sh ausgeführt.
Gleichzeitig startet startup.sh den offiziellen PostgreSQL-Entrypoint.
Pre-Upgrade (resources/pre-upgrade.sh)
pre-upgrade.sh entscheidet, ob ein Full-Backup erforderlich ist.
Ein Backup wird unter anderem bei Major-Upgrades oder bei Layout-Migrationen erzeugt.
Wichtige Punkte:
- Full-Backup mit
pg_dumpallwird unter/var/lib/postgresql/backupabgelegt. - Der Backup-Pfad wird in der Config unter
migration_backup_pathgespeichert. local_state=upgradingwird gesetzt, damitstartup.shauf ein laufendes Upgrade warten kann.
Post-Upgrade (resources/post-upgrade.sh)
post-upgrade.sh unterscheidet zwischen Restore-Fall und regulären Migrationen.
Restore-Fall:
- Wenn
migration_backup_pathgesetzt ist, wird das Zielverzeichnis für einen Restore vorbereitet. post-upgrade.shinitialisiert mit den Funktionen des offiziellen Entrypoints eine neue Datenbank und startet sie temporär.restore.shspielt das Backup ein, danach laufen Passwort-Rotation und Migrationen wie im regulären Fall.- Erst ganz am Ende wird
local_stateentfernt, bis dahin wartetstartup.sh.
Der Restore muss vollständig in post-upgrade.sh laufen und nicht beim regulären Start:
Der Dogu-Operator startet den Pod neu, sobald post-upgrade.sh beendet ist.
Ein Restore im Start würde dabei abgebrochen und eine halb eingespielte Datenbank hinterlassen.
Bricht ein Lauf ab, bleibt migration_backup_path gesetzt und der nächste Aufruf beginnt mit leerem PGDATA von vorne.
Ein flock verhindert, dass ein erneuter Aufruf des Operators einen noch laufenden Restore stört.
Regulärer Migrationsfall:
- Wenn kein Restore nötig ist und eine DB bereits initialisiert ist, startet
post-upgrade.shPostgreSQL temporär. - Das Superuser-Passwort wird einmalig rotiert (siehe unten).
- Danach werden Migrationsskripte aus
/docker-entrypoint-initdb.d(ausresources/migrations) manuell ausgeführt. - PostgreSQL wird wieder gestoppt und
local_stateentfernt.
Rotation des Superuser-Passworts (rotateSuperuserPassword)
Vor doguctl v0.12.2 nutzte doguctl random Gos math/rand statt crypto/rand. Betroffene Werte sind nicht erkennbar, deshalb rotiert jede Instanz ohne Marker.
- Marker
password_rotatedin der Dogu-Config, kein Versionsvergleich. - Reihenfolge: Config, dann
ALTER USER, dann Marker — eine abgebrochene Rotation wird wiederholt. - Läuft nach
startPostgresql, weilALTER USEReine laufende DB braucht. Über den Unix-Socket genügttrust, das alte Passwort wird nicht gebraucht. - Läuft auch im Restore-Fall, nachdem das Backup eingespielt wurde.
Startup (resources/startup.sh)
startup.sh wartet, solange local_state=upgrading gesetzt ist.
Bei leerem PGDATA (postgresql nicht installiert) setzt initAdmin zusätzlich password_rotated=true, da ein frisch erzeugtes Passwort sicher ist.
Danach startet es /usr/local/bin/docker-entrypoint.sh mit den Dogu-spezifischen Parametern.
Wichtig:
Der offizielle Entrypoint führt Skripte in /docker-entrypoint-initdb.d automatisch nur bei einer frischen Initialisierung aus.
Deshalb gibt es im bestehenden Datenbestand den manuellen Aufruf der Skripte in post-upgrade.sh.
Wo kommen neue Migrationsskripte hin?
Neue Migrationsskripte kommen nach resources/migrations/ und werden im Dockerfile nach /docker-entrypoint-initdb.d/ kopiert.
Aktuell:
resources/migrations/02-restrictStatVisibility.shresources/migrations/03-migrateConstraintsOnPartitionedTables.sh
Der Restore (resources/restore.sh) ist bewusst kein Migrationsskript, er wird nur von post-upgrade.sh aufgerufen.
Reihenfolge und Konventionen
- Skripte als
NN-beschreibung.shbenennen (01-...,02-..., ...). - Skripte idempotent bauen (mehrfaches Ausführen darf nicht schaden).
- Abschlussmarker in der Dogu-Config setzen (z. B.
restricted_stat_visibility=true), damit Schritte nur einmal laufen. - Immer mit
set -o errexit -o nounset -o pipefailarbeiten.