Automatisierte inkrementelle Backups unter Linux

Nach einem Festplattencrash oder anderen Gründen für Datenverlust ist man froh, auf ein aktuelles Backup zurückgreifen zu können. Als mir im Februar aber meine SSD-Karte nach einem Kurzschluss abgeraucht ist, war das letzte Backup allerdings schon mehrere Wochen alt und der Datenverlust daher schmerzlich. Dass die Daten so selten gesichert wurden lag nur daran, dass es mir einfach zu unbequem war, die externe Festplatte regelmäßig anzuschließen, das Kopieren anzustoßen und dann den Laptop nicht ausschalten zu dürfen.

Um sich die lange Wartezeit zu ersparen und Festplattenplatz zu sparen eignen sich inkrementelle Backups, d.h. es werden nur die Dateien kopiert, die seit der letzten Sicherung verändert wurden. Natürlich gibt es Software, die das automatisch erledigt, aber ich hatte auf die Schnelle keine gefunden, die meinen Bedürfnisse zufriedenstellend und unkompliziert erledigt.

Naja, nach dem Crash musste also eine sinnvolle Lösung erstellt werden, die ich nun präsentiere. Die Methode baut auf diesem Artikel auf, wurde aber noch etwas erweitert.

Für das Backup nutze ich eine externe 500-GB-Platte, die in zwei Partitionen geteilt ist. Auf der ersten Partition (20 GB) wird ein Live-Linux für Notfälle installiert. Das eigentliche Backup liegt auf der zweiten Partition.

Als Backup-System wird einfach das Dateisystem (in meinem Fall ext4) genutzt, sodass keine spezielle Software nötig ist, um Dateien wiederherzustellen. Ein Eintrag in der fstab sorgt dafür, dass die Partition ohne root-Berechtigung (schließlich ging es mir in erster Linie um Bequemlichkeit und da will ich auch kein Passwort eingeben müssen) gemountet werden kann.

UUID=5d4d9308-bca6-4a65-ab62-d1b218508fc5       /backup ext4    defaults,user,noauto   0       0

Die benötigte ID der Partition liefern blkid. Beim Einstecken der Platte wird automatisch das Skript ~/bin/backup.sh aufgerufen, dass die eigentliche Arbeit macht (siehe weiter unten). Für den automatischen Aufruf nutze ich udev mit einer Regeldatei /etc/udev/rules.d/80-backup-erstellen.rules mit dem Inhalt

ATTRS{serial}=="57584E583038534534373331", SYMLINK+="backup-drive", RUN+="/usr/bin/sudo -u henrik /home/henrik/bin/backup.sh"
Die benötigte Seriennummer liefert `sudo udevadm info -a -n /dev/sdb2 | grep serial`.

In einer Datei ~/.backup.conf liste ich Zeile für Zeile alle zu sichernden Verzeichnisse auf, denn ich wollte nicht das komplette Home-Verzeichnis inklusive .thumbnails (Vorschaubilder) oder der lokalen Kopie sämtlicher Mails kopieren.

Das Skript bindet die Platte ein und sorgt für ein inkrementelles Backup, wobei jede Version in einem eigenen Verzeichnis liegt. backup.0 ist die aktuellste Version, backup.1 die zweitaktuellste usw.. Um Zeit und Platz zu sparen, liegt bei Dateien, die sich von einer Version zur nächsten nicht geändert haben, aber dieselbe (nicht nur eine Kopie) Datei in beiden Verzeichnissen, d.h. es wird nur einmal Platz auf der Platte verwendet. Dazu werden Hardlinks verwendet.

backup.sh sieht folgendermaßen aus:

#!/bin/bash

# UDEV triggert mehrfach hintereinander. Die Datensicherung aber nur einmal (minimale Wartezeit 10 Minuten) anstoßen
if [ -f /tmp/backup.lock ]; then
    filemtime=$(stat -c %Y /tmp/backup.lock)
    currtime=$(date +%s)
    diff=$(($currtime-$filemtime))
    if [ $diff -lt 600 ]; then
        exit;
    fi
fi

touch /tmp/backup.lock

DISPLAY=:0 kdialog --msgbox "Backup wird erstellt."

mount /backup

cd /backup

# backup.$n verschieben nach backup.{$n+1}
i=0
while [ -d "backup.$i" ]; do
    i=$(($i+1))
done
i=$(($i-1))
while [ $i -gt 0 ]; do
    j=$(($i+1))
    mv "backup.$i" "backup.$j"
    i=$(($i-1))
done

# letztes Backup kopieren mit Hardlinks -> inkrementelles Backup
cp -al backup.0 backup.1

cd ~

# zu sichernde Ordner aus Konfigurationsdatei lesen
IFS=$'\n'
dirs=$(cat ~/.backup.conf)

# Ordner sichern
for dir in $dirs; do
    echo "Erstelle Backup für Ordner $dir:"
    rsync -avX --delete "$dir/" "/backup/backup.0/$dir/"
    echo ""
done

umount /backup

DISPLAY=:0 kdialog --msgbox "Datensicherung abgeschlossen."

Wer auch nach einer bequemen Lösung sucht, findet hier evtl. eine Anregung. Kommentare sind willkommen!