In dieser Dokumentation beschreibe ich, wie ich die Daten, die in einer Applikation, die in einem Podman Container läuft, sichere und nach einem Totalverlust wieder herstelle.
Ich verwende in meinem Beispiel einen Midpoint Container, in dem als Datensatz User erstellt wurden. Ich mache ein Backup, zerstöre alle Container und Volumes und anschließend zaubere ich den Zustand der letzten Sicherung wieder her.
Dies ist Teil eines größeren Projektes, wo ich versuche ein komplettes IAM System aufzubauen:
Ich habe im Vorfeld folgendes dokumentiert:
Ich halte diese Dokumentation allgemein und so kann sie auch für andere Projekte verwendet werden.
Inhalt:
Es gibt nicht nur viele Wege die nach Rom führen, sondern auch viele Wege wie man Backups erstellen kann von einer SQL-Datenbank, die innerhalb eines Podman/Docker Containers läuft.
Das Backup ist auch nicht so sehr das Problem, wie nachher das Restore.
Einige Wege deute ich hier an, einen beschreibe ich ausführlich. Wer also meine Fehlversuche und Erfahrungen überspringen möchte, kann direkt zu Punkt 5 vorrücken:
Folgende Befehle schreiben den Inhalt der Container in Tar-Archive:
podman container export -o /d/IAM/backups/midpoint_container_backup.tar midpoint-midpoint_server-1
podman container export -o /d/IAM/backups/midpoint_container_backup.tar midpoint-midpoint_data-1
Folgendes Beispiel importiert eines der Archive in einen Container:
podman import /d/IAM/backups/midpoint_container_backup.tar midpoint-restored-image
Es werden aber nur die Container gesichert, nicht die Volumes.
Sinnvoller ist es natürlich die Volumes zu sichern, weil ja da die Daten liegen.
Zuerst sollten wir unsere Volumes kennen. Wir sehen sie mit:
podman volume ls
Wenn wir mehr über ein jeweiliges Volume erfahren wollen geben wir ein:
podman volume inspect midpoint_midpoint_home | grep Mountpoint
Als Ergebnis sehen wir z.B.:
"Mountpoint": "/home/user/.local/share/containers/storage/volumes/midpoint_midpoint_home/_data"
Der Befehl zum Sichern lautet generell:
podman volume export --output /path/to/backup/volumename.tar
Und somit in meinem Beispiel:
podman volume export midpoint-midpoint_data-1 --output /d/IAM/backups/midpoint_data_backup.tar
Das Problem bei mir war aber, dass es nicht funktioniert :-(
Ich erhalte eine Fehlermeldung, dass der Befehl falsch geschrieben ist und der angegebene Pfad nicht existiert. Damit geht auch folgendes bei mir nicht:
Podman und Docker sind ja Programme, die auf einem Rechner laufen und selber Daten speichern. Laut dem "podman volume inspect" Befehl, liegt das Volume im Betriebssystem, bei mir unter: D:/Users/User/anaconda3/Library/d/IAM/midpoint.
Ich habe es aber weder da noch woanders auf meinem Windows Rechner gefunden. Hätte ich es dort gesehen, könnte man es kopieren und wegsichern (bei gestoppten Containern).
Eigentlich wollen wir ja nur ein paar SQL Daten sichern. Das geht mit folgendem Befehl:
podman exec -i midpoint-midpoint_data-1 pg_dump \
-U midpoint \
-h localhost \
-d midpoint \
-W > /d/IAM/backups/midpoint_backup_$(date +%Y%m%d).sql
Das Wiedereinspielen geht wie folgt:
PGPASSWORD=db.secret.pw.007 cat /d/IAM/backups/midpoint_backup_20250321.sql | \
podman exec -i midpoint-midpoint_data-1 psql -U midpoint -d midpoint
Bei mir fehlte danach noch das Admin Passwort, das liegt aber nicht an der SQL-DB, sondern am fehlenden keystore.jceks, den ich bei diesem Versuch noch nicht gesichert hatte (mehr dazu siehe unten). D.h. damit sollte es eigentlich funktionieren.
Hier noch ein Tipp, wie man im laufenden Container auf die SQL Datenbank zugreift:
Anmelden am SQL Server:
PGPASSWORD=db.secret.pw.007 podman exec -it midpoint-midpoint_data-1 \
psql -U midpoint -d midpoint
Anzeigen aller Tabellen:
\dt
SQL Abfrage:
SELECT * FROM m_user WHERE nameorig = 'administrator';
Dieser Weg ist meines Achtens der sauberste und vor allem: Er funktioniert bei mir.
Wir bauen hier die Option eines Restores direkt in die Containerstruktur mit ein.
Dazu müssen wir die originale Datei docker-compose.yml um folgende Punkte erweitern:
Sowohl in der midpoint_server als auch in der midpoint_data Service-Section muss ein zusätzliches Volume eingefügt werden:
Bei midpoint_data:
- ./:/backup:Z
Bei midpoint_server:
- ./:/opt/midpoint/var/import:Z
Dieses Volume zeigt auf den Ordner, wo die docker-compose.yml liegt und bindet es als Laufwerk in den jeweiligen Container ein. Dadurch kann innerhalb des Container auf die Festplatte des Betriebssystems, auf dem Podman läuft, zugegriffen werden.
Nun fügen wir einen weiteren Service in die docker-compose.yml ein:
midpoint_restore:
image: evolveum/midpoint:${MP_VER:-latest}-alpine
command:
- /bin/bash
- -c
- |
echo 'Restoring database...'
# Restore PostgreSQL data
cp -r /backup/midpoint_data_backup/* /var/lib/postgresql/data/
chown -R 999:999 /var/lib/postgresql/data/
chmod -R 700 /var/lib/postgresql/data/
echo 'Restoring keystore file...'
# Restore keystore file
if [ -f /backup/keystore.jceks.backup ]; then
cp /backup/keystore.jceks.backup /opt/midpoint/var/keystore.jceks
chmod 600 /opt/midpoint/var/keystore.jceks
echo 'Keystore file restored successfully.'
else
echo 'WARNING: Keystore backup file not found!'
fi
echo 'Restore completed.'
depends_on:
- midpoint_data
volumes:
- midpoint_data:/var/lib/postgresql/data
- midpoint_home:/opt/midpoint/var
- ./:/backup:Z
networks:
- net
profiles:
- restore
Dieser Container startet im Normalfall ("podman compose up") NICHT. Das wird durch den untersten Abschnitt "profiles" geregelt. Auch dieser Container besitzt ein gemountetes Laufwerk "backup", wo er auf die gesicherten Daten des darunter liegenden Betriebssystems zugreifen kann.
Und das tut er auch mit den Copy-Befehlen in der Kommandozeile innerhalb des Containers.
Das Backup kann damit jederzeit wie folgt erzeugt werden:
Die Container müssen oben sein, aber es sollte kein Traffic, bzw. Datenänderung stattfinden.
Folgende Befehle erzeugen dann sowohl ein Backup der Datenbank als auch eines des Keystores (den brauchen wir für das Admin Passwort):
podman cp -a midpoint-midpoint_data-1:/var/lib/postgresql/data ./midpoint_data_backup
podman cp -a midpoint-midpoint_server-1:/opt/midpoint/var/keystore.jceks ./keystore.jceks.backup
Ich habe danach meine Container und meine Volumes komplett gelöscht.
Dann habe ich sie wieder initialisiert. (Details <- Reset des Midpoint Servers).
Dann habe ich in einer Konsole folgende Befehle nacheinander ausgeführt:
podman stop midpoint-midpoint_server-1
podman stop midpoint-midpoint_data-1
podman compose up midpoint_restore
podman compose up
Und Tadaaa, meine Daten innerhalb meines Midpointservers waren wieder da :-D
Hier seht Ihr die:
version: "3.3"
services:
midpoint_data:
image: postgres:16-alpine
environment:
- POSTGRES_PASSWORD=db.secret.pw.007
- POSTGRES_USER=midpoint
- POSTGRES_INITDB_ARGS=--lc-collate=en_US.utf8 --lc-ctype=en_US.utf8
networks:
- net
volumes:
- midpoint_data:/var/lib/postgresql/data
- ./:/backup:Z
data_init:
image: evolveum/midpoint:${MP_VER:-latest}-alpine
command: >
bash -c "
cd /opt/midpoint ;
bin/midpoint.sh init-native ;
echo ' - - - - - - ' ;
bin/ninja.sh -B info >/dev/null 2>/tmp/ninja.log ;
grep -q \"ERROR\" /tmp/ninja.log && (
bin/ninja.sh run-sql --create --mode REPOSITORY ;
bin/ninja.sh run-sql --create --mode AUDIT
) ||
echo -e '\\n Repository init is not needed...' ;
"
depends_on:
- midpoint_data
environment:
- MP_SET_midpoint_repository_jdbcUsername=midpoint
- MP_SET_midpoint_repository_jdbcPassword=db.secret.pw.007
- MP_SET_midpoint_repository_jdbcUrl=jdbc:postgresql://midpoint_data:5432/midpoint
- MP_SET_midpoint_repository_database=postgresql
- MP_INIT_CFG=/opt/midpoint/var
networks:
- net
volumes:
- midpoint_home:/opt/midpoint/var
midpoint_server:
image: evolveum/midpoint:${MP_VER:-latest}-alpine
depends_on:
data_init:
condition: service_completed_successfully
midpoint_data:
condition: service_started
command: [ "/opt/midpoint/bin/midpoint.sh", "container" ]
ports:
- 8082:8080
environment:
- MP_SET_midpoint_repository_jdbcUsername=midpoint
- MP_SET_midpoint_repository_jdbcPassword=db.secret.pw.007
- MP_SET_midpoint_repository_jdbcUrl=jdbc:postgresql://midpoint_data:5432/midpoint
- MP_SET_midpoint_repository_database=postgresql
- MP_SET_midpoint_administrator_initialPassword=Test5ecr3t
- MP_UNSET_midpoint_repository_hibernateHbm2ddl=1
- MP_NO_ENV_COMPAT=1
networks:
- net
volumes:
- midpoint_home:/opt/midpoint/var
- ./:/opt/midpoint/var/import:Z
midpoint_restore:
image: evolveum/midpoint:${MP_VER:-latest}-alpine
command:
- /bin/bash
- -c
- |
echo 'Restoring database...'
# Restore PostgreSQL data
cp -r /backup/midpoint_data_backup/* /var/lib/postgresql/data/
chown -R 999:999 /var/lib/postgresql/data/
chmod -R 700 /var/lib/postgresql/data/
echo 'Restoring keystore file...'
# Restore keystore file
if [ -f /backup/keystore.jceks.backup ]; then
cp /backup/keystore.jceks.backup /opt/midpoint/var/keystore.jceks
chmod 600 /opt/midpoint/var/keystore.jceks
echo 'Keystore file restored successfully.'
else
echo 'WARNING: Keystore backup file not found!'
fi
echo 'Restore completed.'
depends_on:
- midpoint_data
volumes:
- midpoint_data:/var/lib/postgresql/data
- midpoint_home:/opt/midpoint/var
- ./:/backup:Z
networks:
- net
profiles:
- restore
networks:
net:
driver: bridge
volumes:
midpoint_data:
midpoint_home:
Nun bin ich in der Lage beliebige Daten, die in Containern und Volumes erzeigt werden, zu sichern und wieder herzustellen.
Als Nächstes möchte ich einen Nginx Server an einen LDAP Server anbinden.
So, stay tuned,
Achim Mertens