Regelmäßige zfs snapshots

gadean

Depp vom Dienst!
Hey,
mich würde mal interessieren, wie Ihr das handhabt mir regelmäßigen, rollenden zfs Snapshots?

Ich hab gelesen, das einige wohl selbst geschriebene Scripten benutzten, andere sysutils/freebsd-snapshot oder sysutils/zfs-snapshot-mgmt.

Bei den beiden Ports scheint das letzte Update schon etwas länger her zu sein, was man unterschiedlich verstehen kann:
1. Es gibt keine Probleme und deswegen keine Updates
2. Wird nicht mehr aktiv maintained
3. Es gibt andere/bessere(?) Lösungen die benutzt werden

Bevor ich mir da jetzt etwas selbst bastel oder etwas totes installiere, würde ich gerne wissen wie Ihr damit umgeht?

Am Ende möchte ich eigentlich nur wöchentliche, rollende Snapshots (zb. die letzten 8 Wochen) von ausgewählten Datasets.
 
Für manches nutze ich eigene Scripte, ansonsten sanoid (https://github.com/jimsalterjrs/sanoid). Das Projekt lebt auf jedenfall und der Port ist auch auf der aktuellsten Version, sucht aber wohl Maintainer. Für deinen Anwendungsfall auf jedenfall geeignet, eventuell bisschen Overkill wenn die Regeln nicht allzu kompliziert sind.
 
Am Ende möchte ich eigentlich nur wöchentliche, rollende Snapshots (zb. die letzten 8 Wochen) von ausgewählten Datasets.
Ein simpler Ansatz wäre einfach ein CRON-Job zu benutzen, der einmal die Woche aufgerufen wird. Der CRON-Job könnte aus einem einfachen Skript bestehen, welches automatisiert Snapshots mit dem Datum als Name nimmt. Das hat den Vorteil, das man das einfach sortieren kann und dann behält man einfach nur die acht obersten Snapshots und den Rest löscht man.

Das Skript könnte in etwa so aussehen:

Bash:
#!/bin/sh

# Anzahl der zu behaltenden Snapshots
keep_snapshots=8

# Liste der ausgewählten Datasets
datasets="pool/dataset1 pool/dataset2"

for dataset in $datasets
do
    # Erstelle einen neuen ZFS-Snapshot mit einem Namen a-la
    # pool/dataset1@2024-06-27-01-30
    snapshot_name=$(date +"%Y-%m-%d-%H-%M")
    if ! zfs snapshot "${dataset}@${snapshot_name}"; then
        echo "Fehler: Snapshot konnte nicht erstellt werden für ${dataset}@${snapshot_name}" >&2
        exit 1 # Beenden, wenn kein neuer Snapshot erstellt werden konnte
    fi

    # Wieviel Snapshots haben wir?
    snapshot_count=$(zfs list -t snapshot -H -o name -r "${dataset}" | wc -l)
 
    # Sind überhaupt mehr Snapshots als 'keep_snapshots' da?
    if [ $snapshot_count -gt $keep_snapshots ]; then

        # Anzahl der zu löschenden Snapshots:
        snapshots_to_destroy_count=$((snapshot_count - keep_snapshots))

        # Liste der Namen der zu löschenden Snapshots:
        snapshots_to_destroy=$(zfs list -t snapshot -H -o name -r "${dataset}" | sort | head -n $snapshots_to_destroy_count)

        echo "Folgende Snapshots werden gelöscht für Dataset ${dataset}:"
        echo "$snapshots_to_destroy"
        echo "$snapshots_to_destroy" | xargs -n 1 zfs destroy -r # !! Hier wird dann wirklich die Snapshots gelöscht
    fi
done

Das ist übrigens ein POSIX-Shell-Skript und funktioniert auch ohne installierte Bash. Das da Bash steht, ist nur für die Syntaxhervorhebung.

Könnte man auch noch abändern, das das Datum im Snapshot-Namen ausgewerten wird, um mit dem aktuellen Datum zu vergleichen. Damit nicht ältere Snapshots beim häufigeren Aufruf verfrüht gelöscht werden, obwohl noch keine 8 Wochen (oder was für ne Zeit auch immer) rum sind.
 
Ich habe jahrelang sysutils/zfsnap2 verwendet und war damit immer sehr zufrieden - es ist leicht zu verwenden und die Snapshots sind gut leserlich. Für das Wegsicher hatte ich dann zxfer verwendet. Das ist aber mittlerweile recht veraltet und unterstützt nicht mehr alles, was ich benötige.

Daher benutze ich jetzt immer die sanoid + syncoid Kombi, die im selben Port enthalten sind und aktiv weiter entwickelt zu werden scheinen.
 
Super, danke für den Input.

Mit sanoid sollte das relativ einfach zu Lösen sein, hat aber wohl "mehr" Abhängigkeiten
Code:
//...
frequently = 0
weekly = 8
// ...

zfsnap2 sieht eigentlich auch ganz nett aus, aber scheint wohl etwas eingeschlafen zu sein - beim Release :(

Denke ich werde mal mit sanoid ein wenig experimentieren.

@Andy_m4 Danke für dein Script, das wäre natürlich die schmalste Lösung, nur habe ich bedenken mir selbst in den Fuß zu schießen. Shell Scripting ist jetzt nicht unbedingt meine Spezialität :D
 
Shell Scripting ist jetzt nicht unbedingt meine Spezialität
Ja. Ich habe es versucht ausreichend zu kommentieren und sprechende Variablennamen zu nehmen. :-)
Und man kann ja, das ist ja das schöne an Shellskripten, sich auch einzelne Teile rausnehmen und so ausführen und gucken was passiert.
Die einzigen "problematischen" Aufrufe (weil die etwas ändern) sind die von zfs create und zfs destroy. Alles andere gibt ja letztlich nur Information aus.

mir selbst in den Fuß zu schießen
Das Skript hat auch ein paar potentielle Probleme.

So benutzt es als Trenner Leerzeichen. Das wird dann zum Problem, wenn z.B. in Dataset-Namen Leerzeichen vorkommen. Das kann man vermeiden, in dem man echte Listen einsetzt. Allerdings kennt POSIX-Shell keine echten Listen, soweit ich weiß. Man müsste dann auf die bash zurückgreifen oder auf die csh, falls man partout keine externen Abhängigkeiten haben möchte.
Auch bei Snapshot-Namen hat man ähnliche Probleme. Wenn da Snapshots drin sind, die Leerzeichen im Namen haben haben oder sich nicht an das Namensschema halten, funktioniert das Skript auch nicht so wie erwartet.

Das zweite potentielle Problem ist, das sich das Skript auf die Ausgaben der benutzten Programme verlässt. Ändern die sich in neuen Versionen, dann kann das zum Problem werden. Üblicherweise ändert sich das Ausgabeformat aber nicht oder nur sehr selten, weil die natürlich wissen, das ihre Tools in Skripten verwendet werden. Und potentielle "Lokalitätsprobleme" sind in dem Skript auch nicht drin. Von daher wird das wohl eher nicht zum Stolperstein werden.

Und dann halt das Problem, welches ich schon angesprochen habe. Nämlich das der Zeitfaktor keine Rolle spielt, sondern stumpf nur die neusten 8 Snapshots behalten werden. Egal, ob die ne Woche alt sind oder ein Jahr.
Das ließe sich aber beheben, falls dies gewünscht ist.

Es gibt noch mehr "Cons". Das Skript richtet sich sehr danach, was Du jetzt haben willst. Wenn irgendwann mal größere Änderungen da sein sollen (was so über "welche Datasets" und "wieviele snapshots möchte ich behalten), dann geht das nicht, ohne das Skript anzupassen. Das Problem hast Du bei den Fertiglösungen nicht.

Das Skript hat aber auch ein paar "Pros". :-)
Es hat keine externen Abhängigkeiten. Es ist simpel. Das macht es übersichtlich. Und der Pflegebedarf (sprich: Bedarf an Updates) dürfte sich in Grenzen halten. Die wesentlichen potentiellen Bugs sind bekannt.
Die Fertiglösungen sind komplexer. Umso komplexer eine Software ist, umso größer ist die Wahrscheinlichkeit das da Bugs drin sind. Und selbst wenn man darauf aufmerksam wird, ist das Fixen schwieriger, da man sich da erst einarbeiten muss.
 
Das kann man vermeiden, in dem man echte Listen einsetzt. Allerdings kennt POSIX-Shell keine echten Listen
ob Posix-Konform, weiß ich nicht, glaube aber, dass der
IFS
das Problem mit den Leerzeichen im Namen auch für die csh lösen kann
Das habe ich aus Scripten von @Kamikaze entnommen und nicht weiter nachgelesen. IFS ist Input Field Seperator und wenn der etwa so aussieht:
Code:
IFS='
'
lauscht er nur auf Zeilenumbrüche und nicht auf Leerzeichen.

Wenn ich nun ganz daneben liege, habe ich etwas vorher falsch verstanden und bitte den Beitrag dann einfach zu ignorieren.
 
ob Posix-Konform, weiß ich nicht, glaube aber, dass der
IFS
das Problem mit den Leerzeichen im Namen
Du hast völlig Recht. Man kann die Variable IFS benutzen und damit ein Trennzeichen definieren:
Code:
datasets="pool/data set1;pool/dataset2"
IFS=';'
Wenn man also Kollision mit dem Namen hat, sucht man sich einfach das passende Trennzeichen.
Entbindet einem aber immer noch nicht davon aufzupassen, was in den Namen vor kommt. Man könnte natürlich ein Zeichen nehmen, welches in Dataset-Namen oder Snapshot-Namen nicht vorkommen darf (Doppelpunkt).

Bei csh oder bash ist es sowieso unkritisch. Die beiden Shells kennen Arrays, wodurch man gar kein Trennzeichen mehr braucht.
 
So schmerzverliebt, dass man csh-Scripte schreibt, kann man doch gar nicht sein :) So als Faustregel tut man gut daran, wenn man mit der POSIX-kompatiblen Bource-Shell nicht mehr weiterkommt, gleich Python oder eine andere richtige Scriptsprache der Wahl zu nehmen. Denn da bekommt man auch gleich Dinge wie belastbare Fehlerbehandlungen, robuste Bibliotheken mit allen erdenklichen Funktionen und so weiter.
 
gleich Python oder eine andere richtige Scriptsprache der Wahl zu nehmen. Denn da bekommt man auch gleich Dinge wie belastbare Fehlerbehandlungen, robuste Bibliotheken mit allen erdenklichen Funktionen und so weiter.
Dem kann ich nur zustimmen und ist auch dringend zu empfehlen, wenn nicht irgendwas entgegensteht.

dass man csh-Scripte schreibt, kann man doch gar nicht sein
Heutzutage sowieso nicht mehr.
 
Ok, der Ehrgeiz hat mich eingeholt :D Ich musste es einfach mal probieren und es funktioniert
Code:
#!/bin/sh
set -e

if [ `id -u` -ne 0 ]; then
    echo "Please run this as root"
    exit
fi

KEEP_SNAPSHOTS=8
DATASETS="zroot/data/test1 zroot/data/test2"
PREFIX="weekly"
NOW=$(date +%Y-%m-%d-%H-%M-%S) # <- ist absichtlich nicht im loop

for dataset in $DATASETS
do
    echo "Processing \"${dataset}\""
    echo "  Creating \"@${PREFIX}_${NOW}\""
    zfs snapshot "${dataset}@${PREFIX}_${NOW}"

    snapshots_to_destroy=$(zfs list -t snapshot -H -o name -S creation "${dataset}" | grep "@${PREFIX}_" | tail -n +$((KEEP_SNAPSHOTS+1)) | sort)
    for snapshot_to_destroy in $snapshots_to_destroy
    do
        echo "  Removing \"${snapshot_to_destroy##$dataset}\""
        zfs destroy "${snapshot_to_destroy}"
    done
done

Code:
# ./snapshot_test
Processing "zroot/data/test1"
  Creating "@weekly_2024-06-28-21-50-00"
  Removing "@weekly_2024-06-28-21-39-27"
Processing "zroot/data/test2"
  Creating "@weekly_2024-06-28-21-50-00"
  Removing "@weekly_2024-06-28-21-39-27"

zfs list -t snapshot -H -o name -S creation "${dataset}" | grep "@${PREFIX}_"
Liste der Snapshots, sortiert nach creation und dann auf ein Prefix gefiltert.
(Erstelle gelegentlich auch von Hand Snapshots die ich behalten möchte)

tail -n +$((KEEP_SNAPSHOTS+1))
Umgekehrtes tail um die Snapshots zu bekommen, die entfernt werden können

sort um die Liste umzudrehen.
Mein Gedanke war, das es effizienter ist die Snapshots in umgekehrter Reihenfolge zu löschen (von "ältest" zu "weniger alt"),

PS:
Wie findet Ihr eigentlich die passenden Dokumentationen zu sh (Beispiel: string substitution)?
Wenn man nach sh ... sucht, findet man überwiegend Sachen zu bash, was aber nicht immer funktioniert.

@Yamagi Wohl wahr, aber mit Python stehe ich aufem Kriegsfuß :D
 
Zuletzt bearbeitet:
Ok, der Ehrgeiz hat mich eingeholt :D
Das ist Dein Problem und damit musst Du alleine klarkommen. ;-)

Mein Gedanke war, das es effizienter ist die Snapshots in umgekehrter Reihenfolge zu löschen (von "ältest" zu "weniger alt"),
Der Gedanke ist nicht verkehrt.

Wie findet Ihr eigentlich die passenden Dokumentationen zu sh ?
Die erste Anlaufstelle ist natürlich die Manpage von sh. :-)

Beispiel: string substitution
Ich bin mir nicht sicher, was Du meinst. Aber wenn Du dieses ${...} - Gedöns meinst, dann fällt das eher unter den Begriff Parameter expansion.
Wenn Du also 'im Netz' nach "POSIX Shell parameter expansion" suchst, solltest Du eigentlich fündig werden.
Und auch die o.g. Manpage hat ein Abschnitt dazu.

aber mit Python stehe ich aufem Kriegsfuß
(Fast) jede andere Skriptsprache tut es ja auch. Python war ja nur exemplarisch gemeint, weils weit verbreitet ist. Der Punkt ist ja der, das solche Skriptsprachen mehr Komfort bieten als wenn man mit den üblichen Shells herumskriptet.

Allerdings setzt das auch voraus, das man ne Skriptsprache auch einigermaßen beherrscht. Die Attraktivität von Shell-Skripten liegt darin, das man ja im Groben ja quasi einfach nur das aufschreiben muss, was man in der Kommandozeile machen würde. Klar. Sowas wie Parameter Expansion, Schleifen etc. ist ein bisschen Special aber der Rest ist ja das, was man von seiner täglichen Arbeit kennt.

findet man überwiegend Sachen zu bash
Wobei man auch überlegen könnte auch gleich sowas wie bash zu nutzen. Also wenn man eh schon sich entschlossen hat statt einer Skriptsprache mit einer Shell zu skripten. Denn ein bisschen mehr Komfort bringt sowas wie die bash ja schon mir. Von der Doku-Situation ganz abgesehen.

Den einzigen Pluspunkt den POSIX-Shell hat ist, das es halt immer funktioniert (also zumindest bezogen auf den built-in Kram und man hat dann immer noch Abhängigkeit von den Programmen die man darin aufruft).
Ich schreib ja auch nicht oft Shell-Skripte, weil ich auch "richtige" Programmiersprachen für komfortabler halte. Shell-Skripte benutze ich nur bei ganz einfachen Sachen. Und da ist es dann tatsächlich so, das dieses zero-dependency-Dingens dann bei mir der Grund ist warum ich POSIX nehme und nicht etwa irgendwie bash, zsh oder was es da sonst noch so gibt. Zumal ja die bash den POSIX-Kram inkludiert und damit im Prinzip jedes POSIX-Shell-Skripte auch immer ein valides bash-Skript ist.
 
Zurück
Oben