Praxis: Backup Docker Container-Daten: Volumes / Bind Mounts
In meinem Artikel „Alle Docker-Container: Host übersiedeln, Theorie und Praxis“, bin ich bereits ein klein wenig auf das Thema Backup eingegangen. Wer den Artikel verfolgt hat, weiß, dass ich die Daten meiner Docker-Container über Bind-Mounts auslagere und mit rsync sichere. Gestartet wird der Backup-Job über crontab. Doch zunächst habe ich mir Gedanken gemacht, was beim Einsatz von Docker in einem Single-Server-Setup eigentlich gesichert werden muss:
Docker Container Backup?
Bei der Verwendung von Volumes oder Bind-Mounts müssen die Containerdaten nicht gesichert werden. Voraussetzung ist allerdings, dass der Container beim Austausch oder Neuanlegen mit den ursprünglichen Optionen oder mit der ursprünglichen Docker Compose-Datei erstellt wird. Aus diesem Grund empfehle ich die Verwendung von Docker Compose, da die docker-compose.yml-Datei verschiedene Container einfach zu einem Service kombinieren kann und dabei sämtliche Optionen für das Erstellen der Container in einer Datei hinterlegt und damit dokumentiert. Ein einfaches "docker compose up" startet die hinterlegten Container mit den angegebenen Optionen, auch wenn die Container noch nicht oder nicht mehr vorhanden sind. Daten, die dabei nicht verloren gehen sollen, können in Volumes oder Bind-Mounts, also bestimmten Ordnern außerhalb des Containers ausgelagert werden. Das Wichtigste an einem Container sind also die verwendeten Optionen, mit denen dieser erstellt wurde - am einfachsten in Form einer docker-compose.yml-Datei - und die Daten der verwendeten Volumes oder Bind-Mounts. Für das Sichern von Docker Volumes schaut der offizielle Weg wie folgt aus:
Backup / Restore: Docker Volumes
Ein Blick in die offizielle Dokumentation von Docker empfiehlt für ein Backup oder Restore einen eigenen Container zu starten und das Volume über einen Bind-Mount auf den Host zu übertragen:
Volume sichern:
docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
Volume wiederherstellen:
docker run --rm --volumes-from datavolume-name -v $(pwd):/backup image-name bash -c "cd /path-to-datavolume && tar xvf /backup/backup.tar --strip 1"
Quelle, siehe: https://docs.docker.com/storage/volumes/ (Abrufdatum: 06.09.2022)
Mir ist bewusst, dass Docker-Volumes die bevorzugte Variante für das Speichern von Docker-Daten sind, ich habe mich bei meinem Setup dennoch gegen den Einsatz von Volumes entschieden. Der Grund dafür ist, dass einfache Ordner (Bind-Mounts) von einem einzelnen Server wesentlich einfacher gesichert werden können. Siehe auch, Docker Volumes.
Bind-Mounts sichern
Beim Einsatz von Bind-Mounts können bestimmte Ordner der Docker-Container auf einen definierten Ordner am Host gemappt werden. Die Ordner enthalten alle relevanten Daten der Container, wodurch sich auch das Backup auf diese Ordner beschränkt. Ein besonderes Augenmerk sollte dabei auf die verwendeten Datenbanken gelegt werden:
MySQL-Datenbank im laufenden Betrieb kopieren
Wird eine Datenbank im laufenden Betrieb kopiert, ist nicht sichergestellt, dass die Datenbank am Ziel konsistent ist. Auch wenn ich in der Vergangenheit immer wieder mal MySQL-Datenbanken im Betrieb auf meine Entwicklungsumgebung kopiert habe, hatte ich dennoch noch nie ein Problem mit einer Datenbank. Ob ein Problem entstehen kann, liegt an den Daten und hauptsächlich auch daran, wie viele Daten während des Kopierens verändert werden. Fakt ist, dass das Kopieren einer Datenbank im laufenden Betrieb nicht ganz sauber ist und Probleme verursachen könnte.
Lösung für MySQL: mysqldump
Wer einen Dump einer laufenden Mysql-Datenbank erstellen möchte, sollte dafür mysqldump verwenden. Mit mysqldump kann die Datenbank exportiert und am Zielsystem importiert werden, siehe: MySQL Befehle in Linux: Verbindung, Datenbank, Backup. Mysqldump sollte vorbereitend zu einem Backup ausgeführt werden, wodurch das erstellte Abbild der Datenbanken in das Backup aufgenommen werden kann. Da die Dumps alle Daten enthalten, müssen die eigentlichen Datenbankfiles des Containers nicht zusätzlich kopiert werden.
Die erzeugten Datenbank-Dumps können einfach zu einem Kopierauftrag hinzugefügt werden.
Hier ein Befehl von dem Remote-Computer mit dessen Hilfe ich das Backup erstelle. Der Befehl verbindet sich mit SSH auf den Server und erstellt im Datenbankcontainer einen Dump mit mysqldump und schreibt diesen in eine Datei.
ssh root@server.domain.tld "docker exec mysqlcontainer mysqldump --flush-logs --user=root --password=??? mysqlusername | gzip -c > /backups/db/mysqlcontainerdump.gz"
Container stoppen
Alternativ könnte der Container für die Datenbank auch gestoppt, die Files gesichert und die Datenbank im Anschluss wieder gestartet werden. Nachdem diese Variante mit einer kurzen Downtime verbunden ist, würde ich die Vorgehensweise nicht für ein Backup empfehlen.
rsync -rltD --delete /var/web/libe.net/db/ /var/web/libedbcopy
docker stop libenet_libenet-mysql.1.csubj0bcuu8ssd9xyg6rbyve5
rsync -rltD --delete /var/web/libe.net/db/ /backup/dbcopy
docker start libenet_libenet-mysql.1.csubj0bcuu8ssd9xyg6rbyve5
Diese Variante habe ich zuletzt beim Übersiedeln meines Servers versucht, siehe: Webserver mit Docker Container umziehen, Theorie und Praxis
In der Praxis: Wie ich meine Containerdaten sichere
Ich verwende Docker auf einem V-Server im Internet und zu Hause auf meiner NAS. Die Docker Container am gemieteten V-Server übertrage ich mittels rsync täglich auf meine NAS und die Daten der NAS auf eine externe Festplatte und zusätzlich auf meinen Linux Receiver, siehe auch: www.script-example.com/rsync-bash-funktion.
Ich verwende als Vorbereitung für ein Backup oder das Übersiedeln von Container jeweils einen Ordner pro Applikation. Innerhalb des Ordners habe ich die Optionen für das Erstellen der Container in Form einer docker-compose.yml - Datei hinterlegt und als Unterordner die im docker-compose-yml-File verwendeten Bind-Mounts angelegt. Exemplarisch habe ich alle relevanten Daten eines Docker-Services ca. wie folgt organisiert:
- ./dockerService1/docker-compose.yml
- ./dockerService1/db/* ... Datenbankdateien
- ./dockerService1/conf/* ... persistente Konfigfiles
In den Docker-Compose Dateien können die Ordner relativ zum Docker-compose-File angegeben werden
version: '3.1'
services:
service1:
image: test
...
volumes:
- ./dockerService1/www:/var/www
Siehe auch: Docker Daten speichern: Docker Volumes vs. Host-Ordner
Beim Einsatz von Datenbanken könnte zudem ein weiterer Unterordner für die Datenbandumps erstellt werden:
- ./dockerService1/dbdumps/* ... Datenbankdumps
Dadurch, dass ich die eigentlichen Daten der Datenbank mithilfe von mysqldump in den dbdumps-Ordner sichere, müsste ich die eigentlichen Datenbankdateien nicht mitsicher und könnte diese auch an einen anderen Ort ablegen. Auch der Einsatz von Volumes für die Datenbankfiles wäre an dieser Stelle denkbar. Im Fehlerfall können die Dumps restored und die Datenbank somit wiederhergestellt werden.
Um den hier beschriebenen dockerService1 zu sichern, reicht es alle Dateien und Ordner unterhalb von dockerService1 zu sichern:
- Die Datei docker-compose.yml kann den Docker-Container im Fehlerfall oder bei einem Serverwechsel erneut erstellen und
- alle persistenten Userdaten werden über Bind-Mounts in den Container gemappt.
Für die Datenbankcontainer habe ich in die Bash-Datei von script-example.com die hier beschriebene mysql-dump-Zeile eingefügt.
#!/bin/bash
LOGFILE="/var/log/rsync.txt"
# ... Variablen und backup-Funktion von https://www.script-example.com/rsync-bash-funktion
# ...
ssh root@domain.tld "docker exec mysqlcontainername mysqldump --user=root --password=dbpassword dbname | gzip -c > /var/web/webservicename/dbdumps/dump.gz"
backup '-e ssh root@domaintld:/var/web' /backup/cloud
# ...
#Finish Script: Runtime-Information and Final-Summary: siehe: https://www.script-example.com/rsync-bash-funktion
Der Mysqldump wird dabei am Webserver abgelegt (ssh root@ ...) und mit rsync übertragen (backup '-e ssh ...)
Damit der Backup-Job jeden Tag läuft, habe ich das Bash-Script in Crontab eingefügt:
22 0 * * * sudo /scripts/rsync-backup.sh > /dev/null 2>&1
siehe auch: Linux CronJobs - geplante Tasks | Debian crontab [erklärt]
Fazit
Mit rsync und optional mysqldump und dem richtigen Ordner-Layout ist es sehr einfach alle Docker-Containerdaten eines Single-Server-Setups auf einen anderen Host zu übertragen und damit ein Backup zu erstellen. Um mehrere Versionen der Daten abzulegen, könnten am Backup-Host Filesystemsnapshots verwendet werden, siehe auch: ZFS vs BTRFS - Filesystem | Deduplizierung und Snapshots. Dadurch, dass Daten mit rsync auf dem Backup-Host in derselben Form wie am Quellhost abgelegt werden können, wäre es auch denkbar die Container im Fehlerfall auf dem Backup-Host zu starten und diesen theoretisch als Standby-Server zu verwenden. Voraussetzung dafür ist natürlich eine geeignete Hardware und eine geeignete Netzwerkanbindung.
{{percentage}} % positiv