piBackup - Sicherung im Heimnetz
Oder die Sicherung des Heimnetzes. Denn je mehr man selber Zuhause betreibt und administriert, desto wichtiger wird es die Infrastruktur auch regelmäßig und zuverlässig zu sichern. Ein kompletter Datenverlust der Zuhause betriebenen Nextcloud kann sonst nämlich sehr viel Zeit und Nerven kosten. Da ich mittlerweile mehrere Systeme (Raspberry Pis) für verschiedene Aufgaben im Einsatz habe, musste also ein vernünftiges Backup-Konzept her.
Lange Zeit habe ich mich davor gesträubt Serversysteme Zuhause zu betreiben. Mit dem Aufkommen der Raspberry Pis und ihrer hohen Leistung bei niedrigen Stromverbrauch, hat sich dies erheblich geändert. Was mich zuletzt dennoch daran hinterte ein Heimnetz aufzubauen, war das Thema Backup und Recovery. Ein VPS bei einem Provider wird immer gesichert und lässt sich gegen den Einwurf von Münzen immer wieder herstellen. Vor größeren Systemänderungen kann man selbst auch einen Snapshot erstellen, welcher sich problemlos wiederherstellen lässt. Daher spielte ich tatsächlich sehr lange mit dem Gedanken, mir eine ESXi Infrastruktur mit einem leistungsstarken Intel NUC aufzubauen. Hier ist man allerdings sehr schnell im vierstelligen Kostenbereich und der Stromverbrauch steigt ebenso.
Nachdem ich mir den ersten Raspberry Pi 4 mit einem einfachen Webserver aus Interesse installiert hatte, kam doch recht schnell der Wunsch auf diesen regulär statt des VPS zu nutzen. Da aber auch eine Neuinstallation eines Debian mit einem einfachen Nginx bei Totalausfall nicht in 5 Minuten erledigt ist, musste irgendwie ein regelmäßiges Backup her...
Das Basisbackup
Das beste Backup ist immer noch das Fullbackup. Hierbei kann man nichts vergessen und nach der Wiederherstellung hat man (idealerweise) wieder den Ursprungszustand. In der heutigen Zeit verbindet man damit sofort (zumindest geht es mir so) immer ein Snapshot einer VM. Der Raspberry Pi ist aber keine VM, sondern "echtes Blech". Dadurch, dass aber Linux eingesetzt wird, gibt es auch den dd Befehl, welcher in der Lage ist, ganz stumpf und kompromisslos Blöcke von A nach B zu kopieren. Der Raspberry Pi hat mittlerweile zwei USB 3.0 Anschlüsse. Warum also nicht den Inhalt der SDCard (auf der sich das System befindet) per dd auf einen USB Stick kopieren? Und das funktioniert einwandfrei.
dd if=/dev/mmcblk0 of=/backup/$(date +%Y%m%d)FullBackup.img
Voraussetzung ist natürlich, dass ein USB Stick eingerichtet und unter /backup eingehängt ist.
Das würde theoretisch bereits ausreichen um das Schlimmste zu vermeiden. Für den kompletten Datenverlust müsste schon die SDCard und der USB Stick gleichzeitig kaputt gehen. Nicht unmöglich (bspw. durch Blitzschlag), aber die Wahrscheinlichkeit sinkt. Dieser Befehl würde sich sogar einfach 1:1 in einen cron-Job einfügen lassen, so dass nicht einmal ein separates Script nötig wäre. Aber was ist, wenn der USB Stick aus irgendwelchen Gründen nicht eingehängt ist? Wir kommen also um ein Script nicht herum, was uns aber sowieso viele weitere Möglichkeiten eröffnet.
Erweitertes Basisbackup
Es gilt also zu prüfen, ob der USB Stick verfügbar ist. Sonst würde der dd Befehl nämlich das Backup in das Root-Verzeichnis schreiben und die SDCard wäre voll und der Pi arbeitsunfähig.
if mountpoint -q /backup; then
...
Je nach Größe des USB Sticks muss man vor dem Backup natürlich aufräumen. Also kann man vor dem dd entweder einfach alle img-Dateien löschen, oder nur Backups die älter als x Tage sind. In meinem Fall lösche ich vor dem Backup einfach alle img-Dateien, da erstellte Backups von anderer Stelle eingesammelt werden. Hierzu später mehr.
Um nicht jeden Tag manuell zu prüfen, ob das Backup erfolgreich gelaufen ist, empfiehlt es sich auf dem System einen Mailer (z.B. Postfix) einzurichten. Somit kann im Script eine Mail bei Erfolg und/oder Misserfolg versandt werden.
echo "/backup not mounted!" | mailx -s "Pi Backup" xy@irgendwo.de
Individuelles erweitertes Basisbackup
Ausschließlich ein Fullbackup zu haben ist natürlich für Totalausfälle perfekt geeignet. Was ist aber, wenn ich nur eine Konfigurationsdatei oder den Inhalt einer Webseite von Vorgestern benötige? Für diesen Fall habe ich das Script nochmals um eine Funktion erweitert, welche mir explizit Nginx und Webseiten sichert.
webserverBackup() {
rm -f /backup/*.tar.gz
mkdir /backup/$(date +%Y%m%d)_webServerBackup
mkdir /backup/$(date +%Y%m%d)_webServerBackup/nginxConf
mkdir /backup/$(date +%Y%m%d)_webServerBackup/nginxWwwRoot
cp /etc/nginx/sites-available/* /backup/$(date +%Y%m%d)_webServerBackup/nginxConf/
cp -r /usr/share/nginx/html/* /backup/$(date +%Y%m%d)_webServerBackup/nginxWwwRoot/
tar -cf /backup/$(date +%Y%m%d)_webServerBackup.tar -C / backup/$(date +%Y%m%d)_webServerBackup
gzip /backup/$(date +%Y%m%d)_webServerBackup.tar
rm -rf /backup/$(date +%Y%m%d)_webServerBackup
}
Innerhalb des Scripts kann ich dann entscheiden, ob ich ein Fullback und Webserver-Backup machen möchte, oder nur eines von beiden.
while getopts o: flag
do
case "${flag}" in
o) option=${OPTARG};;
esac
done
...
if [ "$option" = "full" ]; then
...
Mehr ist es an dieser Stelle nicht. Wenn man das Script nun per cron einbindet, hat man regelmäßig ein (Full)Backup seines Raspberry Pis mit einem Nginx Webserver.
00 01 * * * /bin/bash /root/webserverBackup.sh -o "full"
Das Gleiche Gerüst lässt sich ebenfalls für eine Nextcloud verwenden, wobei hier natürlich die Funktion einige Unterschiede aufweist. Das Script für den Webserver befindet sich hier und das Script für die Nextcloud hier.
Snapshot Charme - Das Einsammeln der Backups
Tagesaktuelle Backups, welche allerdings immer wieder überschrieben werden, sind zwar nett, aber auch gefährlich. Sofern man als externes Medium am Raspberry Pi nicht eines mit hoher Kapazität hat, muss eine andere Lösung her. Meine Pis haben alle (bis auf die Nextcloud) eine 64GB SDCard und einen 128GB USB Stick. Mit dieser Lösung funktioniert immer nur ein Backup bzw. eine img-Datei auf dem USB Stick (die Nextcloud hat eine 256er SDCard und 512er SSD HDD).
Ich habe mein Netzwerk in eine sichere interne Zone und eine DMZ getrennt. Die Idee war also, dass ein anderes System alle Backups einsammelt und vorhält. Natürlich sollte dieses System aber nicht in der DMZ stehen. Also wurde ein Raspberry Pi 4 mit einer externen 2TB HDD als System hierfür auserkoren. Damit das Kopieren später auch scriptgesteuert erfolgen kann, habe ich die SSH Keys unter den Servern ausgetauscht. Die Basis dieses "Einsammeln der Backups" ist nichts weiter als ein scp Befehl.
scp user@pi:/backup/20210909FullBackup.img /fileserver/piBackups/pi/
Eine grafische Übersicht über das Backup-Konzept:
Der Fileserver weist eine einfache Verzeichnisstruktur auf, in welcher die einzelnen Pis abgebildet sind und deren Backups abgelegt werden.
Bashscriptvoodoo
Ok nicht wirklich :) Alles was das Script macht ist durch ein festgesetztes Array zu iterieren, welches die zu sichernden Systeme definiert.
aPis=("pi41" "pi42" "pi43" "pi44")
for pi in ${aPis[@]};
do
....
if [ "$pi" == "pi41" ]; then
...
Innerhalb der for-Schleife kann individuell festgelegt werden, ob bei einem bestimmten System etwas anderes gemacht werden soll. Im Kern wird aber nur der scp-Befehl ausgeführt. Ich habe diesen in der Lösung in einer erweiterten Funktion namens "copyLatestFile" untergebracht, welche u.a. in der Lage ist verschiedene Dateitypen zu verarbeiten.
result=`copyLatestFile -r true -u "$user" -p "$pi" -s "$remoteBackupDir" -f "img" -d "$backupRootDir/$pi/"`
Die Funktion kopiert in diesem Beispiel einfach nur noch die aktuellste img-Datei.
Im zweiten Teil der for-Schleife wird dann noch geprüft, welche Backups veraltet sind und gelöscht werden können.
filesToDel=`find $backupRootDir/$pi/ -mtime +$holdTime`
if ! [ -z "$filesToDel" ]; then
find $backupRootDir/$pi/ -mtime +$holdTime -delete
...
fi
Das Script läuft bei mir ca. 2 Stunden nachdem die Pis ihre lokale Sicherung gemacht haben (diese dauert ungefähr 1:40). Das Script logt alle Vorgänge und schickt am Ende eine Mail mit den Ergebnissen.
Copy backup from pi41...
20210909pi41FullBackup.img copied.
20210909_webServerBackup.tar.gz copied.
Cleanup...
Deleted /fileserver/piBackups/pi41/20210906_webServerBackup.tar.gz
/fileserver/piBackups/pi41/20210906pi41FullBackup.img
------------------------------
Copy backup from pi42...
20210909p42FullBackup.img copied.
Cleanup...
Deleted /fileserver/piBackups/pi42/20210906p42FullBackup.img
/fileserver/piBackups/pi42/20210905p42FullBackup.img
------------------------------
Copy backup from pi43...
20210909p43FullBackup.img copied.
Cleanup...
Deleted /fileserver/piBackups/pi43/20210905p43FullBackup.img
------------------------------
Copy backup from pi44...
20210909_ncBackup.tar.gz copied.
Cleanup...
Deleted /fileserver/piBackups/pi44/20210902_ncBackup.tar.gz
------------------------------
Und das war es schon. Je nach Kapazität des Fileservers kann man über Tage oder Wochen (oder noch länger) alle Backups seiner Rasperry Pi bzw. Serversysteme aufbewahren und hat jederzeit Zugriff darauf. Die img-Dateien lassen sich einfach mit einem Flash-Tool wie balena Etcher oder dem Raspberry Pi Imager auf eine SDCard spielen und wieder in ein System einsetzen. Hinweis: balena Etcher kann u.U. nach dem Flashvorgang eine Fehlermeldung auswerfen. Bei mir bootete das System trotzdem.
Das vollständige Script für das Einsammeln der Backups befindet sich ebenfalls hier auf Codeberg.
Anregungen und Diskussionen zum Beitrag findet ihr auf Mastodon.