MariaDB mySQL backup und wiederherstellen in Docker

Datenbanken aus einem MariaDB- oder MySQL-Container sichern und zurückspielen: die Einzeiler für mysqldump und mariadb-dump, der Import über docker exec -i und der Umzug von einem Container in den nächsten in einem einzigen Befehl.

Sobald du Docker für eine MariaDB- oder MySQL-Datenbank nutzt, ist das Sichern und Einspielen einer Datenbank etwas umständlicher: Die Datenbank läuft im Container. Das Backup soll auf den Host. Ein typischer Fall ist die MySQL-Datenbank hinter einer WordPress-Installation in Docker.

Ein Weg ist, in der docker-compose.yml einen Ordner vom Host in den Container zu mounten und dann im Container zu arbeiten. Kürzer geht es mit docker exec und den folgenden Einzeilern. Der Trick daran: >, < und | wertet die Shell auf dem Host aus, nicht der Container. Der Dump wandert also über die Standardausgabe des Containers in eine Datei auf dem Host, beim Import läuft es umgekehrt.

Eine Vorbemerkung, die weiter unten mehrfach wiederkommt: Welche Client-Programme im Container liegen, hängt am Image. MySQL-Images bringen mysqldump und mysql mit, MariaDB-Images ab Version 11.0 nur noch mariadb-dump und mariadb.

Breaking Change (Mai 2024):
Die Releases vom 16. Mai 2024 haben das geändert: 10.5.25, 10.6.18, 10.11.8, 11.0.6, 11.1.5, 11.2.4 und 11.4.2. Seither schreibt mariadb-dump als allererste Zeile die Direktive /*!999999\- enable the sandbox mode */ in jeden Dump (MariaDB.org, Release Notes 10.11.8, MDEV-33727). Sie schaltet den Client in einen Sandbox-Modus, in dem ein manipulierter Dump keine Kommandozeilenbefehle mehr auslösen kann.

Der Preis dafür: Ältere MariaDB-Clients und alle MySQL-Clients kennen die Zeile nicht und brechen mit ERROR at line 1: Unknown command '\-' ab. Das Problem bleibt damit nicht bei alten Servern. Auch der aktuelle mysql-Client scheitert an einem MariaDB-Dump. Wer den Dump trotzdem dorthin bringen muss, schneidet die erste Zeile ab. Beim Sichern geht das mit mariadb-dump … | tail -n +2 > db.sql, beim Import mit tail -n +2 db.sql | mysql …. Die Datenbank-Connectoren der Programmiersprachen sind laut MariaDB nicht betroffen.

Backup mittels mysqldump

docker exec db_container_name mysqldump [--user nutzername] [--password=deinpasswort] datenbank_name > /dein/backup/pfad/db.sql

Merke:
Für MySQL-Images und für MariaDB-Images bis 10.11 funktioniert mysqldump weiterhin, und zwar für beide Datenbanken gleich. Vorausgesetzt, das Programm liegt im Container. Ab MariaDB 11.0 liegt es dort nicht mehr, siehe den nächsten Abschnitt.

Backup mit mariadb-dump

Ab MariaDB 11.0 ist der Symlink mysqldump abgekündigt und aus dem offiziellen Docker-Image entfernt (MariaDB-Dokumentation). Es blieb nicht beim Dump. Mit 11.0 sind die mysql-benannten Programme insgesamt aus dem Image verschwunden, weshalb damals auch der Healthcheck des Images ohne mysqladmin neu gebaut werden musste (MariaDB.org). Für MariaDB heißen die drei also mariadb-dump, mariadb und mariadb-admin. Das Vorgehen bleibt identisch:

docker exec db_container_name mariadb-dump [--user nutzername] [--password=deinpasswort] datenbank_name > /dein/backup/pfad/db.sql

Zwei Dinge lässt dieser Einzeiler offen. Erstens sperrt mariadb-dump ohne weitere Angabe die Tabellen jeder Datenbank, solange er sie liest; --lock-tables ist die Voreinstellung. Bei InnoDB-Tabellen ist --single-transaction die bessere Wahl: Der Dump läuft dann in einer Transaktion und bildet den Stand eines Zeitpunkts ab, ohne schreibende Zugriffe zu blockieren. Für MyISAM hilft das nicht, weil die Engine nichts von Transaktionen weiß (MariaDB-Dokumentation). Zweitens landet das Passwort so in der Prozessliste und in der Shell-History; die MariaDB-Dokumentation nennt das ausdrücklich unsicher und verweist auf eine Option-Datei. Oder auf --password ohne Wert, dann fragt der Client interaktiv nach.

Wiederherstellen der Datenbank

Um ein Backup zurückzuspielen, verwende folgenden Befehl:

docker exec -i db_container_name mysql [--user nutzername] [--password=deinpasswort] datenbank_name < /dein/backup/pfad/db.sql

Für MySQL-Images und für MariaDB-Images bis 10.11 stimmt der Befehl so. Bei einem MariaDB-Image ab 11.0 heißt der Client mariadb, der Import lautet dort also docker exec -i db_container_name mariadb [--user nutzername] [--password=deinpasswort] datenbank_name < /dein/backup/pfad/db.sql.

Das -i ist Pflicht. Es hält die Standardeingabe offen (Docker-Referenz). Lässt du es weg, bekommt der Client im Container keine Eingabe, liest nichts ein und beendet sich, ohne dass etwas nach einem Fehler aussieht.

Und noch eine Falle: Der Dump einer einzelnen Datenbank enthält kein CREATE DATABASE. Die Zieldatenbank muss also vorher existieren, sonst antwortet der Server mit Fehler 1049, Unknown database. Wer das Anlegen mitsichern will, gibt beim Dump --databases datenbank_name an. Dann stehen CREATE DATABASE und USE im Dump.

Backup und Wiederherstellen in einem Befehl

Willst du eine Datenbank aus einem Container in einen anderen umziehen, ohne eine Datei dazwischenzulegen, verbindest du Dump und Import mit einer Pipe. Für MySQL sieht das so aus:

docker exec db_container_name mysqldump [--user nutzername] [--password=deinpasswort] datenbank_name | docker exec -i zweiter_db_container_name mysql [--user nutzername] [--password=deinpasswort] -C datenbank_name

Für MariaDB-Datenbanken wird nun folgender Befehl eingesetzt:

docker exec db_container_name mariadb-dump [--user nutzername] [--password=deinpasswort] datenbank_name | docker exec -i zweiter_db_container_name mysql [--user nutzername] [--password=deinpasswort] datenbank_name

Das -C im MySQL-Beispiel ist die Kurzform von --compress und komprimiert die Client-Verbindung. Seit MySQL 8.0.18 gilt die Option als Altlast neben --compression-algorithms, funktioniert aber weiterhin (MySQL-Handbuch). Laufen beide Container auf demselben Host, bringt sie wenig.

Zwei Einschränkungen zu dieser Pipe. Die zweite Zeile schickt einen MariaDB-Dump in ein Programm namens mysql, und ob das gutgeht, entscheidet die Sandbox-Zeile von oben: Ein MariaDB-Client ab den Mai-2024-Releases versteht sie, ein älterer nicht, ein echter MySQL-Client nie. In einem MariaDB-Image ab 11.0 gibt es mysql ohnehin nicht mehr, dort heißt das Ziel mariadb. Geht der Dump an MySQL, muss die erste Zeile vorher weg. Zweitens entsteht bei dieser Variante keine Datei und damit kein zweiter Versuch. Bricht die Übertragung ab, ist der Dump verloren und die Zieldatenbank nur halb gefüllt.

Cheers!