Die Backup-Strategie
Wo soll das Backup gespeichert werden?
Die erste Frage die ich mir gestellt habe war: Wo soll das Backup landen? Direkt auf dem Web-Server? Dann sind die Daten verloren, sobald der ganze Server weg ist. Auf einem Cloud-Speicher? Das ist bequem, birgt allerdings auch Probleme mit den Datenschutzgesetzen. Oder auf einem privaten NAS? Das ist aus Datenschutzsicht zwar in Ordnung, aber dann muss das NAS über das Internet erreichbar sein. Das wollte ich vermeiden. Ich habe mich deshalb für einen Zwischenweg entschieden: Für den schnellen Zugriff lege ich das Backup auf dem Server ab. Gleichzeitig nutze ich das kostenlose Angebot von blaucloud.de. Das ist ein Cloud-Speicher, dessen Server in Deutschland stehen. Außerdem unterstützt blaucloud, das auf nextcloud basiert, das WebDav-Protokoll. Zusätzlich, aber das ist nur ein Bonus. Außerdem habe ich auf meinem NAS eine Synchronisierung mit der blaucloud eingerichtet. Dadurch bleibt das NAS im Internet verborgen und ich habe trotzdem eine 3. Kopie des Backups in meinen eigenen, sicheren vier Wänden.Welche Software soll ich nutzen?
Die Auswahl von Plugins für automatische Backups mit WordPress ist sehr umfangreich, einige davon sind kostenlos und die große Mehrheit ist sehr bequem zu bedienen. Die Backups laufen automatisch und auch die Wiederherstellung ist nur einen Mausklick entfernt. Das klingt paradiesisch, aber der Schein trügt. Zunächst halte ich es für absurd, ein System aus sich selber heraus zu sichern. Das ist wie ein Feuerlöscher, der bei Waldbrandgefahr zwischen den trockenen Bäumen steht. Sicher kann ich mit den zahlreichen WordPress-Plugins die Datenbank und das Dateisystem sichern. Aber der Prozess wird eben innerhalb eines System ausgeführt, das auch von außen erreichbar ist. Wenn nur ein anderes Plugin kompromittiert wird, gefährdet das die ganze Backup-Strategie. Außerdem muss ich, bei der Verwendung mehrerer WordPress-Instanzen jedes Backup-Plugin einzeln pflegen. Auch die regelmäßige Datensicherung, die viele Hoster von sich aus anbieten, reicht mir nicht aus, da diese nur minimal gesteuert werden kann und dort immer das ganze System gesichert wird, man also bei der Wiederherstellung nicht selektieren kann, welche Backup, welcher Ordner oder welche Datenbank zurück gespielt werden soll. Ich habe mich also für duplicity entschieden. Duplicity wird über die Kommandozeile bedient (deshalb der notwendige SSH-Zugang), unterstützt viele Protokolle (FTP, WebDav, Amazon S3, rsync, …) und es gibt sogar eine grafische Benutzeroberfläche – wenn man doch nicht ohne kann. Außerdem bietet duplicity die Verschlüsselung mit GnuPG an und ist damit auch bestens geeignet, um die Datensicherung in der Cloud abzulegen.Wie oft soll ich ein Backup anlegen?
Die Frage sollte mich nicht länger beschäftigen, vor allem weil ich dazu einen sehr schönen Blog-Eintrag gefunden habe. Dazu muss erklärt werden, dass duplicity mit inkrementellen Backups arbeitet. Dabei wird initial ein komplettes Backup angelegt. Danach werden nur noch die Änderungen an den zu sichernden Dateien erfasst. Für die Wiederherstellung muss also erst das letzte volle Backup zurückgespielt werden, um darauf dann die inkrementellen Backups “anzuwenden. Der Backup-Plan lautet also wie folgt:- es erfolgt initial eine volle Datensicherung
- danach gibt es jeden Tag eine inkrementelle Datensicherung
- jeden Monat erfolgt eine volle Datensicherung
- inkrementelle, tägliche Backups, die älter sind als ein Monat, werden gelöscht
- volle Backups, die älter als 12 Monate sind, werden gelöscht
Welche Daten sollen gesichert werden?
Wie bereits erwähnt, stört mich bei den meisten Hostern, dass immer ein Backup des ganzen Servers angelegt wird. Da auf einem Server gerne aber mehr als eine Domain untergebracht ist, möchte ich die Datensicherung gerne je Domain und Datenbank durchführen. Mein Ziel ist es also, jeden Ordner im Dateisystem der einer (Sub-)Domain zugeordnet ist sowie jede Datenbank getrennt zu sichern. So kann ich eine Wiederherstellung auch punktuell anstoßen.Vorbereitung
Bevor es jetzt ans Eingemachte geht, der übliche Hinweis zur gebotenen Vorsicht: Wenn du nicht weißt, was hier passiert, lass dich von jemanden unterstützen, der weiß, was hier passiert. Wer auf der Konsole arbeitet, kann sehr schnell sehr viel falsch machen.
Duplicity und Verschlüsselung einrichten
Die erste Hürde, die du nehmen musst, ist die Installation von duplicity. Entweder du bekommst das über die Konsole selber hin – oder du fragst bei deinem Hoster nach. In vielen Fällen kann auch ein Shared Hostern das für dich installieren:
1 |
sudo apt-get install duplicity |
1 |
sudo apt-get install gnupg2 |
1 |
gpg --gen-key |

Cloud-Speicher einrichten
Der kostenlose blaucloud-Account für 5 GByte-Speicher ist ziemlich schnell eingerichtet. Dazu benötigst du nur einen beliebigen Benutzernamen, eine E-Mail-Adresse und ein Passwort. Über den Benutzerbnamen wird später auch deine Cloud erreichbar sein. Ich habe eine zufällige Zeichenkette verwendet, damit die Verbindung zur Funktion als Backup-Speicher nicht auf den ersten Blick ersichtlich ist (z.B. qwertz123.blaucloud.de). Danach musst du nur noch deine E-Mail-Adresse bestätigen und schon ist der Cloud-Speicher über folgende URL verfügbar:
1 |
webdav://benutzername:passwor@benutzername.blaucloud.de/remote.php/webdav/ |
MySQL-Benutzer einrichten
Natürlich kannst du für das Backup einfach den Benutzer nutzen, den du auch für administrative Zwecke nutzt. Ich verrate dir aber kein Geheimnis wenn ich dir sage, dass es sehr sinnvoll ist, dafür einen eigenen Benutzer anzulegen, insofern deine Hosting-Umgebung das zulässt. Dazu führst du auf einer beliebigen Oberfläche (phpMyAdmin, MySQL Workbench oder direkt über das MySQL-CLI) folgende Query aus. Hier setzt du nur einen Benutzernamen und ein Passwort ein.
1 2 |
GRANT LOCK TABLES, SELECT ON *.* TO 'USERNAME'@'localhost' IDENTIFIED BY 'PASSWORD'; GRANT SHOW VIEW ON *.* TO 'USERNAME'@'localhost' |
Die Backup-Strategie umsetzen
Die Zugangsdaten ablegen
Die Informationen, die wir oben gesammelt haben, werden erstmal in der Datei backup.conf abgelegt. Bitte beachte, dass die Id für den öffentlichen Schlüssel nur ein Verweis ist. Die tatsächlichen Schlüssel liegen im Benutzerordner unter ~/.gnugpg/.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# mit diesem Wert steuerst du die Ausgabe von duplicity # je höher, desto mehr Debug-Nachrichten werden ausgegeben # das hilft bei der Fehlersuche export DUPLICITY_VERBOSITY=2 # die Passphrase ist das Passwort für den privaten Schlüssel # die Id für den öffentlichen Schlüssel wird utner GPG_PUP_KEY abgelegt export PASSPHRASE=secret_gpgp_key_password export GPG_PUB_KEY=public_gpgp_key_id # dieser Ordner wird für die lokalen Backups verwendet export BASE_PATH_BACKUP=/private-backup/ # dieser Ordner enthält die temporären MySQL-Dumps sowie den Cache von duplicity export BASE_PATH_TEMP=/private-backup/temp/ export LOG_FILE=backup.log # das sind die Zugangsdaten zu deinem WebDav-Anbieter export WEBDAV_USER=webdav_user export WEBDAV_PASSWORD=webdav_password export WEBDAV_URL=somewhere.server.de/webdav.php/folder/backup # schließlich hinterlegst du noch eine E-Mail-Adresse # an die Fehlernachrichten geschickt werden export SUPERVISOR_EMAIL=error_messages@foobar.com # und die E-Mail-Adresse des Absenders export LOCAL_EMAIL=sender@foobar.com |
1 2 3 4 |
[client] user=mysql_user password=mysql_password host=localhost |
Die Ordner der virtuellen Hosts sichern
Im Folgenden werde ich die Shell-Scripte und den Prozess ganz kurz erklären. Zunächst will ich zwei Server-System unterstützen: nginx und apache2. Dazu frage ich den ersten Parameter ab:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
if [[ $1 == 'apache' ]] then SERVER_SOFTWARE='apache' elif [[ $1 == 'nginx' ]] then SERVER_SOFTWARE='nginx' else echo 'Keine Server-Architektur angegeben, probiere mal nginx oder apache' fi |
1 |
/usr/sbin/apache2ctl -S |

1 |
/usr/sbin/apache2ctl -S | grep "port 80 namevhost" | awk -F ' ' '{ print $5 }' | sed -E 's/[:()]//g' | sed -E 's/[ 0-9]$//g' |
1 |
grep -oE 'DocumentRoot \"(.*)\"' $configFile | awk -F ' ' '{ print $2 }' | sed -E 's/["]//g' |
1 2 3 4 5 6 7 8 9 10 11 |
if [[ $SERVER_SOFTWARE == 'apache' ]] then configFilesString=$(/usr/sbin/apache2ctl -S | grep "port 80 namevhost" | awk -F ' ' '{ print $5 }' | sed -E 's/[:()]//g' | sed -E 's/[ 0-9]$//g') configFiles=($(echo "$configFilesString" | tr ',' ' ')) elif [[ $SERVER_SOFTWARE == 'nginx' ]] then configFiles=(/etc/nginx/conf.d/*.conf) fi |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
if [[ $SERVER_SOFTWARE == 'apache' ]] then srcFolder=$(grep -oE 'DocumentRoot \"(.*)\"' $configFile | awk -F ' ' '{ print $2 }' | sed -E 's/["]//g') dstFolder=$(basename $srcFolder) elif [[ $SERVER_SOFTWARE == 'nginx' ]] then srcFolder=$(grep -oE 'root (.*);' $configFile | awk -F ' ' '{ print $2 }' | sed -E 's/[;]//g') dstFolder=$(basename ${srcFolder%htdocs}) fi |
Die Datenbanken des MySQL-Servers sichern
Nun geht es an die Datenbanken. Dazu nutze ich den CLI-MySQL-Client und frage erstmal einfach alle Datenbanken ab:
1 |
mysql --defaults-extra-file=database.conf -Bse 'show databases' |
Warning: Using a password on the command line interface can be insecure.Da die Ausgabe von mysql nur einen String zurückgibt, muss ich den erst in ein Array umwandeln, dass ich dann durch-loopen kann. Dazu gibt es zwei Wege – den unteren finde ich etwas eleganter. Entscheide dich einfach für einen:
1 2 |
databasesArray=($(echo "$databasesString" | tr ',' ' ')) IFS='_' read -r -a databasesArray<<< "$databasesString" |
1 2 3 4 5 6 7 8 9 10 |
ignoreDatabases=['information_schema,sys,performance_schema'] for database in "${databasesArray[@]}" do if [[ ! " ${ignoreDatabases[*]} " == *"${database}"* ]] then ... fi done |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
result="$( ( mysqldump --defaults-extra-file=database.conf ${database} > ${BASE_PATH_TEMP}${database}.sql ) 2>&1 )" len=${#result} if [ "$len" -gt "0" ] then echo $result | mail -s "Error when dumping mysql database ${database}" $SUPERVISOR_EMAIL -r $LOCAL_EMAIL fi ./backupFilesystem.sh -f ${BASE_PATH_TEMP}${database}.sql -d "${BASE_PATH_BACKUP}databases/${database}" rm ${BASE_PATH_TEMP}${database}.sql |
Die Daten mit duplicity verschlüsselt sichern
Nun geht es ans Eingemachte bzw. die tatsächliche Datensicherung. Das Script backupFilesystem.sh erwartet zwei Parameter: Die Dateien bzw. der Ordner, der gesichert werden soll und der Name des Zielordners. Danach wird duplicity mehr als ein mal aufgerufen. Zunächst wird, wenn das letzte Vollbackup älter ist als 1 Monat, ein komplettes Backup angelegt. Danach wird duplicity angewiesen, Backups, die älter sind als 12 Monate, zu entfernen. Dann werden außerdem die inkrementellen Backups entfernt, die älter sind als 1 Monat. Das ganze wird einmal aufgerufen um die Backups lokal abzulegen und dann ein 2. Mal für den WebDav-Speicher. Wer Ordner und Dateien manuell sichern will, kann also dieses Script mit den entsprechenden Parametern aufrufen.Automatisieren mit cron
Jetzt muss der ganze Spaß natürlich noch automatisch laufen. Dazu werden die folgende Zeilen in die Datei /etc/cron.d/dailyBackup gepackt:
1 2 3 |
# m h dom mon dow user command 0 1 * * * root /bin/bash /backup/backupVirtualhosts.sh 0 2 * * * root /bin/bash /backup/backupDatabase.sh |
1 |
0 1 * * * root /bin/bash /backup/backupFilesystem.sh -f /home/ -d /private-backup/home/ |