Home   Profil   Fun
#35 Linux  03.06.2007

Volles Backup eines Webservers mit MySQL-Datenbanken



Ich habe dieses Tutorial ausführlich mit meinem eigenen Debian-System getestet. Trotzdem bitte die Augen offenhalten und die einzelnen Schritte gegebenenfalls an das eigene System anpassen. Vielen Dank.

Backup
Wiederherstellung
Tips
Eine einzelne Datenbank aus einem vollen MySQL-Dump wiederherstellen


Backup
Es gibt viele Möglichkeiten ein volles Backup eines Webserver mit allen MySQL-Datenbanken zu erstellen. Solange die Gesamtmenge der Daten nicht zu groß ist kann man dies auf relativ einfache Weise mit einem stage4 tar-Archiv erreichen. Dies bedeutet, wir erstellen einfach ein Archiv aller Dateien des gesamten Linux-Systems. Zusätzlich sichern wir den MBR (Master Boot Record) und die Partitionstabelle. Im Falle eines Plattencrashs ist es so möglich das gesamte Linux-System wiederherzustellen, indem man einfach die Festplattenstruktur anlegt und das stage4 tar-Archiv entpackt. Natürlich kann man auch einzelne Dateien aus dem Archiv wiederherstellen, falls dies benötigt wird. Weiterhin ist es wichtig dass die MySQL-Datenbanken in einem konsistenten Zustand gesichert werden. Und zuletzt der wichtigste und meist vernachlässigte Schritt: Das Überprüfen und Testen des Backups, sodass es auch wirklich zur Wiederherstellung verwendet werden kann. Der Vorteil bei dieser Vorgehensweise ist, dass das Gesamtvolumen der Daten relativ klein ist, da nur die eigentlichen Daten, nicht aber die Partitionen als ganzes gebackupt werden.

In diesem Linux-Tutorial verwende ich ein einfaches Szenario mit einer einzigen Festplatte, die zwei Partitionen enthält: sda1 gemountet nach / und sda2 gemountet nach /boot. Es gibt keine LVMs (logical volumes) oder andere Arten von Virtualisierung. Auch wird das Sichern von SELinux-Informationen in diesem Artikel nicht beschrieben.


Der erste Schritt ist das Sichern des Festplattenlayouts. Die folgenden drei Schritte produzieren sehr kleine Dateien. Diese können erstmal auf dem zu sichernden System abgelegt werden und dann auf das Backupsystem heruntergeladen werden. Wir beginnen mit dem Backup des Master Boot Records.
dd if=/dev/sda of=/backup_sda_mbr count=1 bs=512

Nachdem wir die Datei backup_sda_mbr heruntergeladen haben geht es weiter. Um sicherzustellen, dass es beim Download nicht zu Veränderungen gekommen ist und die Datei auf dem Backupsystem tatsächlich identisch ist, vergleichen wir sie mit md5sum. Beide Ergebnisse müssen exakt übereinstimmen. Auf dem Originalsystem:
dd if=/dev/sda count=1 bs=512 | md5sum
Auf dem Backup-System:
md5sum backup_sda_mbr

Es geht weiter mit dem Backup der Partitionstabelle.
sfdisk -d /dev/sda > /backup_sda_pt.sfdisk

Hierbei wird lediglich eine kleine Textdatei erstellt. Am besten diese kurz öffnen und prüfen ob sie gültige Daten enthält. Der MBR und die Partitionstabelle sind wichtige Daten. Fehlen beide, ist ein Zugriff auf das bestehende Dateisystem der Festplatte in der Regel nur noch mit hohem Aufwand möglich.

Bei einem Wiederherstellen des Gesamtsystems müssen die symbolischen Links in / manuall erstellt werden. Daher ist es sinnvoll eine entsprechende Übersicht mit wegzusichern.
ls -lha / > /symlinks.txt

Auch sollte man sich abspeichern, welche Dateisysteme auf den Partitionen verwendet werden. Hier im Beispiel ist es ext3.
df -hT
Filesystem    Type    Size  Used Avail Use% Mounted on
/dev/sda1     ext3    7.4G  789M  6.2G  12% /
...
/dev/sda2     ext3     92M   66M   21M  77% /boot
...

Die letzten beiden Dateien auch noch auf das Backup-System kopieren und anschließend vom Server entfernen.



Jetzt haben wir schonmal die Plattenstruktur gesichert. Im Falle eines Hardwarefehlers kann der MBR und die Partitionstabelle in wenigen Sekunden wieder hergestellt werden. Als nächstes werden die eigentlichen Daten gebackupt. Dies beginnt mit einem MySQL-Dump aller Datenbanken. Zusätzlich werden alle Binärdateien von MySQL im Backup enthalten sein. Sollte es aus irgendwelchen Gründen Probleme mit dem Import des Dumps geben (z.B. wegen der Zeichensätzen) kann man die MySQL-Datenbanken einfach mit den Binärdateien wiederherstellen. Tatsächlich tun wir dies in diesem Tutorial, da das gesamte Dateisystem wiederhergestellt wird. Als erstes verschaffen wir uns aber einen Überblick über die Größe der Partitionen und deren Dateisysteme.
df -h

Wir werden hier unkomprimierte tar-Archive erstellen. Der Grund dafür ist, dass schon ein einzelner Fehler im Archiv zum Verlust aller Daten führen kann. Wohingegen dies bei unkomprimierten Archiven eher nur zum Verlust der Datei führt wo der Fehler aufgetreten ist.

Es geht weiter mit dem MySQL-Dump aller Datenbanken. Jedoch können wir nicht einfach so einen Dump erstellen, denn die Daten in den Datenbanken könnten sich während des Dumpens ändern. Dies würde dann zu Inkosistenzen im Backup führen. Deshalb loggen wir uns erstmal in MySQL ein und flushen die Tabellen mit einem read lock. Dies bedeutet, dass keine Daten mehr geändert, sondern nur noch gelesen werden können. Dieses Lock ist global über alle Datenbanken.
mysql> flush tables with read lock;

Es ist wichtig diese Session offen zu halten, da beim Beenden der Session das Lock sofort entfernt wird. Deshalb schieben wir den Prozess einfach in den Hintergrund.
[Press ctrl-z]

Im nächsten Schritt erstellen wir nun das Dump der MySQL-Datenbanken. mysqldump könnte hier ohne weitere Optionen verwendet werden, da ja das globale read lock bereits in der MySQL-Shell gesetzt wurde. Aber es ist auch möglich ein globales read lock nur für die Dauer des Dumps zu setzen: mysqldump benutzt --opt per default. Dieses lockt aber nur für jede Datenbank individuell, sodass es immer noch zu Inkosistenzen zwischen Datenbanken untereinander kommen kann. Für ein globales Lock über alle Datenbanken muss --lock-all-tables angegeben werden.
mysqldump --opt --lock-all-tables --all-databases -u root -p > /root/all-databases_yyyymmdd.sql



Ok, soweit so gut. Wir haben nun das Festplattenlayout und alle Datenbank als Dump gesichert. Der letzte Schritt ist Backups der beiden Partitionen zu erstellen. Wie schon erwähnt ist das Ergebnis unkomprimiert um auf der sicheren Seite zu sein.
Alle Ordner in / sichern außer /dev, /proc, /sys (und /selinux).
cd /
tar cfvp /fullbackup-sda1-yyyymmdd.tar /bin /etc /home /initrd /lib /media /mnt /opt /root /sbin /srv /tmp /usr /var
Und schließlich noch den Ordner /boot von der zweiten Partition (sda2) sichern.
cd /boot
tar cfvp /fullbackup-sda2-yyyymmdd.tar *

Falls auf dem System nicht mehr genug freier Speicherplatz vorhanden ist, kann man die Archive auch direkt über SSH auf dem Backup-System erstellen. Allerdings ist dann keine Überprüfung mehr möglich, ob die Daten auch wirklich korrekt über das Netzwerk übertragen wurden.
cd /
tar cfvp - /bin /etc /home /initrd /lib /media /mnt /opt /root /sbin /srv /tmp /usr /var | ssh  user@backupsystem "cat > /backups/fullbackup-sda1-yyyymmdd.tar"
Und entsprechend der Ordner /boot von der zweiten Partition (sda2).
cd /boot
tar cfvp - * | ssh user@backupsystem "cat > /backups/fullbackup-sda2-yyyymmdd.tar"

Die MySQL-Datenbanken haben immer noch das globale read lock gesetzt. Dies hat dazu geführt, dass die die Binärdateien von MySQL ebenfalls in konsistentem Zustand im Backup gelandet sind. Jetzt kann das Lock wieder entfernt werden. Das tun wir indem wir erstmal den entsprechenden Prozess wieder in den Vordergrund bringen.
fg
[Hit return]
mysql> unlock tables;
mysql> quit



Wenn man das ganze nun z.B. auf DVD brennen möchte, geht das per Kommandozeile wie folgt.
growisofs -Z /dev/cdrom -R -J /backups/

oder nur eine einzelne Datei
growisofs -Z /dev/cdrom -R -J /backups/fullbackup-sda1-yyyymmdd.tar



Um sicherzugehen, dass die Daten auf dem Backupmedium die gleichen sind wie auf dem Server, lassen wir wiederum md5sum auf beiden Seiten laufen. Das Ergenbis muss exakt übereinstimmen. (Wie bereits erwähnt geht das natürlich nur sofern man die Archive nicht direkt übers Netz erstellt hat)
Server:
md5sum fullbackup-sda1-yyyymmdd.tar fullbackup-sda2-yyyymmdd.tar

Backupmedium:
md5sum fullbackup-sda1-yyyymmdd.tar fullbackup-sda2-yyyymmdd.tar

Als allerletzten Check kann man noch jeweils eine einzelne Textdatei extrahieren und in einem Texteditor öffnen.
tar xfvp fullbackup-sda1-yyyymmdd.tar var/www/domain/htdocs/index.html
tar xfvp fullbackup-sda2-yyyymmdd.tar grub/menu.lst

Schließlich sollten das MySQL-Dump und die Backupdateien auf dem Server wieder entfernt werden.
rm /root/all-databases_yyyymmdd.sql
rm /fullbackup-sda1-yyyymmdd.tar
rm /fullbackup-sda2-yyyymmdd.tar


Werden andere Zeichensätze als UTF-8 in MySQL verwendet macht es Sinn wenigstens einmal einen Testimport des Dumps zu machen. Es kann manchmal leider zu Problemen mit dem Zeichensatz kommen. Dies führt dann dazu, dass sich im Extremfall das Dump nicht mehr importieren lässt oder zu fehlerhaften Daten führt. Falls möglich am besten auf dem Originalsystem einmal in eine identische Testdatenbank importieren. Alternativ kann man es auch lokal mit exakt gleichkonfiguriertem MySQL testen.

Fertig. Wir haben nun ein volles, verifiziertes Backup des Webserver mit allen MySQL-Datenbanken.



Wiederherstellung
Um das ganze System z.B. nach einem Festplattencrash wiederherzustellen sind folgende Schritte notwendig: Booten über ein Rescuesystem oder LiveCD, die Festplattenstruktur wieder herstellen, die Partitionen formatieren und die Archive extrahieren. Ich gehe hier mal vom worst case aus und nehme an, dass man sich mit einer neuen völlig leeren Festplatte wiederfindet. MySQL und alle Datenbanken werden in diesem Vorgang automatisch mitwiederhergestellt. Dies geschieht dadurch dass die MySQL-Binärdateien aus dem stage4 tar-Archiv verwendet werden. Nur um es hier auch zu erwähnen: Wenn man den Dump importieren möchte nimmt man das folgende Kommando. Der Mysql Dämon muss laufen.
mysql -u root -p < /root/all-databases_yyyymmdd.sql

Im ersten Schritt loggt man sich auf dem Rescuesystem ein und setzt Datum und Zeit auf die aktuellen Werte.
date MMDDhhmm[YYYY]

Dann kopiert man backup_sda_mbr und backup_sda_pt.sfdisk vom Backupserver nach / auf dem ursprünglichen System.

Damit wird der Master Boot Record wiederhergestellt
cd /
dd if=/backup_sda_mbr of=/dev/sda count=1 bs=512
und die Partitionstabelle.
sfdisk /dev/sda < /backup_sda_pt.sfdisk

sfdisk beschwert sich eventuell dabei, dass die Festplatte gerade verwendet wird. Wenn dies auftritt kann man nach der Partition suchen, die von der LiveCD zum Swappen benutzt wird.
dmesg | grep swap

Anschließend deaktiviert man das Swappen dort und versucht nocheinmal die Partitionstabelle zu schreiben.
swapoff /dev/sdaX

Formatieren beider Partitionen mit den ursprünglich verwendeteten Dateisystemen. In diesem Beispiel war es ext3.
mke2fs -j /dev/sda1
mke2fs -j /dev/sda2

Erstellen und Aktivieren des Swapbereiches auf der Swap-Partition.
mkswap /dev/sda3
swapon /dev/sda3



Inzwischen haben wir die Festplattenstruktur wiederhergestellt und fahren mit dem Mounten der Dateisysteme fort.
mount /dev/sda1 /mnt/sda
mkdir /mnt/sda/boot
mount /dev/sda2 /mnt/sda/boot

Ein wichtiger Hinweis an dieser Stelle: Bevor man die Archive auf dem Originalsystem extrahiert sollte man /etc/passwd aus fullbackup-sda1-yyyymmdd.tar temporär an einem sicheren Ort entpacken.
tar xfvp fullbackup-sda1-yyyymmdd.tar etc/passwd
Die darin enthaltenen UIDs und GUIDs sollten zumindest was die wichtigsten Benutzer angeht mit denen auf dem Rescuesystem übereinstimmen. Falls notwendig kann man diese auf dem Rescuesystem einfach anpassen. Sollte man das unterlassen kann es passieren, dass sich bestimmte Dämonen nicht starten lassen. Man muss dann später manuell die Besitzer und Gruppen korrigieren, was aufwendig und fehlerträchtig sein kann.

Kopieren des Backups von sda1 vom Backup-System und Auspacken in /mnt/sda1.
cd /mnt/sda; ssh user@backupsystem "cat /backups/fullbackup-sda1-yyyymmdd.tar" | tar xfvp -

Fehlende Ordner erstellen
mkdir proc
mkdir dev
mkdir sys
(mkdir selinux)

Kopieren des Backups von sda2 vom Backup-System und Auspacken in /mnt/sda1/boot.
cd /mnt/sda/boot; ssh user@backupsystem "cat /backups/fullbackup-sda2-yyyymmdd.tar" | tar xfvp -

Jetzt noch die symbolischen Links in / gemäß der Übersicht in Datei symlinks.txt erstellen.

Ok, fertig. Nach dem Unmounten der Partitionen kann das wiederhergestellte System gebootet werden.
Beim Hochfahren sollte man nach Fehlern Auschau halten.
cd /
umount /mnt/sda/boot
umount /mnt/sda
reboot



Tips
Abschließend noch ein paar ergänzende Tips.

Mit partprobe (im Packet parted enthalten) kann man die Partitionstabelle vom Kernel neu einlesen lassen ohne den ganzen Server neu booten zu müssen.
partprobe /dev/sda

Für den Fall, dass Grub Probleme macht, sei es dass er das Menu nicht anzeigt oder Dateien nicht findet obwohl alle Pfade korrekt sind und ähnliches kann man Grub einfach neu in den MBR installieren. Dies geschieht durch folgende Schritte nachdem man erneut die LiveCD bzw. das Rescuesystem gebootet hat.
mount /dev/sda1 /mnt/sda1
mount /dev/sda2 /mnt/sda1/boot
mount -t proc none /mnt/sda1/proc
mount -o bind /dev /mnt/sda1/dev
chroot /mnt/sda1 /bin/bash
source /etc/profile
grep -v rootfs /proc/mounts > /etc/mtab
/usr/sbin/grub-install /dev/sda
exit
cd /
umount /mnt/sda1/dev
umount /mnt/sda1/proc
umount /mnt/sda1/boot
umount /mnt/sda1
reboot
Danach sollte das Booten über Grub wieder möglich sein.

Für große Systeme oder ganze Netzwerke ist
Bacula das Mittel der Wahl.
rsync ist eine weitere Möglichkeit Backups zu erstellen.
Oder auch das ganze über Snapshots machen wenn LVMs verwendet.



Eine einzelne Datenbank aus einem vollen MySQL-Dump wiederherstellen
Erstelle eine Kopie des vollen Dumps.
Bearbeite die Kopie mit vi.
Suche nach dem CREATE DATABASE statement, indem du folgendes in vi eingibst:
/^CREATE DATABASE
Mit 'n' solange suchen, bis du beim richtigen Statement angekommen bist.
Danach noch einmal, sodass du bei dem direkt folgenden Statement bist.
Ab hier alle Zeilen bis kurz vor dem Dateiende entfernen: Und zwar nur soweit, bis zu den Zeilen, wo die alten Werte der Variablen gesetzt werden.
Und danach auch alle Zeilen ab dem allerersten CREATE DATABASE statement bis zum richtigen entfernen, solange es nicht bereits das erste ist.
Jetzt ist nur noch der Header, die Variablen am Anfang und Ende der Datei und die Statements der wiederherzustellenden Datenbank enhalten. Abspeichern und importieren:
mysql -u root -p < ./modified_dump.sql