Niemand will Backup, alle wollen Restore!

Wie versprochen die „rundum glücklich“-Lösung für das Backup. Ein grundlegendes Problem ist das Backup im laufenden Betrieb. Noch während das Backup läuft, verändern laufende Prozesse Dateien. Im Allgemeinen ist die /var-Partition und im Besonderen sind Datenbanken und Mailqueues problematisch. Man kann sich nie sicher sein, ob der Stand im Backup wirklich konsistent ist.

Es gibt verschiedene Ansätze dem Problem Herr zu werden. Eine Datenbank könnte man in ein SQL-File dumpen, der Mailserver könnte kurz angehalten werden um die Mailqueue zu sichern. Das sind jedoch alles nur 99%-Lösungen. Eigentlich müsste man das System für den Zeitpunkt des Backups komplett einfrieren, das Backup durchführen und es danach weiterlaufen lassen.

Snapshots implementieren diese Technik und verhindern so eine Downtime des betroffenen Services. Zunächst in Datenbanken wie z.B. Oracle populär, finden sich Snapshots jetzt auch in Dateisystemen (LVM, Windows Vista Shadow Copies). Der Snapshot selbst ist eine atomare Aktion, die lediglich den Snapshot-Zeitpunkt (t) festhält. Alle nach diesem Zeitpunkt (t+x) geschriebenen Blöcke werden separat gespeichert, so dass nun in aller Ruhe alle Blöcke des Zeitpunkt (t) ins Backup wandern können. Ist das Backup beendet, kann der Snapshot verworfen werden.


Möchte man LVM-Snapshots nutzen, benötigt man noch etwas freien Platz in einer Volume Group. Dieser freie Speicher wird dafür verwendet, Änderungen am Dateisystem nach dem Zeitpunkt (t) zu speichern. Man sollte diesen Platz ausreichend dimensionieren, denn läuft das Snapshot-Volume voll, wird der Snapshot ungültig. Eine Faustregel sagt 15 bis 20% der Größe des Originaldateisystems sind angemessen. Wenn wir also einen Snapshot von einem 10GB Volume anlegen wollen, sollte der Snapshot 2GB groß sein. Das bedeutet, es können 2GB an Änderungen in das Dateisystem geschrieben werden, bevor der Snapshot ungültig wird. Da das Backup relativ schnell durchläuft, sind wir mit der Faustregel auf der sicheren Seite.

Snapshots erstellen

Ok, genug Theorie, wir legen also von den Volumes /var, /home und /usr einen Snapshot an:

sudo lvcreate -L2G -s -n usrbackup /dev/mainvg/usr
  Logical volume "usrbackup" created
sudo lvcreate -L2G -s -n homebackup /dev/mainvg/home
  Logical volume "homebackup" created
sudo lvcreate -L5G -s -n varbackup /dev/mainvg/var
  Logical volume "varbackup" created

Zur Kontrolle schauen wir mal mit lvscan nach:

sudo lvscan
  ACTIVE   Original '/dev/mainvg/usr' [10.00 GB] inherit
  ACTIVE   Original '/dev/mainvg/var' [30.00 GB] inherit
  ACTIVE   Original '/dev/mainvg/home' [10.00 GB] inherit
  ACTIVE            '/dev/mainvg/backup' [50.00 GB] inherit
  ACTIVE   Snapshot '/dev/mainvg/usrbackup' [2.00 GB] inherit
  ACTIVE   Snapshot '/dev/mainvg/homebackup' [2.00 GB] inherit
  ACTIVE   Snapshot '/dev/mainvg/varbackup' [5.00 GB] inherit

Die Snapshots können jetzt unter z.B. /mnt/lvm-backup gemountet werden.

cd /mnt/lvm-backup
sudo mkdir var
sudo mkdir usr
sudo mkdir home
sudo mount /dev/mainvg/varbackup /mnt/lvm-backup/var/
sudo mount /dev/mainvg/usrbackup /mnt/lvm-backup/usr/
sudo mount /dev/mainvg/home /mnt/lvm-backup/home/

Jetzt können wir uns unter /mnt/lvm-backup umsehen und davon überzeugen, dass alle Dateien da sind 😉 Wenn die Snapshots nicht mehr gebraucht werden, können sie wieder gelöscht werden:

umount /mnt/lvm-backup/var
umount /mnt/lvm-backup/usr
umount /mnt/lvm-backup/home

lvremove /dev/mainvg/varbackup
lvremove /dev/mainvg/usrbackup
lvremove /dev/mainvg/homebackup

Backup mit dar

Für das eigentliche Backup ist es dann schon fast egal, welches Tool verwendet wird. Tar ist der Klassiker und sicher eine gute Wahl. Ich bevorzuge allerdings dar, weil man damit sehr einfach inkrementelle Backups erstellen kann und die Unterstützung für Wechselmedien eingebaut ist. Wechselmedien wie DVDs haben wir bei dem Rootserver zwar nicht, es gibt jedoch FTP-Server, die nur Dateien bis zu einer Größe von 2GB unterstützen. Also wird das Backup in kleinere Slices gesplittet.

Falls dar dar noch nicht auf dem Server installiert ist, kann das jetzt nachgeholt werden:

sudo aptitude install dar

Für das erstellen eines Backup-Scriptes empfehle ich wärmstens das DAR-Tutorial. Es macht wenig Sinn, die folgenden Scripte zu Copy&Pasten, wenn man sie nicht verstanden hat. Ok, Tutorial gelesen und verstanden? Fein, dann kann es ja losgehen.

Durch das Setup unseres Servers müssen wir etwas umdenken: /home, /usr und /var können mittels der LVM-Snapshot Technik gesichert werden, das Root-Dateisystem (bin, boot, etc, lib, opt, tmp) liegt jedoch nicht auf einem LVM-Volume und muss „klassisch“ gesichert werden. Wer sein Root auf einem LVM hat, ist jetzt fein raus. Da in den betroffenen Verzeichnissen eher statische Daten liegen, sollte das jedoch kein Problem sein.

Komplett-Backup der Root-Partition

Das Backup-Script dar-root-full-backup.sh für ein Full-Backup der Root-Partition sieht so aus:

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
27
28
29
#!/bin/sh
 
# target dir
DIR=/mnt/backup
 
FILE=${DIR}/`date -I`_root-backup_full
echo Backing up to $FILE
 
# start from root
cd /
 
# run dar
nice -n 19 /usr/bin/dar -m 256 -y -s 1073741824 -D -R / -c $FILE \
   -Z "*.gz"   \
   -Z "*.bz2"  \
   -Z "*.zip"  \
   -Z "*.png"  \
   -Z "*.jpg"  \
   -X "*~"     \
   -X ".*~"    \
   -X "*backup*.dar" \
   -P home \
   -P usr \
   -P var \
   -P media \
   -P mnt            \
   -P sys            \
   -P dev/pts        \
   -P proc

Dank des DAR-Tutorials ist natürlich sofort klar, dass hier in Slices zu je 1GB gesichert wird. Verzeichnisse wie /proc und die LVM-Volums /home, /usr und /var werden vom Backup ausgeschlossen (-P).

Das Backup landet dann z.B. in /mnt/backup/2007-07-14_root-backup_full.1.dar.

Differentialbackup der Root-Partition

Das Backup-Script dar-root-diff-backup.sh für ein Differentialbackup der Root-Partition sieht so aus:

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
27
28
29
30
31
32
#!/bin/sh
 
# target dir
DIR=/mnt/backup
 
FILE=${DIR}/`date -I`_root-backup_diff
PREV=`/bin/ls $DIR/*_root-backup*.dar|/usr/bin/tail -n 1|/usr/bin/awk -F '.' '{print $1;}'`
echo Backing up to $FILE
echo Previous backup is $PREV
 
# start from root
cd /
 
# run dar
nice -n 19 /usr/bin/dar -m 256 -y -s 1073741824 -D -R / -c $FILE \
   -Z "*.gz"   \
   -Z "*.bz2"  \
   -Z "*.zip"  \
   -Z "*.png"  \
   -Z "*.jpg"  \
   -X "*~"     \
   -X ".*~"    \
   -X "*backup*.dar" \
   -P home \
   -P usr \
   -P var \
   -P media \
   -P mnt            \
   -P sys            \
   -P dev/pts        \
   -P proc \
   -A $PREV

Der Einzeiler PREV=... sucht immer das jüngste Backup. Der Unterschied zum vorherigen Script ist lediglich der Parameter -A $PREV. Damit macht man DAR das vorhergehende Backup als Referenz bekannt. Jetzt werden nur noch die Unterschiede zum letzten Backup gesichert.

Vollständiges Backup der LVM-Partitionen

Das Backup-Script dar-homeusrvar-full-backup.sh für ein Full-Backup der LVM-Partitionen sieht so aus:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/bin/sh
 
# target dir
DIR=/mnt/backup
 
# target file
FILE=${DIR}/`date -I`_homeusrvar-backup_full
echo Backing up to $FILE
 
# create snapshots
echo Creating snapshots...
lvcreate -L2G -s -n usrbackup /dev/mainvg/usr
lvcreate -L2G -s -n homebackup /dev/mainvg/home
lvcreate -L5G -s -n varbackup /dev/mainvg/var
 
# mount snapshots
mount /dev/mainvg/varbackup /mnt/lvm-backup/var/
mount /dev/mainvg/usrbackup /mnt/lvm-backup/usr/
mount /dev/mainvg/home /mnt/lvm-backup/home/
 
# start /mnt/lvm-backup
cd /mnt/lvm-backup
 
# run dar
nice -n 19 /usr/bin/dar -m 256 -y -s 1073741824 -D -R /mnt/lvm-backup -c $FILE \
   -Z "*.gz"   \
   -Z "*.bz2"  \
   -Z "*.zip"  \
   -Z "*.png"  \
   -Z "*.jpg"  \
   -X "*~"     \
   -X ".*~"    \
   -X "*backup*.dar"
 
# umount snapshots
umount /mnt/lvm-backup/var     
umount /mnt/lvm-backup/usr
umount /mnt/lvm-backup/home
 
echo Removing snapshots...
lvremove -f /dev/mainvg/varbackup
lvremove -f /dev/mainvg/usrbackup
lvremove -f /dev/mainvg/homebackup

Das ist ein wenig mehr Magie. Vor dem Backup werden die Snapshots erstellt, nach dem Backup wieder weggeräumt. Da die Snapshots nach /mnt/lvm-backup gemountet werden, ist die Exclude-Liste relativ kurz.

Differentielles Backup der LVM-Partitionen

Das Backup-Script dar-homeusrvar-diff-backup.sh für ein Differentialbackup der LVM-Partitionen sieht so aus:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/bin/sh
 
# target dir
DIR=/mnt/backup
 
# target file
FILE=${DIR}/`date -I`_homeusrvar-backup_diff
PREV=`/bin/ls $DIR/*_homeusrvar-backup*.dar|/usr/bin/tail -n 1|/usr/bin/awk -F '.' '{print $1;}'`
echo Backing up to $FILE
echo Previous backup is $PREV
 
# create snapshots
echo Creating snapshots...
lvcreate -L2G -s -n usrbackup /dev/mainvg/usr
lvcreate -L2G -s -n homebackup /dev/mainvg/home
lvcreate -L5G -s -n varbackup /dev/mainvg/var
 
# mount snapshots
mount /dev/mainvg/varbackup /mnt/lvm-backup/var/
mount /dev/mainvg/usrbackup /mnt/lvm-backup/usr/
mount /dev/mainvg/home /mnt/lvm-backup/home/
 
# start from /mnt/lvm-backup
cd /mnt/lvm-backup
 
# run dar
nice -n 19 /usr/bin/dar -m 256 -y -s 1073741824 -D -R /mnt/lvm-backup -c $FILE \
   -Z "*.gz"   \
   -Z "*.bz2"  \
   -Z "*.zip"  \
   -Z "*.png"  \
   -Z "*.jpg"  \
   -X "*~"     \
   -X ".*~"    \
   -X "*backup*.dar" \
   -A $PREV
 
# umount snapshots
umount /mnt/lvm-backup/var     
umount /mnt/lvm-backup/usr
umount /mnt/lvm-backup/home
 
echo Removing snapshots...
lvremove -f /dev/mainvg/varbackup
lvremove -f /dev/mainvg/usrbackup
lvremove -f /dev/mainvg/homebackup

Ordnung halten

Die Backup-Scripte können jetzt via cron nachts aufgerufen werden. Zwei Zeilen in /etc/crontab genügen:

# backup jobs
15 2    2-31 * * root   /root/bin/diff-backup.sh
15 2    1    * * root   /root/bin/full-backup.sh

An jedem 1. des Monats läuft das Komplettbackup, an allen anderen Tagen das Differentialbackup. full-backup.sh ist lediglich ein Wrapper-Script und sieht so aus:

1
2
3
#!/bin/sh
/root/bin/dar-root-full-backup.sh
/root/bin/dar-homeusrvar-full-backup.sh

Das gleiche nochmal für’s Differentialbackup (diff-backup.sh):

1
2
3
#!/bin/sh
/root/bin/dar-root-diff-backup.sh
/root/bin/dar-homeusrvar-diff-backup.sh

Damit das Backup-Verzeichnis nicht voll läuft, müssen alte Backups gelöscht werden. Dafür legen wir unter /etc/cron.daily ein kleines Script delete_old_backups.sh an, das alle Dateien, die vor mehr als 32 Tagen erstellt wurden, löscht:

1
2
3
4
5
6
#!/bin/sh
FILES=`find /mnt/backup  -mtime +32`                                            
if [ -n "$FILES" ]; then 
  echo deleting old backups...
  rm -v $FILES
fi

Fertig?

Nicht ganz. Zu jedem Backup gehört ein Restore. Am besten testet man das gleich vom Rescue-System aus. Zuerst werden die Volumes gemountet:

/etc/init.d/lvm stop
mdrun
/etc/init.d/lvm start
mount /dev/md1 /mnt/
mount /dev/md0 /mnt/boot/
mount /dev/mainvg/usr /mnt/usr/
mount /dev/mainvg/var /mnt/var/
mount /dev/mainvg/backup /mnt/mnt/backup/
mount /dev/mainvg/home /mnt/home/

Ok, jetzt liegt das Backup unter /mnt/mnt/backup. Wir nehmen das letzte Full-Backup und spielen es nach /mnt/mnt/restore/

dar -x /mnt/mnt/backup/2007-07-14_root-backup_full.1.dar -R /mnt/mnt/restore

Das home/var/usr-Backup muss auch noch zurück gespielt werden:

dar -x /mnt/mnt/backup/2007-07-14_homeusrvar-backup_full.1.dar -R /mnt/mnt/restore

So, das sollte es gewesen sein. Hoffen wir darauf, dass nie ein Backup zurückgespielt werden muss 😉

Lektüre

Lesetipps:

Zum vorherigen Teil: FTP-Backup: Speicherplatz nutzen »
Zum ersten Teil: Debian Etch auf einem Rootserver mit Raid-1 und LVM »