mysqldump mit Docker versioniert nutzen

Inhaltsverzeichnis

Die Nutzung von Datenbank-Tools in verschiedenen Versionen kann schnell vereinfacht werden. Lokal mit MySQL 5.6, MySQL 8 oder MariaDB arbeiten wird damit erheblich erleichtert.

Hierfür arbeiten wir uns Stück für Stück zu einem Bash-Alias das uns die wiederkehrenden Arbeiten erleichtert.

Fangen wir damit an die Docker-Images zu finden, die wir generell verwenden wollen:

mysql:5.6 und mysql:8 für die MySQL-Versionen die sich grundlegend unterscheiden. mariadb:10 sollte ausreichen, da hier nur eine Major-Version existiert.

Container aufsetzen

Starten wir also erst einmal einen MySQL-Server in der Version 8 um uns durchzuarbeiten. Dieser ist nur über localhost:3306 erreichbar, wir wollen ja nicht das jemand im LAN/WAN mitspielen darf.

docker run --rm -d \
    --name mysqldemo \
    -p 127.0.0.1:3306:3306 \
    -e MYSQL_ROOT_PASSWORD=rootpass mysql:8

Um diesen Container später wieder zu löschen, den Port und Speicherplatz wieder freizugeben, bitte Folgendes vormerken:

docker stop mysqldemo

Befehl ausarbeiten

Nun fertigen wir uns einen Befehl mit dem wir über mysqldump ein Backup der mysql.user-Tabelle machen können:

docker run --net=host --rm -it mysql:8 \
  mysqldump --opt --no-tablespaces \
  --single-transaction --lock-tables=false \
  --protocol=TCP -h localhost --port=3306  -u root -prootpass \
  mysql user

Dies führt wie gewollt zu einem gültigen Backup durch das offizielle Werkzeug.

-- MySQL dump 10.13  Distrib 8.0.22, for Linux (x86_64)
--
-- Host: localhost    Database: mysql
-- ------------------------------------------------------
-- Server version       8.0.22

Parameter erklärt

Gehen wir kurz auf die Parameter ein und warum diese gewählt wurden:

--net=host wird genutzt, um das Image auf das Host-Netzwerk aufzulegen anstatt einem virtuellen Subnetz. Dies ermöglicht uns Zugriff auf localhost:3306 unseres Host-Rechners. Das Weglassen dieser Option würde dazu führen das localhost der Container wäre und nicht der Host-Rechner. Entsprechend würden wir die folgende Fehlermeldung bekommen:

mysqldump: Got error: 2003: Can't connect to MySQL server on 'localhost' (99) when trying to connect`

--rm gibt an das dieser Container gelöscht werden kann wenn er beendet wird.

-it zeigt auf das wir im Interaktiv-Modus gerne ein TTY hätten. Kurz gesagt verhält sich unser Befehl dadurch wie eine eigene Konsole und kann so z. B. auf CTRL+C reagieren.

--opt erwzingt viele Mechaniken die, zusammengefasst, ein optimiertes Backup erstellen welches effizient ausgeführt werden kann. So werden z.B. viele INSERT-Anweisungen gruppiert und nicht einzeln ausgeführt.

--no-tablespaces deaktiviert die Sicherung von selbigen. Es handelt sich um erweitertes Feature, welches auf den meisten Shared-Hosts deaktiviert ist hiermit unterbinden wir dieses Abbruchszenario.

--single-transaction --lock-tables=false sorgen sowohl bei InnoDB als auch bei MyISAM dafür das so wenig sperrende Methoden wie möglich verwendet werden. So wird nicht gleich die ganze Webseite unnutzbar da alle Datenbanken gesperrt sind.

--protocol=TCP erzwingt das Protokoll, selbst wenn aus unerfindlichen Gründen Sockets bevorzugt werden sollten. Erkennt man meist an folgender Fehlermeldung:

mysqldump: Got error: 2002: Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2) when trying to connect`

-h localhost --port=3306 -u root -prootpass bilden die restlichen Verbindungsdaten zur Datenbank ab. Das Passwort wird ohne Leerzeichen erst einmal angehängt, zu versuchszwecken. Das weglassen des -p-Parameters würde zu einer Eingabeaufforderung führen.

mysql user sind Datenbank-Name und Tabellen-Name. Alternativ gibt es --all-databases um einen kompletten Auszug des Servers zu ziehen (aber bitte die Ausgabe in eine Datei umleiten).

Inkompatibilität zwischen Versionen

Nur als Erinnerung warum wir uns all das eigentlich antun: Manchmal müssen wir ein Backup von einem Server machen dessen Version wir nicht installiert haben. Hier ein kleines Beispiel, was passiert wenn wir einen MySQL-Server Version 8 mit einem mysqldump der Version 5.6 versuchen:

mysqldump: Got error: 2059: Authentication plugin 'caching_sha2_password' cannot be loaded: /usr/lib/mysql/plugin/caching_sha2_password.so: cannot open shared object file: No such file or directory when trying to connect

Klappt also nicht, doch wir könnten einfach mysql:8 durch mysql:5.6 ersetzen in unserem obigen Befehl. Damit wäre ein Backup wieder möglich.

Verzweifelte Frau startet Teams-Meeting auf Mac.
Aber sind wir damit wirklich glücklich?

Bash-Funktion ausarbeiten

Hier ein Beispiel wie ich das gewonnene Wissen nun verwenden kann um Backups schnell anzufertigen. Dazu legen wir die folgende Funktion in ~/.bash_aliases an:

backup-sql-tcp ()
{
    if [ "$#" -ne 5 ]; then
        echo "Backup einer Datenbank über TCP"
        echo
        echo "Verwendung:"
        echo "backup-sql-tcp [version] [db-host] [db-port] [db-user] [db-name]"
        echo
        echo "Beispiel:"
        echo "backup-sql-tcp mysql:8 localhost 3306 root mysql"
        return 1
    fi

    echo -n "Password: "
    read -s password
    echo
    BACKUP_DT=`date +%Y%m%d-%H%M%S`

    set -o pipefail

    docker run --net=host --rm -it -e MYSQL_PWD=${password} $1 \
        mysqldump --opt --no-tablespaces \
        --single-transaction --lock-tables=false \
        --protocol=TCP -h $2 --port=$3 -u $4 \
        $5 | gzip -1 > "backup-$5-$BACKUP_DT.sql.gz" \
            && echo "Backup abgeschlossen" || echo "Fehler beim Backup"

    return 0
}

Noch schnell die Funktion über source ~/.bash_aliases nachgeladen aus ausführen:

$ backup-sql-tcp backup-sql-tcp mysql:8 localhost 3306 root mysql
$ Password:
Backup abgeschlossen

$ ls
backup-mysql-20210110-161805.sql.gz

Nun können wir jede Version manuell starten und haben damit Zugriff auf die korrekte Version für den Zielserver:

backup-sql-tcp backup-sql-tcp mysql:8 ...
backup-sql-tcp backup-sql-tcp mysql:5.6 ...
backup-sql-tcp backup-sql-tcp mariadb:10 ...