Docker Postgres Backup Script

Aus Laub-Home Wiki

Bei Postgres Containern legt man die persistenten Datenbank Daten zwar auf einem Docker Volume ab, sichert die Datenbank aber dennoch mit pg_dumpall. Dies verhindert dass die Daten die man sichert inkonsistent sind, da man beim Abzug der Datenbank Dateien sicherstellen müsste, das keine Datenbankoperatoren zur gleichen Zeit stattfinden. Man müsste also den DB Container vorher stoppen. Deshalb ist es Best Practice die im Container laufende Postgres Datenbank via pg_dumpall zu sichern. Den Befehl reicht man einfach in den Container rein und lässt den Output, der über stdout ausgegeben wird in eine Datei laufen. Will man es komprimieren, lässt man den Output vorher durch gzip laufen. (mehr Informationen hier)

docker exec CONTAINERNAME pg_dumpall -c -U POSTGRESUSER \
        | gzip > backup.sql.gz

Will man wissen, welche Container auf dem Postgres Image aufsetzen, welches man ggf. sichern möchte, erledigt das der folgende Befehl:

docker ps --format '{{.Names}}:{{.Image}}' | grep 'postgres' | cut -d":" -f1

--format '{{.Names}}:{{.Image}}' sorgt dafür das nur der Containername und das verwendete Docker Image, aller laufender Container ausgegeben werden, der grep Befehl filtert die Ausgabe dann nur auf Postgres Images. Das backup-docker-postgres.sh Script bündelt die beiden Befehle und sichert somit alle Postgres Datenbanken, die als Container auf dem Docker Host laufen in den definierten Backup Ordner. Die Datenbank Backups werden mit einem Zeitstempel versehen und können nach X-Tagen (DAYS) automatisch gelöscht werden. Wichtig ist auch, da es sich um ein lokales Backup handelt, sollte man auf jeden Fall die Backup Dateien auf irgendein anderes System oder in die Cloud (Dropbox, Google Drive und Co) sichern. Das Script legt man am besten nach /usr/local/sbin/backup-docker-postgres.sh. Dieses kann dann via cron-job automatisch, regelmäßig gestartet werden.

Script backup-docker-postgres.sh

Das Script kann einfach in /usr/local/sbin/ angelegt werden. Nach dem Anlegen das Execute Recht vergeben und die folgenden Parameter im Script nach belieben definieren:

Parameter Definition Beispiel
BACKUPDIR definiert den Backup Ordner, sollte er nicht existieren, wird er automatisch angelegt
BACKUPDIR=/backup/postgres
DAYS definiert die wie lange ein Backup aufgehoben wird
DAYS=2
TIMESTAMP definiert den Zeitstempel der im Backup Dateiname verwendet wird
TIMESTAMP=$(date +"%Y%m%d%H%M")
CONTAINER definiert die Container, dessen Datenbanken gesichert werden sollen. Bei mehr als einem Container, einfach ein Leerzeichen zwischen die verschiedenen Container Namen setzen. Hier kann auch mittels
docker ps --format '{{.Names}}:{{.Image}}' | grep 'postgres' | cut -d":" -f1
gearbeitet werden, um zum Beispiel alle Container, auf Basis des Postgres Image, zu sichern, oder diese mit grep weiter zu filtern. (Siehe Kommentare im Script)
CONTAINER="postgrescontainer1 postgrescontainer2 postgrescontainer3"
VOLUME=CONTAINER=$(docker ps --format '{{.Names}}:{{.Image}}' | grep 'postgres' | cut -d":" -f1)
CONTAINER=$(docker ps --format '{{.Names}}:{{.Image}}' | grep 'postgres' | cut -d":" -f1 | grep -v 'container1\|container2')

Download des Scriptes ist hier möglich: https://github.com/alaub81/backup_docker_scripts/raw/main/backup-docker-postgres.sh

/usr/local/sbin/backup-docker-postgres.sh

#!/usr/bin/env bash
#########################################################################
#Name: backup-docker-postgres.sh
#Subscription: This Script backups docker postgres containers,
#or better dumps their databases to a backup directory
##by A. Laub
#andreas[-at-]laub-home.de
#
#License:
#This program is free software: you can redistribute it and/or modify it
#under the terms of the GNU General Public License as published by the
#Free Software Foundation, either version 3 of the License, or (at your option)
#any later version.
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
#or FITNESS FOR A PARTICULAR PURPOSE.
#########################################################################
#Set the language
export LANG="en_US.UTF-8"
#Load the Pathes
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# set the variables

# Where to store the Backup files?
BACKUPDIR=/backup/postgres

# How many Days should a backup be available?
DAYS=2

# Timestamp definition for the backupfiles (example: $(date +"%Y%m%d%H%M") = 20200124-2034)
TIMESTAMP=$(date +"%Y%m%d%H%M")

# Which Containers do you want to backup?
# Container names separated by space
#CONTAINER="postgrescontainer1 postgrescontainer2 postgrescontainer3"
# you can use "$(docker ps --format '{{.Names}}:{{.Image}}' | grep 'postgres' | cut -d":" -f1)"
# for all containers which are using postgres images
#CONTAINER=$(docker ps --format '{{.Names}}:{{.Image}}' | grep 'postgres' | cut -d":" -f1)
# you can filter all containers with grep (include only) or grep -v (exclude) or a combination of both
# to do a filter for 2 or more arguments separate them with "\|"
# example: $(docker ps --format '{{.Names}}:{{.Image}}' | grep 'postgres' | cut -d":" -f1 | grep -v 'container1\|container2')
#CONTAINER=$(docker ps --format '{{.Names}}:{{.Image}}' | grep 'postgres' | cut -d":" -f1 | grep -v 'container1\|container2')
CONTAINER=$(docker ps --format '{{.Names}}:{{.Image}}' | grep 'postgres' | cut -d":" -f1)


### Do the stuff
echo -e "Start $TIMESTAMP Backup for Databases: \n"
if [ ! -d $BACKUPDIR ]; then
        mkdir -p $BACKUPDIR
fi

for i in $CONTAINER; do
        POSTGRES_USER=$(docker exec $i env | grep POSTGRES_USER |cut -d"=" -f2)
        echo -e " create Backup for Database on Container:\n  * $i";
        docker exec $i pg_dumpall -c -U $POSTGRES_USER | gzip > $BACKUPDIR/$i-$TIMESTAMP.sql.gz
        # dont delete last old backups!
        OLD_BACKUPS=$(ls -1 $BACKUPDIR/$i*.gz |wc -l)
        if [ $OLD_BACKUPS -gt $DAYS ]; then
                find $BACKUPDIR -name "$i*.gz" -daystart -mtime +$DAYS -delete
        fi
done
echo -e "\n$TIMESTAMP Backup for Databases completed\n"

zum Schluss das execute Recht setzten:

chmod +x /usr/local/sbin/backup-docker-postgres.sh

Das Script kann nun via

backup-docker-postgres.sh

ausgeführt werden.

Regelmäßiges Backup einrichten (cron-job)

Will man nun das Script regelmäßig, zum Beispiel täglich ausführen, reicht es einen Symlink in das cron.daily Verzeichnis zu legen:

ln -s /usr/local/sbin/backup-docker-postgres.sh /etc/cron.daily/backup-docker-postgres

Restore

Der Restore erfolgt händisch via psql command. Die Benötigten Variablen (DB Name und DB User und ggf. DB Passwort) bekommt man einfach mit folgendem Befehl ausgeben:

docker exec CONTAINERNAME env

Mit diesen Informationen kann man dann einfach den folgenden Befehl editieren und ausführen

# Restore Aller Datenbanken komprimiert
zcat backup.sql.gz |docker exec -i CONTAINERNAME psql -U POSTGRESUSER

nun sollte die Datenbank mit dem SQL Backup gefüttert sein.

GitHub Repository

Das Ganze könnt ihr auch in GitHub als komplettes Repository finden: