Articles tagged "backup"

Docker-Gitea-Postgres Part 2: Postgres Backup

Teil 2 einer Reihe über Gitea & Postgres im Docker-Container: Postgres Backup

Hintergrund dieser Reihe ist das notwendige Update einer Docker-Gitea-Instanz und in diesem Rahmen auch das Major-Version-Upgrade der zugehörigen Postgres-Datenbank.

Postgres

Backup allgemein

Um aus einer laufenden Postgres-Instanz (nahezu) alle Daten zu sichern, gibt es den Befehl pg_dumpall. Da das entstehende Backup reiner Text ist, wird es gleich in einem Rutsch mit xz komprimiert, damit es kleiner wird.

postgres@db:~ # pg_dumpall | xz >pg-backup.xz
[...]

Umgekehrt kann man auch einen Dump in eine (leere) Datenbank wieder einspielen :

postgres@db:~ # xzcat pg-backup.xz | psql -U <USERNAME> -d <DATABASE>

Backup im Docker

Da in den meisten Docker-Umgebungen nicht nur eine Postgres-Datenbank laufen wird, habe ich mir im Laufe der Zeit ein Script geschrieben, welches bei Übergabe des Container-Namens sich ein Backup auf den Host (konkret: ins aktuelle Verzeichnis) zieht:

root@docker:~ # .../postgres-backup.sh gitea_db
-rw-r--r-- 1 root root   115320 Apr 17 09:59 gitea_db_2022-04-17_09:59:23_postgres:9-alpine_9.6.24.xz

Im Backup-Dateinamen sind nacheinander Container-Name, Zeitstempel, Image-Name und Posgres-Version hinterlegt. So kann man auch z.B. im Rahmen eines Upgrades ;) nachvollziehen, mit bzw. auf Basis welcher Version das Backup angelegt wurde.

Das Script macht folgende Schritte:

  • (Zeile 3-13) Basierend auf dem übergebenen Namen wird die Container-ID ermittelt
  • (Zeile 14-19) Beginnt der zu Grunde liegende Image-Name nicht mit "postgres" wird das Script abgebrochen - es ist vermutlich kein Postgres-Container
  • (Zeile 21-29) Der Postgres-Username und -Version werden aus den Container-Umgebungsvariablen extrahiert
  • (Zeile 31-36) Der Backup-Dateinamen wird generiert und sichergestellt, dass es nicht bereits existiert
  • (Zeile 37-39) pg_dumpall wird aufgerufen, das Ergebnis durch xz komprimiert und ein ls -l auf die Datei angezeigt
#!/bin/ash

PGCONTAINER=$1
PGCONTID=$(docker ps -qf "name=^${PGCONTAINER}$")

if [ -z "$PGCONTID" ]; then
    echo "Container '${PGCONTAINER}' not found"
    exit 1
elif [ $(echo ${PGCONTID}| wc -m) -ne 13 ]; then
    echo "Unknown container id: ${PGCONTID}"
    exit 1
fi

DIMAGE=$(docker inspect ${PGCONTID} | jq '.[].Config.Image' | tr -d '"')
PGIMAGE=$(basename $DIMAGE | grep '\(^\|/\)postgres:')
if [ -z "${PGIMAGE}" ]; then
    echo "Not a Posgres image '${DIMAGE}'"
    exit 1
fi

export $(docker inspect ${PGCONTID} | jq '.[].Config.Env' | grep '"POSTGRES_USER\|"PG_VERSION' | tr -d '"' | sed -e 's/,$//')

if [ -z "${POSTGRES_USER}" ]; then
    echo "No Posgres user found"
    exit 1
elif [ -z "${PG_VERSION}" ]; then
    echo "No Postgres version number found"
    exit 1
fi

BACKUPFILE=${PGCONTAINER}_$(date +%F_%T)_${PGIMAGE}_${PG_VERSION}
if [ -f "${BACKUPFILE}.xz" ]; then
    echo "Backupfile exists"
    exit 1
fi

docker exec -it -w /tmp ${PGCONTID} sh -c "pg_dumpall -U ${POSTGRES_USER}" | xz >${BACKUPFILE}.xz

ls -l ${BACKUPFILE}.xz

Restore im Docker

Ich habe es nicht geschafft, die Backup-Datei vom Host aus direkt an einen psql in einen Container per Pipe zu übergeben. Falls hier jemand einen Tipp hat, gerne per Mail an mich ;)

Um also die Daten in den neu angelegten (oder umgezogenen oder...) Container zu bringen, kopiert man die Backup-Datei in den Container:

root@docker:~ # docker cp $BACKUPFILE gitea_db:/tmp/

Der zweite Schritt besteht dann darin, im Container das Backup wieder an Postgres zu verfüttern:

root@docker:~ # docker exec -it gitea_db bash
root@bash-5.1# psql -U gitea -d gitea < /tmp/gitea_db_2022-04-17_09:59:23_postgres:9-alpine_9.6.24.xz
root@bash-5.1# rm /tmp/gitea_db_2022-04-17_09:59:23_postgres:9-alpine_9.6.24.xz

Es macht natürlich keinen Sinn, das Backup im Container liegen zu lassen, es ist schließlich nur eine Kopie, daher wird es auch gleich wieder gelöscht.

Anmerkungen:

  • Meine Docker-Installationen laufen im Allgemeinen auf einem btrfs-Dateisystem. Daher ist Kopieren und Löschen ein Vorgang, der keinerlei Speicher benötigt. Läuft Docker allerdings mit irgendeiner Art von Images, dann ist dieser HD-/SSD-Speicherplatz bis zum nächsten Container-Anlegen und anschließendem "purge" verloren! Hier geht's nur um ca. 100kB, also wirklich nicht viel. Bei einer Multi-GiByte-Datenbank sieht das dann vielleicht anders aus!
  • Natürlich könnte man die Backup-Datei in ein temporäres Docker-Volume kopieren, dieses in den neuen Datenbank-Container einhängen und von dort das Restore machen. Der Aufwand war/ist mir aber zu groß.
  • Wer sofort seinen Speicher zurück möchte, der kann natürlich nach dem Restore den Container zerstören und neu anlegen. Dann ist der Speicher nach der nächsten Maintainance auch wieder frei.

Docker-Gitea-Postgres Part 1: Gitea Backup

Teil 1 einer Reihe über Gitea & Postgres im Docker-Container: Gitea Backup

Hintergrund dieser Reihe ist das notwendige Update einer Docker-Gitea-Instanz und in diesem Rahmen auch das Major-Version-Upgrade der zugehörigen Postgres-Datenbank.

Gitea

Backup allgemein

Gitea hat eine integrierte Backup-Funktion, welche man sehr einfach über die Command-Line aufrufen kann. Ergebnis ist dann eine ZIP-Datei, welche später oder wo anders wieder eingespielt werden kann:

gitea@gitea:~ # gitea dump -c .../app.ini --file /tmp/gitea-dump.zip

Backup im Docker

Wenn Gitea im Docker läuft, muss man natürlich auf die laufende Gitea-Instanz im Container zugreifen und anschließend die Sicherung aus dem Container holen. Dies kann mit dem folgenden Script passieren, welches der Sicherung am Schluss auch noch einen Zeitstempel verpasst:

#!/bin/ash

CONT_NAME=gitea
CONT_ID=$(docker ps -qf "name=^${CONT_NAME}$")
docker exec -u git -it -w /tmp ${CONT_ID} bash -c '/app/gitea/gitea dump -c /data/gitea/conf/app.ini --file /tmp/gitea-dump.zip'
docker cp ${CONT_ID}:/tmp/gitea-dump.zip .
docker exec -u git -it -w /tmp ${CONT_ID} bash -c 'rm /tmp/gitea-dump.zip'
BACKUPFILE=gitea-dump-$(date +"%Y%m%d%H%M").zip
mv ./gitea-dump.zip $BACKUPFILE

Da ich aktuell nur eine Gitea-Instanz am Laufen habe, ist der Container-Name im Script in Zeile 3 fest auf gitea gesetzt - natürlich kann man das Script an dieser Stelle erweitern und z.B. den Container-Namen an der Command-Line übergeben. Oder über ein Konstrukt eine Schleife bauen, welche gleich alle Gitea-Instanzen sichert.

Apropos Anpassungen: Meine Docker-Hosts nutzen AlpineLinux, daher ruft das Script in Zeile 1 eine ash auf - dies muss ggf. noch auf sh oder bash korrigiert werden.

Restore

Leider gibt es aktuell kein Restore-Command für Gitea. Die notwendigen Schritte sind jedoch IMHO ausreichend in der - nicht immer sehr ausführlichen - Dokumentation von Gitea unter Usage: Backup and Restore beschrieben. Bei mir haben die Schritte funktioniert - sowohl beim Restore als auch beim Umzug auf einen anderen Rechner/Host. Falls der Link nicht mehr gehen sollte, hier die Schritte:

# open bash session in container
root@docker:~ # docker exec --user git -it 2a83b293548e bash
# unzip your backup file within the container
root@bash-5.1# unzip gitea-dump-1610949662.zip
root@bash-5.1# cd gitea-dump-1610949662
# restore the gitea data
root@bash-5.1# mv data/* /data/gitea
# restore the repositories itself
root@bash-5.1# mv repos/* /data/git/repositories/
# adjust file permissions
root@bash-5.1# chown -R git:git /data
# Regenerate Git Hooks
root@bash-5.1# /usr/local/bin/gitea -c '/data/gitea/conf/app.ini' admin regenerate hooks