
Effiziente Backups für kleine WordPress-Seiten
Im Agenturalltag kommt man oft in die Situation, eine kleine WordPress-Seite eines Kunden aktualisieren zu müssen. Dann steht stets ein kleines Banner auf der Admin-Seite:

Schon setzt das schlechte Gewissen ein: „Habe ich funktionierende Backups?“, „Kann ich den Stand vor der Aktualisierung wieder herstellen?“.
Backup-Methoden
Man klickt den Knopf deutlich beruhigter, wenn man sich seiner Backupstrategie sicher sein kann. Es gibt mehrere Möglichkeiten, von denen gelegentlich sogar die „ich verlasse mich auf den Provider“-Methode ausreichend sein mag.
Will man die Kontrolle selbst behalten, muss man diverse Punkte gegeneinander abwiegen. Bei lange laufenden und intensiv genutzten Blogs kann sich die Größe des Docroots schon mal im Dutzende Gigabyte-Bereich befinden. Dann wird man andere Lösungen brauchen als die, die wir hier vorstellen, z.B. indem man Features eines Snapshot-fähigen Dateisystems nutzt.
Ist die Datenbank hochfrequentiert, muss man auch hier beim Dump spezielle
Vorkehrungen treffen, indem man z.B. nicht SQL-Dateien exportiert, sondern mit
Tools wie mariadb-backup die tatsächlichen DB-Dateien kopiert.
Aber es gibt eine große Zahl an kleineren und mittleren WordPress-Blogs, für die das alles übertrieben wäre, bei denen man aber vielleicht doch ein wenig mehr machen möchte, als der Provider anbietet. Für diese Situationen ist die folgende Methode geeignet.
Git-basiertes Backup
Wir verwenden das Versionsverwaltungs-Werkzeug Git, um ein Abbild des aktuellen Docroots zu speichern. Das erlaubt, zu einem beliebigen Zeitpunkt in der Vergangenheit zurückzuspringen.
Es wäre allerdings eine schlechte Idee, das Git-Repository als .git-Ordner
direkt im Docroot zu haben. Das würde im schlimmsten Fall dem geneigten
Angreifer Zugriff auf sensible Daten wie die Datenbank-Zugriffswerte in der
wp-config.php geben.
Deshalb machen wir einen kleinen Umweg mit Umgebungsvariablen.
Git ist mit einer Vielzahl davon konfigurierbar. Diese Einstellungen können
auch in einer zentralen $HOME/.gitconfig oder beim Aufruf über
Kommandozeilen-Parameter mitgegeben werden.
Der Vorteil von Umgebungsvariablen ist, dass sie nicht immer verwendet werden (wie bei Werten in Config-Dateien), gleichzeitig aber auch nicht bei jedem Aufruf mitgegeben werden müssen (wie bei Kommandozeilen-Parametern).
Wir benötigen für unsere Strategie sechs Parameter:
GIT_DIR- Diese Variable zeigt auf das Git-Repository (den
name.git-Ordner) GIT_WORK_TREE- Hiermit kann man das Arbeitsverzeichnis der ausgecheckten Dateien bestimmen
GIT_AUTHOR_NAME,GIT_AUTHOR_EMAIL,GIT_COMMITTER_NAMEundGIT_COMMITTER_EMAIL- Die Variablen bestimmen den Namen und die Mailadresse des Änderungs- und Commit-Autors
Zuerst legen wir für unsere Backup-Strategie ein leeres Git-Repository an. Wir
lassen es von Git „bare“ konfigurieren, d.h., wir sagen Git, dass es
eigentlich kein Arbeitsverzeichnis gibt.
export GIT_DIR="/backups/webseite.git"
test -d "$GIT_DIR" || git init --bare
Wir müssen git init keinen Ordnernamen mitgeben. Es holt sich die Information
aus der $GIT_DIR-Variable.
Diesen Schritt müssen wir nur ein Mal machen, er lässt sich aber für 0-Installation-Scripte auch zu Beginn des Backup-Scripts ausführen.
Anschließend können wir das Backup laufen lassen, indem wir das Webseiten-Docroot als Arbeitsverzeichnis und unser neu angelegtes Repository als Git-Ordner zuweisen. Den Namen des Committers wählen wir so, dass klar ist, wer hier gerade Änderungen einpflegt. Wir empfehlen, eine tatsächliche E-Mail-Adresse zu verwenden. So kann man im Bedarfsfall schnell die richtige Person anschreiben.
export GIT_DIR="/backups/webseite.git"
export GIT_WORK_TREE="/var/www/html"
export GIT_AUTHOR_NAME="Backup-Script"
export GIT_AUTHOR_EMAIL="support@example.com"
export GIT_COMMITTER_NAME="Backup-Script"
export GIT_COMMITTER_EMAIL="support@example.com"
# wechsle ins Docroot, sonst klappt "git add" nicht.
cd "$GIT_WORK_TREE"
# füge alle Änderungen dem Repository hinzu
git add --all .
# committe die Änderungen falls notwendig
git diff --staged --quiet || git commit --message="automatic backup"
Fertig. Der aktuelle Stand des Docroots ist jetzt für alle Zeiten im Git-Repository verewigt. Git legt außerdem einen Zeitstempel für den Commit an, sodass wir stets nachvollziehen können, von wann das Backup ist.
Was macht die Zeile mit dem git diff eigentlich hier? Das ist ein kleiner
Umweg, der verhindert, dass wir leere Commits anlegen (bzw. dass Git sich über
potentiell leere Commits beschwert). Mit git diff --staged schauen wir, ob
sich durch git add überhaupt etwas geändert hat. Dank --quiet gibt das
Kommando nichts aus, sondern kommuniziert sein Ergebnis durch den Rückgabewert:
0 (Erfolg), falls es keine Änderungen gab, 1 (Fehler) bei Änderungen.
Was ist mit der Datenbank?
Hier gibt es mehrere Möglichkeiten: Man kann sie ebenfalls versionsverwalten, indem man einen SQL-Dump anlegt und an geeigneter Stelle Git in den Index unterjubelt. (Eine Möglichkeit wäre, das Docroot erst in einen temporären Ordner zu kopieren und dann dort die SQL-Datei einfach mit abzulegen, bevor man den Commit mit diesem Ordner macht.)
Es ist auch möglich, die Datenbankbackups separat zu verwalten. Das kann man mit wenigen Zeilen Code im Backupscript mit einbauen:
#!/bin/bash
set -euo pipefail
# Backup-Ordner anlegen, falls er nicht existiert
mkdir -p /backups/db
# Backup anlegen und auch gleich gzippen
export MYSQL_PWD="$DATENBANK_PASSWORT"
mysqldump \
--host="$DATENBANK_HOST" \
--user="$DATENBANK_NUTZER" \
--no-create-db \
--default-character-set=utf8mb4 \
"$DATENBANK_NAME" \
| \
gzip -9 >"/backups/db/$(date +'%Y-%m-%d-%H%M%S').sql.gz"
# Backups löschen, die älter als 31 Tage sind
find /backups/db -type f -name "*.gz" -mtime +31 -exec rm -f "{}" \;
Hinweis: Nutzer von WP-CLI haben es noch einfacher, an einen Dump zu kommen, denn da ist ein entsprechendes Kommando bereits eingebaut. Wir empfehlen Ihnen, sich dieses WordPress-Werkzeug definitiv näher anzusehen, wenn Sie es noch nicht kennen sollten.
Vorteile und Beschränkungen dieser Variante
Es ist wichtig, sich daran zu erinnern, dass Git-Commits nicht umsonst sind. Jede verbuchte Änderung kostet Plattenplatz. Werden im Blog viele Bilder hochgeladen (und von WordPress dann auch gleich hilfreich in ein halbes Dutzend Thumbnails konvertiert), kann die Größe des Git-Repositories schnell wachsen.
Darüber hinaus ist das Backup nicht atomar. Das soll heißen, dass es durchaus Race Conditions geben kann, wo das Backup mitten während eines Updates läuft und damit halb alte, halb neue Dateien aufzeichnet. Der entsprechende Backupstand wäre dann unbenutzbar. Der Vorteil eines Git-Repositorys ist allerdings, dass man in diesem Fall einfach einen Commit weiter in die Vergangenheit springen kann, um die meisten Teile wieder herzustellen.
Ein wesentlicher, in Git eingebauter Vorteil ist die einfache Möglichkeit, das Backup vom Server weg auf eine andere, sichere Instanz zu pushen. Wenn man dort ein Repository remote verfügbar macht, reicht eine Zeile im Backup-Script für Offsite-Backups:
git push origin
Schon ist die Seite vor einer Vielzahl Kalamitäten gefeit, vom Servercrash bis zu Verschlüsselungstrojanern.
Foto von Julio Lopez auf Unsplash
