ZFS offsite Backup auf eine externe Festplatte

Zu einem persönlichen Daten-GAU ist es bestimmt bei jedem schon einmal gekommen. In meinen frühen Jahren musste ich zweimal Datenverluste durch kaputte Festplatten verkraften. Danach folgten sporadische Kopien auf externe Festplatten, mal hier und mal dort ein Backup. Eine unkoordinierte Sammlung an externen Festplatten, gebrannten CDs und DVDs entstand. Zehn Jahre später gab es erste Fehler beim Lesen gebrannter CDs, außerdem wusste ich auch nicht genau was sich wo genau befand. Seit 2015 betreibe ich nun einen kleinen Heimserver mit FreeNAS, den meine Rechner als Zielmedium für ihr automatisiertes Backup nutzen. Außerdem lagern dort meine restlichen Daten. Wenn eine Platte ausfällt ist noch alles im Lot dank des RaidZ2 aber was ist, wenn der Blitz einschlägt, das Haus brennt, jemand beschließt meinen kleinen Server einfach mitzunehmen oder ich einen unsinnigen Befehl auf der Kommandozeile eintippe? Richtig, dann ist alles weg. 

Trotz, dass ich davor tatsächlich panische Angst habe und mir diese Unzulänglichkeit seit Inbetriebnahme der FreeNAS Box bewusst ist, fehlte es bisher an einem richtigen Backup. Warum? Weil der Weg zu einem richtigen Backup doch schon recht steinig ist, wenn man nur über Halbwissen verfügt.

Mittlerweile liegt eine Kopie meiner Daten ein paar Kilometer weit weg an einem vertrauensvollen Ort, ich schlafe ruhiger und mein Halbwissen zum Thema ZFS hat sich zumindest ein klein wenig erweitert. Vielleicht nützt dieses Wissen dem ein oder anderen etwas. Wenn nicht, so hab ich mit der folgenden Anleitung immernoch eine Notiz wie das Backup im Falle des Falles einzuspielen ist.

Anforderungen

Folgende Kriterien waren für mich entscheidend:

  • offsite Backup – die Daten sollen an einem physisch anderen Ort aufbewahrt werden
  • keine Cloud – ich möchte keinen (kommerziellen) Dritten dazwischen schalten; keine Terrabytes über das Internet morsen
  • schneller Zugriff auf einzelne Dateien im Notfall ohne spezielle Hardware aufsetzen zu müssen

Folgende Kriterien sind für mich nicht entscheidend:

  • 24/7 Aktualität des Backups – es geht hier nur um meine privaten Daten
  • 24/7 Zugang zum Backup

Backup-Strategie

Es steht ja schon im Titel – es ist eine Lösung mit externer Festplatte geworden. Diese wird per USB an die FreeNAS Box angeschlossen, das Update-Skript gestartet und anschließend wird die Platte an einen vertrauensvollen Ort transferiert.

Das Ganze findet im rotierenden System statt. Wenn also eine Platte zum vertrauenvollen Ort wandert, wird gleichzeitig eine von dort wieder mitgenommen und wiederum mit dem aktuellen Datenstand versehen. Der Plan ist dies monatlich durchzuführen.

Da die Daten auf der FreeNAS Box schon in einem ZSF Pool liegen bietet es sich natürlich an diesen mit zfs send | zsf receive auf die USB-Platte zu spiegeln. Für alle Eventualitäten hebe ich noch die letzen 3 Snapshots auf.

Umsetzung

1. Pool anlegen

Auf der USB-Platte muss natürlich erstmal ein ZSF Pool angelegt werden. Die Platte wird dazu an das FreeNAS angeschlossen und anschließend in der Konsole ein dmesg ausgeführt. Die letzten Zeilen der dmesg Ausgabe könnten zum Beispiel so aussehen:

umass1 on uhub2
umass1: <Western Digital Elements 25A2, class 0/0, rev 2.10/10.14, addr 5> on usbus0
umass1:  SCSI over Bulk-Only; quirks = 0xc101
umass1:8:1: Attached to scbus8
da1 at umass-sim1 bus 1 scbus8 target 0 lun 0
da1: <WD Elements 25A2 1014> Fixed Direct Access SPC-4 SCSI device
da1: Serial Number 575842314141364832304143
da1: 40.000MB/s transfers
da1: 1907697MB (3906963456 512 byte sectors)
da1: quirks=0x2<NO_6_BYTE>Code-Sprache: Bash (bash)

Das da1 ist die gesuchte Information (USB-Speicher werden in FreeBSD mit da0, da1, da1, … bezeichnet), die Platte können wir nun nämlich über /dev/da1 ansprechen. Mit dem folgenden Befehl wird die ganze Platte für den ZFS Pool verwendet, wir nennen ihn backup.
Achtung: Seid euch bitte 100% sicher, dass /dev/da1 eure USB-Platte anspricht und keine andere, sonst braucht ihr kein Backup mehr 😉

zpool create -f -O compression=lz4 -O atime=off -O casesensitivity=insensitive -O normalization=formD backup /dev/da1Code-Sprache: Bash (bash)

Wenn keine Fehlermeldung aufgetaucht ist können wir nun den Pool auswerfen, unser Backup-Script wird ihn später wieder einbinden.

zpool export backupCode-Sprache: Bash (bash)

2. Backup Skript

Meine Daten liegen alle in dem Pool data. Darin habe ich mehrere Datasets, wovon im folgenden Beispiel nur media und timemachine in das Backup sollen.

FreeNAS Datasets

Das folgende Skript wird zum Beispiel als backup.sh in einem Verzeichnis auf dem FreeNAS gespeichert. Im oberen Bereich erfolgen die individuellen Anpassungen (siehe Kommentare im Skript).

#!/bin/bash

# Pool mit den zu sichernden Daten
MASTERPOOL="data"

# Backup-Pool
BACKUPPOOL="backup"

# Datasets, die in das Backup sollen
DATASETS=("media" "timemachine")

# Anzahl der zu behaltenden letzten Snapshots, mindestens 1
KEEPOLD=3

# Praefix fuer Snapshot-Namen
PREFIX="auto"

# -------------- ab hier nichts aendern ---------------

zpool import $BACKUPPOOL

KEEPOLD=$(($KEEPOLD + 1))

for DATASET in ${DATASETS[@]}
do
    # Namen des aktuellsten Snapshots aus dem Backup holen
	recentBSnap=$(zfs list -rt snap -H -o name "${BACKUPPOOL}/${DATASET}" | grep "@${PREFIX}-" | tail -1 | cut -d@ -f2)
	if [ -z "$recentBSnap" ] 
		then
			dialog --title "Kein Snapshot gefunden" --yesno "Es existiert kein Backup-Snapshot in ${BACKUPPOOL}/${DATASET}. Soll ein neues Backup angelegt werden? (Vorhandene Daten in ${BACKUPPOOL}/${DATASET} werden ueberschrieben.)" 15 60
			ANTWORT=${?}
			if [ "$ANTWORT" -eq "0" ]
				then
					# Backup initialisieren
					NEWSNAP="${MASTERPOOL}/${DATASET}@${PREFIX}-$(date '+%Y%m%d-%H%M%S')"
					zfs snapshot -r $NEWSNAP
					zfs send -v $NEWSNAP | zfs recv -F "${BACKUPPOOL}/${DATASET}"
			fi
			continue
	fi
	
	# Check ob der korrespondierende Snapshot im Master-Pool existiert
	origBSnap=$(zfs list -rt snap -H -o name "${MASTERPOOL}/${DATASET}" | grep $recentBSnap | cut -d@ -f2)
	if [ "$recentBSnap" != "$origBSnap" ]
		then
			echo "Fehler: Zum letzten Backup-Spanshot ${recentBSnap} existiert im Master-Pool kein zugehoeriger Snapshot."
			continue
	fi
	
	echo "aktuellster Snapshot im Backup: ${BACKUPPOOL}/${DATASET}@${recentBSnap}"
	
	# Name fuer neuen Snapshot
	NEWSNAP="${MASTERPOOL}/${DATASET}@${PREFIX}-$(date '+%Y%m%d-%H%M%S')"
	# neuen Snapshot anlegen
	zfs snapshot -r $NEWSNAP
	echo "neuen Snapshot angelegt: ${NEWSNAP}"
	
	# neuen Snapshot senden
	zfs send -v -i $recentBSnap $NEWSNAP | zfs recv "${BACKUPPOOL}/${DATASET}"
	
	# alte Snapshots loeschen
	zfs list -rt snap -H -o name "${BACKUPPOOL}/${DATASET}" | grep "@${PREFIX}-" | tail -r | tail +$KEEPOLD | xargs -n 1 zfs destroy -r
	zfs list -rt snap -H -o name "${MASTERPOOL}/${DATASET}" | grep "@${PREFIX}-" | tail -r | tail +$KEEPOLD | xargs -n 1 zfs destroy -r
done

zpool export $BACKUPPOOL
Code-Sprache: Bash (bash)

3. Backup durchführen

So ein Backup auf eine USB-Platte kann schon ein ganzes Weilchen dauern. Damit die Skript-Ausführung nicht abbricht falls zum Beispiel die Terminal-App aus Versehen geschloßen wird oder die SSH-Verbindung zu FreeNAS Schluckauf bekommt sollte das ganze in einer virtuellen Konsole ausgeführt werden. Dazu eignet sich zum Beispiel das Programm tmux. Dieses wird auf der Konsole einfach mit dem Befehl tmux gestartet und es öffnet sich eine virtuellen Konsole. Um diese zu verlassen ohne sie zu schließen wird einfach [Strg]+[B] und anschließend [D] gedrückt (auch wenn gerade das Backu-Skript läuft). Zum Zurückkehren wird der Befehl tmux attach genutzt (Achtung: Wenn ihr als root tmux startet, müsst ihr natürlich wieder als root die virtuelle Sitzung aufnehmen).

Es wird in der virtuellen Konsole mit cd in das Verzeichnis mit dem Backup Script gewechselt und anschließend das Skript gestartet:

./backup.shCode-Sprache: Bash (bash)

Was passiert jetzt? Zunächst wird der backup Pool eingebunden. Anschließend wird das eigentliche Backup durchgeführt. Dabei erscheint beim erstmaligen Durchlauf für jedes Dataset ein Dialog, welcher fragt ob ein neues Backup angelegt werden soll. Hier ist natürlich ja auszuwählen. Solltet dieser Dialog irgendwann später nochmal auftauchen so ist etwas schief gegangen.

Das Skript erzeugt nun automatisch mit jedem Durchlauf einen Snapshot für jedes angegebene Dataset und benennt diesen mit dem aktuellen Datum, zum Beispiel: data/timemachine@auto-20180218-232209. Dieser Snapshot wird anschließend inkrementell auf die USB-Platte gesendet. Beim ersten Durchgang dauert dies je nach Größe der Daten recht lange. Bei weiteren Durchläufen werden nur noch die Änderungen übertragen. Anschließend werden sowohl auf dem FreeNAS als auch auf der USB-Platte alte Snapshots gelöscht, bis auf die drei letzten (kann mit KEEPOLD eingestellt werden).

Zum Abschluss exportiert das Skript noch den backup Pool – die USB-Platte wird ausgeworfen. Wenn keine Fehlermeldungen aufgetaucht sind kann die Platte nun wieder abgestöpselt werden.

4. Backup einspielen

Nehmen wir an der Nachbar über mir hat einen Wasserschaden und mein FreeNAS ist baden gegangen. Was nun? Wie komme ich wieder an meine Daten? Dieser Schritt sollte unbedingt getestet werden, bevor man sich zufrieden zurücklehnt und in Sicherheit wägt. 

4.1 Komplettes Backup in einen neuen Pool einspielen

Für diesen Fall muss selbstverständlich ein Rechner mit einem ZFS unterstützenden Betriebssystem bereit stehen. Wir gehen einfach mal wieder von einer FreeNAS Box aus. Auf diesem Rechner wird ein neuer Pool angelegt (z.B. in der FreeNAS Weboberfläche) mit dem Namen newpool. Anschließend wird die USB-Platte mit dem Backup angeschlossen und eingebunden.

zpool import backupCode-Sprache: Bash (bash)

Mit dem obigen Skript haben wir nur einzelne Datasets gesichert, diese müssen jetzt auch einzeln wiederhergestellt werden. Zunächst schauen wir, was denn eigentlich da ist.

zfs list -rt snap -o name,creation backupCode-Sprache: Bash (bash)

Dieser Befehl erzeugt eine Ausgabe wie zum Beispiel:

NAME                                        CREATION
backup/media@auto-20180219-124435           Mon Feb 19 12:44 2018
backup/media@auto-20180219-130558           Mon Feb 19 13:05 2018
backup/timemachine@auto-20180219-124435     Mon Feb 19 12:44 2018
backup/timemachine@auto-20180219-130558     Mon Feb 19 13:05 2018Code-Sprache: Bash (bash)

Um nun den letzten Snapshot vom Dataset media einzuspielen ist folgendes notwendig:

zfs send -R backup/media@auto-20180219-130558 | zfs recv -vF newpool/mediaCode-Sprache: Bash (bash)

Jetzt sollten die eingespielten Daten noch geprüft werden:

zpool scrub newpoolCode-Sprache: Bash (bash)

Zum Schluss wird die USB-Platte ausgehangen:

zpool export backupCode-Sprache: Bash (bash)

4.2 Wie komme ich ganz schnell an DIE eine Datei?

Im Falle des Daten-GAUs ist möglicherweise kein Gerät vorhanden, um die ganzen Datasets wieder einzuspielen, trotzdem muss man unter Umständen an bestimmte Daten herankommen. Da mit zfs send | zfs receive ein vollständiges Dateisystem auf die USB-Platte geschrieben wurde, kann diese auch an jedem ZFS unterstützenden Rechner gelesen werden. 

Ich habe das Ganze einmal an meinem Mac ausprobiert. Dazu muss auf dem Mac OpenZFS on OSX installiert werden. Nun kann die USB-Platte an den Mac angeschlossen werden. Etwaige Angebote von OSX die Platte zu initialisieren oder ähnliches sind strengstens abzulehnen.

OSX Fehlermeldung

Der backup Pool auf der USB-Platte wird nun als readonly nach /Volumes/backup gemountet. Da hierfür root Rechte notwendig sind, wird sudo benutzt, es verlangt anschließend das Nutzer-Passwort.

sudo zpool import -o readonly=on -o altroot=/Volumes/backup backupCode-Sprache: Bash (bash)

Nun sollten die Datasets nach einige Sekunden im Finder auftauchen, Dateien können jetzt einfach auf den Mac kopiert werden. Zum Abschluss muss der Pool jedoch unbedingt wieder mit zpool export ausgeworfen werden.

zpool export backupCode-Sprache: Bash (bash)

Anmerkungen

Wie bereits eingehend erwähnt: Die vorgestellte Backup Lösung ist meine eigene Variante für meine privaten Daten, von deren Existenz nicht das Weltgeschehen abhängt. Wenn ihr ähnliches vor habt, nutzt diese Anleitung als Hilfestellung aber informiert euch vorher ausführlich über ZFS, zum Beispiel dort:

Lest euch das obige Backup-Skript durch und versucht es nachzuvollziehen – nutzt es nicht blind, schreibt vielleicht sogar euer eigenes! Lest euch auf der Konsole eingegebene Befehle nocheinmal genau durch, bevor ihr auf Enter drückt. Testet euer Backup erstmal mit Testdaten. Probiert auch unbedingt Schritt 4 aus. Überprüft, welche Snapshots erstellt werden, zum Beispiel mit:

zfs list -rt snap -o name,creationCode-Sprache: TeX (tex)

Ich wünsche euch viel Erfolg auf dem Weg zu eurem Backup!

Kommentare

39 Antworten zu „ZFS offsite Backup auf eine externe Festplatte“

  1. Benutzer Icon
    Christian

    Hallo Jörg,
    durch Zufall bin ich beim Recherchieren auf deinen Beitrag gestoßen. Genau nach dieser Lösung suche ich derzeit und werde deine Anleitung als roten Faden nehmen.
    Vielen Dank für die Dokumentation.
    Gruß
    Christian

  2. Benutzer Icon
    Thomas

    Spitzen Anleitung. Daumen hoch.

  3. Benutzer Icon
    Max

    Nice, Danke!

  4. Benutzer Icon
    Stefan

    Besten Dank für die ausführliche Anleitung. Sie hat mir die Migration meines zfs-Pools sehr erleichtert.

  5. Benutzer Icon
    Markus

    Kurze Anmerkung:
    Wenn man KEEPOLD=3 einstellt, also auf dem Quellsystem drei Snapshots vorgehalten werden, man aber bspw. mit einem Rotationssystem aus sieben externen Zielplatten arbeitet, dann hat man ein Problem. Man müsste dann, käme man zum zweiten Mal bei der fünften Platte an, wieder ein Vollbackup erstellen (wenn ich mich nicht verzählt habe). Um sicher zu gehen empfiehlt es sich KEEPOLD auf die Anzahl der externen Zielplatten +1 zu stellen.

  6. Benutzer Icon
    Thomas

    Tolle Anleitung! Sehr hilfreich und die Gedanken sind prima erklärt. So etwas habe ich gebraucht.

  7. Benutzer Icon
    Matthias

    Ich hab womöglich ein Verständnisproblem: Ich möchte ein Archiv als Backup aufbauen. Daten welche auf normlane Pools liegen, sollen mittels snapshots und send/receive in eine Art Archiv gesichert werden, also einen zweiten Pool welcher dann nur lesbar sein soll. Erfüllt das Skript diese Bedingungen?

    1. Benutzer Icon

      Ja, das kann das Skript. Wenn der Backup-Pool immer verfügbar sein soll, musst du nur den Export weg lassen. Die Lese- und Schreibrechte setzt du über die Benutzerrechte, das macht das Skript nicht 😉

  8. Benutzer Icon
    Florian

    Hallo Jörg, danke für den Post. Da auch ich mein ubuntu-zfs nur Hobby-mäßig zu privaten Zwecken betreibe hat mich der Artikel sehr weitergebracht, Danke für den Erkenntnisgewinn! 🙂
    *FRAGEN* zu Plattenkapazität:
    (1) Da es sich bei dem backup-vdev quasi um ein RAID0 handelt, steht der komplette Platz der Backup-Platte zur Verfügung, richtig?
    (2) In deinem Beispiel kommst du auf ca. 1400 GiB netto Nutzdaten (media 790 GiB, timemachine 610 GiB). Würde also eine 2TB reichen, obwohl in deinem FreeNAS der verfügbare Speicherplatz deutlich größer wäre?

    1. Benutzer Icon

      Danke für das Lob 🙂
      (1) In meinem Fall ist das Backup-Ziel nur eine externe Festplatte deren voller Platz zur Verfügung steht, ein RAID0 ist das aber nicht. Worauf letztendlich der Backup-Pool erstellt wird ist egal, es kann auch auf einem RAID-Verbund sein.
      (2) Ja 2 TB reichen, solange die zu sichernden Daten nicht viel größer werden.

  9. Benutzer Icon
    Raphael

    Super Artikel. Leider funktioniert das nur mit unverschlüsselten Pools. Gibt’s auch eine automatisierte Möglichkeit, verschlüsselte Pools auf eine externe Festplatte zu sichern?

    1. Benutzer Icon

      Damit habe ich mich bisher noch nicht beschäftigt und kann somit leider nicht sagen wie so eine Lösung funktioniert. Aber automatisieren lässt sich alles 😉

  10. Benutzer Icon
    Niklas

    Danke für dieses großartige Skript. Es erfüllt seit einigen Monaten unaufällig seinen Dienst für mich. Ich bin nun dabei, dieses nach meinem persönlichen Geschmack „umzubauen“, sodass im ersten Schritt die alle Snapshots im Masterpool erstellt, im zweiten Schritt die erstellten Snapshots gesendet und im dritten Schritten dann alle „alten“ Snapshots gemäß „KEEPOLD“-Variable gelöscht werden.

    Beim Umbauen des dritten Schritts kam mir dann ein Gedanke: Beim Initialisieren wird ein Voll-Backup erstellt und gesendet. Alle darauf folgenden Snapshots werden nur noch inkrementell gesendet und bis auf die letzten Backups (KEEPOLD) gelöscht. Wird damit dann nicht auch früher oder später das initiale Voll-Backup gelöscht, auf dem die Inkremente beruhen? Wenn dem so wäre, lägen nach mehreren Durchgängen nur noch unvollständige Backups vor. Bitte bring mir da etwas Licht ins Dunkel.

    1. Benutzer Icon

      Danke für deinen Kommentar! Es freut mich, dass das Skript genutzt und angepasst wird 🙂

      Das Voll-Backup wird nicht gelöscht, nur die Möglichkeit im Backuppool zu dem Zeitpunkt des Voll-Backups (nennen wir es snap1) zurück zu springen. Die ZFS-Magie löscht nur das, was wir tatsächlich löschen wollen.
      Aber Achtung: Diese Backup-Variante funktioniert wie ein Mirror in Richtung Originalpool => Backuppool. Nehmen wir an du hast in deinem Originalpool die Datei abc.xyz angelegt und erstellst anschließend den Snapshot snap1. Dieser wird dann mit obigem Skript in den Backuppool gesichert. Irgendwann löschst du die Datei abc.xyz und erstellst danach den Snapshot snap2, welcher wiederum ins Backup gezogen wird. Solange im Backuppool noch snap1 vorhanden ist, kannst du daraus die Datei abc.xyz wieder herstellen. Löschst du jedoch snap1, so ist nur noch ein Rollback zu snap2 möglich, darin war im Originalpool die Datei abc.xyz jedoch nicht mehr vorhanden, also fehlt sie jetzt auch im Backup.
      Wenn du also jederzeit auf alle deine gelöschten Dateien zurückgreifen willst, so darf im Backuppool nichts gelöscht werden.

  11. Benutzer Icon
    Philip Scheiwiller

    Hallo Jörg,
    auch von meiner Seite aus vielen Dank! Hat bei mir soweit alles reibungslos funktioniert.
    Beim überprüfen auf dem Mac mittels OpenZFS ist mir aufgefallen, dass bei einem Dataset nicht zugegriffen werden kann, resp. die Berechtigung fehlen. Ich denke, dass die Berechtigungen vom freenas übernommen wurden und mit dem Mac-Benutzer ja nicht übereinstimmen. Die anderen Datasets funktionieren, da dort die Berechtigung für „nobody“ gelten.
    Habe mich auch mal etwas schlau gemacht und bin zu keinem Ergebnis gekommen, wie der Zugriff ermöglicht werden kann. Hast du allenfalls einen Anhaltspunkt oder einen Tipp dafür? Mir gehts darum, dass ich unabhängig vom OS auf die Daten zugreifen könnte (wenn was schief laufen sollte).

    Danke & Gruß Philip

    1. Benutzer Icon

      Danke für deinen Kommentar! Es freut mich, dass das Skript genutzt und angepasst wird 🙂

      Ja es werden die Berechtigungen von FreeNAS übernommen, wenn diese nicht mit dem Mac-Benutzer übereinstimmen kommst du nur mit root-Rechten weiter. Die schnelle Variante wäre in der Konsole mit su zum Root-Nutzer zu wechseln und dann von da aus die Daten zu kopieren. Anschließend kannst du als root mit dem Befehl chown einen neuen Benutzer zuweisen.
      Eine andere (aus Sicherheitsgründen nicht zu empfehlende) Variante wäre es den Finder als root zu starten, dafür hilft dir deine Suchmaschine des Vertrauens weiter.

  12. Benutzer Icon
    Björn

    Hallo,

    Ich bin auch gerade dabei mir dein Script etwas anzupassen (verschlüsselte USB-HDD, unrekursive backups etc) aber verstehe nicht wie genau das bei dir funktioniert. Wenn ich das richtig sehe wird von jedem dataset ein rekursiver Snapshot erstellt und auch sowohl für die Erkennung des letztes gebackupten Snapshots wie auch zum Löschen alter Snapshots eine rekursive Liste erstellt.
    Wie läuft das denn z.B. beim Löschen? Wenn ich das richtig sehe würde das Script bei mir falsche Snapshots löschen:

    Das wären z.B. meine Snapshots vom „HDDpool/HDD-Share“

    # zfs list -rt snap -H -o name "HDDpool/HDD-Share" | grep "@auto-" | tail -r
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200203.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200202.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200201.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200131.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200130.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200129.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200128.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200203.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200202.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200201.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200131.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200130.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200129.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200128.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200203.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200202.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200201.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200131.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200130.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200129.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200128.0300-1w
    HDDpool/HDD-Share@auto-20200203.0300-1w
    HDDpool/HDD-Share@auto-20200202.0300-1w
    HDDpool/HDD-Share@auto-20200201.0300-1w
    HDDpool/HDD-Share@auto-20200131.0300-1w
    HDDpool/HDD-Share@auto-20200130.0300-1w
    HDDpool/HDD-Share@auto-20200129.0300-1w
    HDDpool/HDD-Share@auto-20200128.0300-1w

    Und dein Löschbefehl gibt das weiter:

    # zfs list -rt snap -H -o name "HDDpool/HDD-Share" | grep "@auto-" | tail -r | tail +2
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200202.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200201.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200131.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200130.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200129.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200128.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200203.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200202.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200201.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200131.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200130.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200129.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200128.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200203.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200202.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200201.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200131.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200130.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200129.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200128.0300-1w
    HDDpool/HDD-Share@auto-20200203.0300-1w
    HDDpool/HDD-Share@auto-20200202.0300-1w
    HDDpool/HDD-Share@auto-20200201.0300-1w
    HDDpool/HDD-Share@auto-20200131.0300-1w
    HDDpool/HDD-Share@auto-20200130.0300-1w
    HDDpool/HDD-Share@auto-20200129.0300-1w
    HDDpool/HDD-Share@auto-20200128.0300-1w

    Da würde dann doch alles an Snapshots außer „HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200203.0300-1w“ gelöscht werden obwohl eigentlich alles mit dem „@auto-20200203.0300-1w“ behalten werden sollte. Die richtige Löschliste wäre dann doch eigentlich folnde:
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200202.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200201.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200131.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200130.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200129.0300-1w
    HDDpool/HDD-Share/LowSec/Stuff_HDD@auto-20200128.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200202.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200201.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200131.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200130.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200129.0300-1w
    HDDpool/HDD-Share/LowSec/Series@auto-20200128.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200202.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200201.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200131.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200130.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200129.0300-1w
    HDDpool/HDD-Share/LowSec@auto-20200128.0300-1w
    HDDpool/HDD-Share@auto-20200202.0300-1w
    HDDpool/HDD-Share@auto-20200201.0300-1w
    HDDpool/HDD-Share@auto-20200131.0300-1w
    HDDpool/HDD-Share@auto-20200130.0300-1w
    HDDpool/HDD-Share@auto-20200129.0300-1w
    HDDpool/HDD-Share@auto-20200128.0300-1w

    Und dann stellt sich mir noch die Frage ob es reicht da einfach nur mit „zfs send -v $NEWSNAP | zfs recv -F „${BACKUPPOOL}/${DATASET}““ das Eltern-Dataset zu senden. Wird werden da dann automatisch alle Unter-Dataset mitgesendet oder müsste man die alle einzeln senden? Das „zfs snapshot -r $NEWSNAP“ erstellt ja für jeden Unter-Datensatz einen eigenen Snapshot.

  13. Benutzer Icon
    Christian

    Hallo,

    ich habe aktuell ein sehr aktues Problem: Mein Pool ist an irgendeiner Stelle corrupted, was ständig zu einem Reboot des Servers führt. Ich kann den Fehler nicht finden. Wenn ich den Pool jedoch readonly mounte läuft der Server stabil, sodass ich Daten sichern könnte. Dein Script mosert nun zurecht, dass es kein Snapshot erstellen kann, da ja readonly. Ginge es irgendwie den Snapshot quasi in-the-fly auf die backupplatte zu erstellen?

    Ich wäre über Hilfe sehr dankbar.

    Gruß Christian

    1. Benutzer Icon

      Soweit ich weiß kann man Snapshots nur direkt auf dem Dateisystem bzw. Volume erstellen, von dem man es erstellt.
      Du kannst jetzt im readonly nur den letzten noch vorhandenen Snapshot per zfs send auf deine Backupplatte schieben. Dann fehlen aber halt die Änderungen seit dem letzten Snapshot. Für solche Fälle hab ich automatische tägliche Snapshots laufen, die nach einer Woche wieder gelöscht werden.
      Aber frag mal in einem Forum mit ZFS-Experten nach wie du noch an deine Daten ran kommen könntest, ich bin kein Guru auf dem Gebiet 😉

  14. Benutzer Icon
    janwels89

    Hallo, Ich habe diseses Skript gefunden und benutze es auf meinen Debian Server mit OpenMediaVault V4. Leider gibt es „tail -r“ nicht auf linux-systemen. Das kann man aber einfach mit „tac“ ersetzen.

    Ich habe den code mal auf github gepostet, damit ihr euch beteiligen könnt, wenn ihr wollt. Ich habe eine Copyright-Hinweiß für Jörg eingefügt. Ich hoffe das geht in Ordnung?

    https://github.com/janwels89/backup-zfs-bash

    1. Benutzer Icon

      Das passt 🙂

  15. Benutzer Icon
    Marcin

    Hallo,

    ich danke Dir für den script.
    Ich habe ein folgendes problem, ich habe ein dataset mit Leerzeichen im name und das kann dieser Skript nicht beherrschen. Hast du eine idee wie ich es losen kann?

    Ein Beispiel:
    Dataset name:“Test Data“
    Der Skript versucht aus „Test Data“ zwei unterschiedliche datasets zu erstellen, „Test“ und „Data“

    Vielen Dank
    Marcin

    1. Benutzer Icon

      Hast du es schon mal versucht das Leerzeichen zu escapen?
      „Test\ Data“
      Der bessere Weg wäre allerdings auf Leerzeichen in Dataset-Bezeichnungen zu verzichten.

      1. Benutzer Icon
        Marcin

        Ja ich habe es auch schon versucht.
        Fehlermeldung lautet: cannot open ‚backup/Test\‘: invalid character ‚\‘ in name.

        Die Lösung ist aber einfach, ich habe Dataset „Test Data“ auf „Test_Data“ umbenannt mit zfs rename Befehl.

  16. Benutzer Icon
    DFFVB

    Das ist ein großartiger Beitrag, der in der Evaluierung von verschiedenen NAS Lösungen, einen wichtigen Haken bei FreeNAS machen lässt, nämlich „Cold BackUp für NAS“. Du erklärst DAU-gerecht und nimmst etwas Berührungsangst vor ZFS. Sehr schön, wie Du auch auf die Wiederherstellung eingehst, denn das ist der zweite Haken. Bspw. hat OMV auch eien BackUp Funktion, wer nach Wiederherstellung sucht, findet nüscht, bzw. wird dann extremes Wissen gefordert (no hate gegen OMV, habe Volker auch schon gespendet).

    Anyway: In den Kommentaren kam ja schon die Kiste mit den verschiedenen Benutzern. Vlt könntest Du den Beitrag noch darum ergänzen?

  17. Benutzer Icon
    Daniel

    Super Anleitung, vielen Dank dafür. Ich versuche gerade das Script zu verstehen, und habe da ein paar Fragen, die mir die Doku nicht beantworten konnte. Über eine Antwort würde ich mich freuen:
    – Wieso ist der Parameter -F bei zfs recv notwendig, bzw. was macht dieser? Laut Doku: „Force a rollback of the file system to the most recent snapshot before performing the receive operation.“ Nach meinem Verständnnis bezieht sich das auf das empfangende DataSet, nur warum muss das sein? Sollte das nicht auf einem konsistenten Stand sein?
    – Die Parameter zum Anlegen des Backup-Pools sind mir nicht klar: Warum werden diese auf „casesensitivity=insensitive“ und „normalization=formD“ gesetzt? Intuitiv hätte ich diese auf sensitive und none gesetzt.
    Danke, Daniel

    1. Benutzer Icon
      Daniel

      Ok, ich habe die Antworten gefunden, zumindest teilweise:
      Die Parameter zum Anlegen scheinen für MacOS zu gelten, bei mir (Linux/FreeBSD) habe ich diese anders gesetzt. Der Parameter -F ist mir noch nicht klar.
      In der Praxis werde ich wohl aber auf https://github.com/jimsalterjrs/sanoid/wiki/Syncoid setzen.

      1. Benutzer Icon

        zfs receive -F wird immer nur dann ausgeführt, wenn auf dem BACKUPPOOL kein passender Snapshot gefunden wurde und ein neues Backup in dem Pool initialisiert werden soll (nach der Sicherheitsabfrage). Durch die Option -F werden dann im BACKUPPOOL alle Snapshots entfernt, die nicht auf der sendenen Seite vorhanden sind. Das ist nur für den Fall relevant, wo schon mal in dem BACKUPPOOL ein gleichnamiges Dataset exisitert, was aber nicht mehr zu den vorhandenen Snapshots auf der sendenden Seite passt.

        1. Benutzer Icon
          Daniel

          Danke für die Erläuterung; jetzt ist es klar 😀
          Noch eine etwas OT-Frage: Hast Du Erfahrungen mit Parametern zur Pool-Erzeugung. Ich würde bis jetzt (für meine Backup-Platte) soetwas in der Art vorsehen:
          zpool create -f -o ashift=12 -O compression=lz4 -O mountpoint=none \
          -O normalization=formD /dev/xxx

          Gibt es hierzu „best practices“, oder Erfahrungen?

          1. Benutzer Icon

            Was die Parameter zur Pool-Erzeugung angeht bin ich momentan raus, dafür mach ich zu wenig ZFS Krempel 😉

  18. Benutzer Icon
    Markus

    Hallo, vielen Dank für die tolle Anleitung. Ich habe das grad mal mit einem kleinen Dataset getestet und es funktioniert soweit alles.

    Nun möchte ich aber meinen kompletten Hauptpool sichern, mit allen Datasets. Wie kann ich automatisch alle Datasets inkludieren? Muss ich dazu einfach das /${DATASET} aus allen Befehlen entfernen?

    Und eine Frage zur Erstellung des Snapshots: Mein Hauptpool hat aktuell 12TB von 15 belegt, die externe Platte fasst 16TB. Werden beim Erstellen des initialen Snapshots im Hauptpool noch mal die 12TB benötigt, die dann rüberkopiert werden auf die externe Platte? Das würde ja nicht passen. Kann man andernfalls den Snapshot direkt auf der externen Platte erstellen lassen? Oder funktioniert das anders?

    1. Benutzer Icon

      Schön, dass es klappt 🙂

      IMHO arbeitet der zfs Befehl immer auf Datasets und nicht auf ganzen Pools. Du müsstest also alle Datasets in dem Array auflisten oder dir stattdessen eine Schleife drum herum skripten, die Stück für Stück alle einzelnen Pools sichert.

      Der Snapshot selber nimmt keinen Speicherplatz weg (auch nicht der Initial-Snapshot) – erst wenn sich die Daten ändern wird nur die Differenz gespeichert, das spart jede Menge Speicherplatz.

  19. Benutzer Icon
    Daniel

    Hallo der B. Ich habe dein tolles Script an meine Bedürfnisse angepasst und würde es gerne auf github laden. Verweis auf Dein Copyright und Deine Seite ist natürlich vorhanden. Ist das ok für dich? Danke & Grüße, Daniel

    1. Benutzer Icon

      Klar, mit Verweis auf diese Seite hier kannst du das machen.
      Viele Grüße, der B.

  20. Benutzer Icon
    Matthias

    Hallo Daniel und Binni,

    habe das Skript unter https://github.com/dapuru/zfsbackup wiedergefunden.

    Vielen Dank für gute Arbeit!

  21. Benutzer Icon
    Banane

    Hallo Jörg, besser als spät als nie:

    saubere arbeit, deine Arbeit hilft mir sehr als Grundlage für mein persönliches Script. Danke dafür !

    Hast du das Script mitlerweile auf Truenas übertragen ?

    Läuft das bei Dir im Root-Kontext, oder hast du es mitlerweile hinbekommen einen separaten User dafür zu erstellen?

    Ich bekomme es (auch mit Delegation etc.) nicht hin, den USB-ZPOOL als nicht-Root zu importieren und exportieren.

    Beste Grüße
    Christian

    1. Benutzer Icon

      Danke, es freut mich wenn der Post weiter hilft.
      Ist TrueNAS nicht nur ein anderes Branding für ehemals FreeNAS? Allgemein sollten die im Script verwendeten Befehle unter jedem FreeBSD Klon laufen.
      Ja, ich führe das Skript als root aus, ist zwar keine super Lösung aber als dann alles lief war mein Elan da weiter dran rum zu basteln erstmal zum Erliegen gekommen 😉

  22. Benutzer Icon
    Felix

    Hallo Jörg,
    Ich bin totaler Anfänger was Shell-Script angeht. Habe es geschafft mit nano und copy&paste dein script als „backup.sh“ anzulegen. Ich kann es auch ausführen – aber dann gibt die Konsole mir einen Fehler aus:

    root@truenas[/mnt/hoard/flxnas]# ./backup.sh
    too many arguments
    usage:
    import [-d dir] [-D] import [-o mntopts] [-o property=value] ...
    [-d dir | -c cachefile] [-D] [-l] [-f] [-m] [-N] [-R root] [-F [-n]]
    -a
    import [-o mntopts] [-o property=value] ...
    [-d dir | -c cachefile] [-D] [-l] [-f] [-m] [-N] [-R root] [-F [-n]]
    [--rewind-to-checkpoint] [newpool]./backup.sh: line 19: testbackup/: No such file or directory
    usage: cut -b list [-n] [file ...]
    cut

    Kannst du mir weiterhelfen?

    Vielen Dank und LG

    1. Benutzer Icon

      Sorry aber ich bin gerade auf Fahrradweltreise unterwegs und habe im Moment nicht die Resourcen für Hacking-Support.
      Schau doch mal in Zeile 19 deines Skripts, das da referenzierte testbackup/ scheint nicht zu existieren 😉

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert