Server Backup mit rsnapshot

Posted on So 21 April 2019 in Computer & Electronics

Ich habe einen kleinen Home-Server. Der dient mir als Fileserver zum synchronisieren meines Laptops, dito für den Windows Rechner meiner Frau, er hostet ein paar Datenbanken (mariadb) und es läuft ein Wiki drauf. Die Spiegelung meiner Dateien via unison Synchronisation plus das RAID System im Server sind ja schon mal ein Sicherheitsfaktor, falls das Laptop geklaut wird oder schlapp macht, aber ein Backup ersetzt das nicht, denn wenn ich nicht rechtzeitig merke, wenn was schief läuft, dann synchronisiert sich das beschädigte System ggf. treu und brav, so dass dann auch die Kopie auf dem Server in Eimer ist. Auch ist es kein Fehler ältere Archiv-Versionen aufzuheben. Wer weiß ob die Uralt-Version eines Dokuments nicht doch nochmal wichtig wird.

Wiki und mariadb sind ohnehin nur auf dem Server und da ist ein Backup Pflicht.

Backup!

Also muss eine Backup-Strategie her. Ganz früher hatte ich halt so hin und wieder von Hand meine Daten auf eine externe Festplatte kopiert und später mit diversen Backup-Programmen experimentiert. Die Auswahl an Programmen darf mit Fug und Recht als uferlos bezeichnet werden, wie eine Google-Suche nach "LINUX backup" demonstriert. Also was nehmen?

Im Vorfeld hatte ich erstmal mit bekannteren Werkzeugen wie Mondo Rescue, Amanda oder Bacula gespielt, aber irgendwie gefiel mir nichts davon – zu groß, zu grafisch zu wenig elegant. Ja – alles total subjektiv, aber bisher lag mein Instinkt bei solchen Dingen meist richtig.

Richtig interessant fand ich drei Tools, die meinen Vorstellungen von einer schlanken, robusten und GUI-losen Lösung entsprachen:

Alle drei sind reine Kommandozeilen Werkzeuge (teils mit optionalem GUI), was ich gerade für eine Server-Backup sehr wichtig finde, denn so kann man Backups leicht skripten und automatisieren und versteht anhand des config Files auf einen Blick, was es alles tut – und was nicht.

Am besten gefiel mir letztlich rsnapshot es beherrscht saubere und zugleich platzsparende inkrementelle Backups und ist sehr flexibel konfigurierbar. Zudem verwendet es kein komplexes Archivformat, sondern speichert den Dateibaum einfach nur ab. Tatsächlich verwendet es dann Hardlinks, um unveränderte Dateien in den verschiedenen Backup Zweigen zu repräsentieren. Das spart Platz und macht es trivial, eine Datei im Backup zu finden.

Ein Medium muss her!

Als nächstes ist zu entscheiden, wo die Sicherung überhaupt gespeichert werden soll.

  • CD/DVD – im Ernst? In 2019? Wohl kaum. Und auch früher war das Mist, denn so eine glitzernde Scheibe verliert ihre Daten schneller, als ein Rodeo-Bulle seinen Reiter.
  • Bänder? Tatsächlich auch heute eine sehr aktuelle und sinnvolle Variante, aber Bandlaufwerke und Bänder sind preislich seit vielen Jahren endgültig im Profi-Sektor zuhause.
  • Cloud? Wäre eine Möglichkeit – zumindest wenn Datenmenge und Internet-Bandbreite in einem sinnvollen Verhältnis stehen. Erfordert aber einen brauchbaren Cloud-Speicher-Anbieter. Vielleicht mache ich das irgendwann, aber momentan war es mir zu teuer.
  • NAS? Warum nicht. Aber extra als Backup Medium wollte ich mir keines kaufen. Und wenn man mal off-site Kopien haben will ist das auch eher nix.
  • Festplatte? Aktuell das Medium meiner Wahl, denn externe USB-Platten sind günstig und in großen Kapazitäten zu haben.

Also externe USB Platten. Zwei WD-Elements Disks mit je 1.5TB in meinem Fall.

Was sichere ich bloß?

Bevor ich mir tiefere Gedanken um die Software gemacht habe stellte sich die Frage, was ich eigentlich alles sichern will und das ist garnicht so trivial, wie es klingt. Will ich den kompletten Rechner aus dem Backup wieder auferstehen lassen können? Oder nur meine Daten? Was ist mit dem Wiki? Reicht da die Datenbank? Oder speichert das noch irgendwo sonst erhaltenswerte Daten? etc., etc.

Letztlich habe ich beschlossen, dass es sich nicht lohnt, ein komplett-Image zu machen, oder das Betriebssystem anderweitig zu sichern. Eine Debian Installation geht einfach und sehr schnell – vorausgesetzt man weiß, welche packages installiert werden sollen und die Konfigs erhalten sind. Darüber hinaus sind noch ein paar Infos schützenswert:

D.h. ich sichere automatisch die Liste installierter packages und unterscheide dabei zwischen manueller und automatischer Installation. Auch halte ich fest, welche packages den Status hold haben, denn das mach ich für besonders wichtige Applikationen, bei denen ich automatische Updates verhindern will. Die folgenden Kommandos liefern diese Info:

apt-mark showmanual
apt-mark showauto
apt-mark showhold

Und für den Fall, dass ich unbedingt wissen will/muss, welche Version eines Pakets installiert war nehme ich auch noch ein

dpkg-query -l

Dafür habe ich mir ein kleines Skript gebastelt, das diese Info speichert (save-package-states.sh):

#!/bin/bash
# save package states
# this script is meant to be executed by rsnapshot

APT_MARK=/usr/bin/apt-mark
DPKG_QUERY=/usr/bin/dpkg-query

# save manual vs. automatic installation and hold state
$APT_MARK showmanual > pkgs_manual.lst
$APT_MARK showauto > pkgs_auto.lst
$APT_MARK showhold > pkgs_hold.lst

# Also provide detailed version and ARCH information in case of problems.
$DPKG_QUERY -l > pkgs-state.txt

Darüber hinaus kommt noch /etc ins backup, denn das eine oder andere File habe ich an meine Bedürfnisse angepasst. Also kommt dies in /etc/rsnapshot.conf:

backup  /etc/   hal/etc/

Das sagt dem Programm, dass wir ein Backup wollen und zwar vom Pfad /etc/ und das soll dann im Folder hal/ auf das Backup-medium geschrieben werden – hal ist der Name meines Servers.

Wichtig ist /home/, denn dort leben meine User-Daten. Bei mir tatsächlich noch mehr, denn ich habe die user homes unter /home/users/, Projektdaten unter /home/data und Web Sachen unter /home/www. Also:

backup  /home/  hal/home/

Nicht vergessen sollte man auch /srv. Bei mir liegt da nur ein Verzeichnis für den FTP-Server, den ich im Intranet habe, aber grundsätzlich findet sich dort ggf. alles mögliche andere Zeug, das verschiedene Dienste nach außen anbieten.

backup  /srv/   hal/srv/

Bleibt noch die Datenbank. Da führen viele Wege nach Rom – welcher sinnvoll ist hängt davon ab, welche Datenmengen zu sichern sind und auch wie stark diese über die Zeit zunehmen.

Oft wird empfohlen, den Datenbank-Pfad (z.B. /var/lib/mysql/) zu sichern. Damit das keine Probleme macht muss man aber sicherstellen, dass grade keine Schreibaktivität stattfindet. Außerdem kann ich es verschmerzen ggf. die Datenbank-Indizes neu aufzubauen. Wichtig sind mir nur die Daten. mariadb bietet eigene Bordmittel für inkrementelle Backups (mariabackup). Sollte ich irgendwann mal eine schnell wachsende Datenbank haben, werde ich das nutzen. Im Augenblick ist das aber nicht der Fall und so habe ich mich für den Weg über mysqldump entschieden und überlasse das inkrementelle Sichern rsnapshot. Das ist simpel und transparent. rsnapshot erlaubt es neben Pfaden auch backup-scripts zu definieren – also externe Programme, die sich darum kümmern die zu sichernden Daten irgendwo zu extrahieren:

backup_script   /etc/rsnapshot/save-package-states.sh   deb-packages/
backup_script   /usr/bin/mysqldump mysql > mysql.sql    mariadb/mysql/
backup_script   /usr/bin/mysqldump xwiki > xwiki.sql    mariadb/xwiki/

Das Xwiki hat auch noch lokale Daten:

backup  /var/lib/xwiki/data     hal/

Und wenn man das alles zusammenfügt ergibt sich folgender Auszug aus der Konfig-Datei:

# File server
backup  /etc/   hal/etc/
backup  /srv/   hal/srv/
backup  /home/  hal/home/
exclude /home/lost+found/

# debian package states
backup_script   /etc/rsnapshot/save-package-states.sh   deb-packages/

# xwiki
backup  /var/lib/xwiki/data     hal/xwiki/

# mariadb
backup_script   /usr/bin/mysqldump xwiki > xwiki.sql    mariadb/xwiki/
backup_script   /usr/bin/mysqldump mysql > mysql.sql    mariadb/mysql/

Wie oft soll ich sichern?

Das schöne an einem inkrementellen Backup ist, das es nur dann wirklich Speicherplatz verbraucht, wenn auch Daten hinzugekommen sind. D.h. es schadet nicht, häufige Backups zu machen. Ich habe mich für täglich entschieden und behalte die letzen 7 Versionen. Zusätzlich speichere ich wöchentliche (4 Stück) und monatliche (max 12) snapshots, so dass ich im Notfall ganz flexibel auf Kopien unterschiedlichen Alters zurückgreifen kann. Im rsnapshot.conf sieht das dann so aus:

retain    daily   7
retain    weekly  4
retain    monthly 12

Zum Verständnis: die Labels daily, weekly und monthly sind total willkürlich. rsnapshot ist es völlig wurscht, wie die heißen und es kümmert sich auch nicht darum, wie oft sie ausgeführt werden – das wird alles extern geregelt (cron). Was sollen die Einträge also? Das ist so: man ruft rsnapshot mit einem Label Argument auf und es nennt das jeweilige Backup dann auch so (daily.0/ ... daily.6/). Also lasse ich jede Nacht dies laufen:

rsnapshot daily

Sonntags noch zusätzlich

rsnapshot weekly

Und immer am 1. des Monats

rsnapshot monthly

rsnapshot schaut dann jeweils nach, wieviele daily, weekly oder monthly backups schon auf dem Medium liegen und löscht ggf. alte Versionen, wenn es mehr werden, als im Konfig File angegeben. Mit anderen Worten, es hebt immer die letzten 7 daily backups auf.

Die Reihenfolge der verschiedenen Backups signalisiert, dass weekly längerfristig ist, als 'daily' und monthly am längsten.

Wenn nun mein weekly backup läuft macht rsnapshot gar kein richtiges Backup, sondern schnappt sich einfach das siebte (=älteste) daily backup (daily.6) und nennt es weekly – somit ist dieser Stand archiviert. Das könnt Ihr nun beliebig tief/komplex schachteln, je nach Euren Bedürfnissen.

Nach einem Jahr schicke ich die Platte dann aufs Altenteil – sprich sie kommt ins Archiv und wird durch eine neue ersetzt. Zu diesem Zeitpunkt beinhaltet sie dann 12 Monats-, 4 Wochen- und 7 Tages-Snapshots.

Und damit mein Backup nicht von der Gesundheit einer einzigen USB-Platte abhängt habe ich zwei davon, die ich im Wechsel anschließe. Streng genommen müsste ich nun die Konfig anpassen, weil rsnapshot ja nicht weiß, was z.B. daily bedeutet, sondern die letzten 7 dieses Typs aufhebt, aber das war mir zu stressig – soll es doch mehr Kopien halten.

Tipp: rsnapshot will tabs als Delimiter zwischen den verschiedenen Argumenten – das hat mich erstmal verwirrt, bis ich es kapiert hatte...

Verschlüsselung

Falls so eine Backup Platte mal in falsche Hände geraten sollte, ist es eine gute Idee, sie zu verschlüsseln. Ich nehme dafür Linux Bordmittel (LUKS). D.h. ich bereite die Platte so vor:

# Generate a key file
dd if=/dev/urandom of=/root/backup.key bs=1k count=4
# create encrypted disk with predefined UUID (generated w/uuidgen)
cryptsetup luksFormat --uuid 56895370-b479-4541-ae57-74fc957fcaa3 /dev/sdX /root/backup.key
# attach the crypto disk
cryptsetup -d /root/backup.key open /dev/sdX backup
# create file system
mkfs.ext4 -m 1 /dev/mapper/backup
# detach disk
cryptsetup close backup

Ihr könnt statt eines key files natürlich auch eine Passphrase nehmen, aber keyfile ist sicherer – vorausgesetzt Ihr denkt daran, dieses gut zu schützen und ein separates Backup des Keyfiles zu machen und an einem sicheren Ort aufzubewahren, denn sonst nützt euch das schönste Backup nix, wenn Ihr es selber nicht mehr entschlüsseln könnt!

Ich erzwinge oben, dass meine Backup Platte eine bestimmte UUID bekommt. Ich gebe diese allen meinen Backup Platten, d.h. sie haben alle die gleiche UUID. Das erlaubt die automatische Verarbeitung im nächsten Abschnitt, ist aber etwas unorthodox. Also überlegt Euch, ob Ihr das für Euch so sinnvoll findet.

auto-mount

Im simpelsten Fall könnte ich nun meine Platte an den Server stöpseln und der nutzt sie allnächtlich für das Backup. Das birgt ein paar Risiken:

  • Wenn irgendwas auf dem Server amok läuft (Virus, Verschlüsselungstrojaner, Hack, schlechtes Karma) dann ist die externe Platte ggf. ebenfalls kompromittiert oder gelöscht.
  • Dito im Falle von Feuer oder Einbruch

Dagegen habe ich zwei Gegenmaßnahmen:

  • Zwei Festplatten, die im Wechsel (idealerweise täglich, aber ich bin dafür zu träge) angeschlossen werden
  • Die angeschlossene Platte wird nur direkt vor dem Backup gemountet und danach wieder unmounted. Das hilft natürlich nur begrenzt, aber besser, als nix...

Dazu habe ich mir ein kleines Wrapper-Skript (/etc/rsnapshot/backup.sh) gebastelt:

#!/bin/bash
# Wrapper around rsnapshot
# takes care of mounting and unmounting the backup disk

MAPPERDEV=/dev/mapper/backup
BACKUPPATH=/media/backup
UUID=56895370-b479-4541-ae57-74fc957fcaa3
KEYFILE=/root/backup.key

# does the mapper device already exist?
if [ ! -e $MAPPERDEV ]; then
        /sbin/cryptsetup --key-file $KEYFILE open /dev/disk/by-uuid/$UUID backup
else 
        echo $MAPPERDEV already exists
fi

# is it mounted?
if [ "$(findmnt -o TARGET -n $MAPPERDEV)" == "$BACKUPPATH" ] ; then
        echo $MAPPERDEV already mounted at $BACKUPPATH
else
        /bin/mount $MAPPERDEV $BACKUPPATH                                                                                                          
fi

/usr/bin/rsnapshot $1

# unmount the backup disk
/bin/umount $BACKUPPATH
/sbin/cryptsetup close backup

# put disk in standby mode
# use grep to suppress the SG_IO warning 
/sbin/hdparm -q -y /dev/disk/by-uuid/$UUID 2>&1 >/dev/null | grep -v 'SG_IO: bad/missing sense data, sb\[\]:'

Das mounted die Disk, führt das Backup durch und unmountet am Ende wieder. Und als Sahnehäubchen fährt es die Disk mit hdparm runter.

Automatisierung

Nun müssen wir das ganze nur noch in den gewünschten Abständen laufen lassen und das geht mit cron. In /etc/cron.d/ habe ich ein File namens rsnapshot, das diesen Inhalt hat:

# crontab for rsnapshot
# m  h  dom  mon     dow     user    command
07   2  *    *       *       root    /etc/rsnapshot/backup.sh daily
07   3  *    *       1       root    /etc/rsnapshot/backup.sh weekly
07   4  1    *       *       root    /etc/rsnapshot/backup.sh monthly

Und damit Ihr Euch das nicht alles von Hand zusammenfummeln müsst, könnt Ihr Euch meine diversen Files hier herunterladen und dann nach Herzenslust modifizieren:

Viel Spaß mit dem Backup und auf dass Ihr es niemals brauchen werdet.