Inkrementelle Backups mit GNU tar erstellen und wiederherstellen
Erstellen Sie mit der Option --listed-incremental von GNU tar ein vollständiges Backup
und anschließend Archive der Änderungen. Diese Anleitung führt Sie durch eine lokale Backup-Kette
und einen Wiederherstellungstest, der Dateiinhalte prüft, auch wenn Sie Dateien hinzufügen,
ändern oder löschen. Jedes Backup erhält eine eigene Snapshot-Kopie. So kann ein fehlgeschlagener
Lauf den Stand des letzten erfolgreichen Backups nicht fortschreiben.
Eine kleine Backup-Quelle einrichten
Verwenden Sie Linux, Bash, GNU tar, gzip sowie die Standardwerkzeuge
diff und die üblichen Dateiwerkzeuge. Die Beispiele wurden mit GNU tar 1.35
und Bash 5 getestet. Prüfen Sie, ob tar --version GNU tar ausweist. Inkrementelle
GNU-Archive nutzen Erweiterungen, die andere tar-Implementierungen möglicherweise nicht unterstützen.
Weitere Informationen finden Sie im
GNU-tar-Handbuch zu inkrementellen Backups.
tar --version
bash --version
gzip --version
Führen Sie die folgenden Blöcke in Bash aus, jeweils vom selben Ausgangsverzeichnis aus. Die Klammern
begrenzen Verzeichniswechsel und Shell-Optionen auf den jeweiligen Block. Die Einrichtung verweigert
die Wiederverwendung eines vorhandenen Verzeichnisses tar-demo.
(
set -euo pipefail
mkdir tar-demo
cd -- tar-demo
mkdir -p project/cache backups
printf 'version one\n' > project/notes.txt
printf 'keep this file\n' > project/unchanged.txt
printf 'remove this file later\n' > project/obsolete.txt
printf 'rebuildable cache\n' > project/cache/item.txt
)
Halten Sie backups wie hier gezeigt außerhalb des Quellverzeichnisbaums,
damit Archive nicht ihre eigene Ausgabe aufnehmen können. Verwenden Sie eine Quelle, in die während
des Backups nicht geschrieben wird. Diese Übung behandelt gewöhnliche Dateien in einem lokalen
Verzeichnis. Sie erstellt weder ein konsistentes Backup einer laufenden Datenbank noch bewahrt sie
jede Art von Systemmetadaten.
Jedes Backup mit seinem Snapshot speichern
Die Snapshot-Datei hält fest, was tar beim vorherigen Backup erfasst hat. Fehlt ein Snapshot, beginnt ein vollständiges Backup, auch Level-null-Backup genannt. Bei Wiederverwendung eines Snapshots werden die Änderungen seit diesem Stand erfasst, und tar aktualisiert ihn während der Erstellung. Kopieren Sie den vorherigen Snapshot, bevor Sie das nächste Archiv erstellen. Lassen Sie die vorherige Kopie unverändert.
Speichern Sie dieses Skript als tar-demo/backup.sh. Das erste Argument benennt ein neues
Backup-Verzeichnis. Das optionale zweite Argument benennt das vorherige erfolgreiche Backup.
Namen dürfen ASCII-Buchstaben, Ziffern, Unterstriche und Bindestriche enthalten.
#!/usr/bin/env bash
set -euo pipefail
unset TAR_OPTIONS
cd -- "$(dirname -- "$0")"
if (( $# < 1 || $# > 2 )); then
printf 'Usage: bash backup.sh NAME [PREVIOUS]\n' >&2
exit 2
fi
for name in "$@"; do
case "$name" in
''|*[!a-zA-Z0-9_-]*)
printf 'Invalid backup name: %s\n' "$name" >&2
exit 2
;;
esac
done
output="backups/$1"
previous=${2:-}
if [[ -n "$previous" ]]; then
for file in complete archive.tar.gz state.snar; do
if [[ ! -f "backups/$previous/$file" ]]; then
printf 'Previous backup is incomplete: %s\n' "$previous" >&2
exit 1
fi
done
fi
# Reserve a new directory before installing cleanup that can remove it.
mkdir -- "$output"
trap 'rm -rf -- "$output"' EXIT
trap 'exit 1' HUP INT TERM
if [[ -n "$previous" ]]; then
cp -- "backups/$previous/state.snar" "$output/state.snar"
fi
tar --create --file=- \
--listed-incremental="$output/state.snar" \
--exclude='project/cache' \
-- project | gzip > "$output/archive.tar.gz"
gzip --test -- "$output/archive.tar.gz"
touch -- "$output/complete"
trap - EXIT
printf 'Created %s\n' "$output"
Führen Sie es einmal ohne Vorgänger aus:
bash tar-demo/backup.sh 00-full
Dadurch werden backups/00-full/archive.tar.gz, state.snar und eine leere
Markierungsdatei complete in tar-demo erstellt. Die
Markierung hält fest, dass die Archiverstellung und die gzip-Prüfung abgeschlossen wurden. Sie
ersetzt keinen Wiederherstellungstest. Bei einem bereits vorhandenen Ausgabeverzeichnis bricht das
Skript ab, bevor es dessen Inhalt verändert, auch bei nicht interaktiver Eingabe. Führen Sie immer
nur ein Backup gleichzeitig aus.
Ausschlüsse konsistent halten
Das in Anführungszeichen gesetzte --exclude='project/cache' schließt dieses Verzeichnis samt
Inhalt aus. Behalten Sie Quellpfad und Ausschlüsse innerhalb einer Kette unverändert bei. Wenn Sie
ändern, was ins Backup gehört, beginnen Sie ein neues vollständiges Backup unter einem neuen Namen.
Die Auswahl kürzlich geänderter Dateien mit find -mtime erfasst gelöschte
Verzeichniseinträge nicht wie dieser Ablauf. Lassen Sie tar das Quellverzeichnis durchlaufen.
Gzip komprimiert jedes Archiv unabhängig. Es entscheidet nicht, welche Dateien geändert wurden. Einen gesonderten Überblick über Komprimierungsoptionen finden Sie unter tar mit Zstd und LZ4.
Änderungen der Reihe nach erfassen
Ändern Sie eine Datei, fügen Sie eine weitere hinzu und löschen Sie die nicht mehr benötigte Datei. Die kurze Pause sorgt dafür, dass sich die Zeitstempel dieses kleinen Beispiels auch auf Dateisystemen mit grober Zeitauflösung unterscheiden. Die inkrementelle Auswahl von GNU tar hängt von Zeitstempeln ab. Stellen Sie die Uhr daher während einer Kette nicht zurück.
(
set -euo pipefail
cd -- tar-demo
sleep 1
printf 'version two\n' > project/notes.txt
printf 'new file\n' > project/added.txt
rm -- project/obsolete.txt
bash backup.sh 01-change 00-full
)
Prüfen Sie das Archiv der geänderten Dateien:
tar --list --gzip --file=tar-demo/backups/01-change/archive.tar.gz
Es enthält project/, project/notes.txt und
project/added.txt; die Reihenfolge kann variieren. Der Verzeichniseintrag enthält zudem
inkrementelle Metadaten. unchanged.txt hängt weiterhin vom vollständigen Backup ab,
während die Löschung von obsolete.txt beim inkrementellen Entpacken umgesetzt wird.
Nehmen Sie eine weitere Änderung vor und verwenden Sie 01-change als Vorgänger:
(
set -euo pipefail
cd -- tar-demo
sleep 1
printf 'version three\n' > project/notes.txt
bash backup.sh 02-change 01-change
)
Die resultierende Kette lautet 00-full → 01-change → 02-change. Um den endgültigen Stand zu erreichen,
benötigen Sie alle drei Archive. Wenn Sie erneut 00-full als Vorgänger wählen,
entsteht ein weiteres Backup relativ zu diesem vollständigen Backup, statt diese Folge zu erweitern.
Die vollständige Kette in einem leeren Verzeichnis wiederherstellen
Beim inkrementellen Entpacken kann GNU tar Dateien löschen, die in einem archivierten Verzeichnis
nicht vorhanden waren. Stellen Sie nur eigene vertrauenswürdige Archive in einem neuen Ziel wieder
her, niemals über Ihrer aktiven Quelle. Verwenden Sie --listed-incremental=/dev/null bei jedem
Entpacken, auch beim vollständigen Backup. Beim Entpacken wird der Verzeichnisverlauf aus den
Archiven gelesen; die gespeicherten Dateien .snar werden nicht benötigt.
(
set -euo pipefail
cd -- tar-demo
mkdir restore
for generation in 00-full 01-change 02-change; do
test -f "backups/$generation/complete"
gzip --test -- "backups/$generation/archive.tar.gz"
tar --extract --gzip --listed-incremental=/dev/null \
--file="backups/$generation/archive.tar.gz" --directory=restore
done
diff --recursive --exclude=cache project restore/project
test ! -e restore/project/obsolete.txt
printf 'Restored files match the source, excluding cache.\n'
)
Bei Erfolg gibt diff nichts aus, und die letzte Zeile bestätigt den
Vergleich. Die wiederhergestellte Datei notes.txt enthält
version three, added.txt enthält
new file, unchanged.txt bleibt erhalten, und
obsolete.txt ist nicht mehr vorhanden. Es wird kein Verzeichnis
cache wiederhergestellt. Verglichen werden Dateinamen und Inhalte, nicht
Eigentümer, Berechtigungen oder erweiterte Attribute.
Bei erneuter Ausführung wird ein vorhandenes Verzeichnis restore abgelehnt,
bevor etwas entpackt wird. Nach einer fehlgeschlagenen Wiederherstellung kann das Verzeichnis
Teilergebnisse enthalten. Prüfen Sie den Fehler und wählen Sie für den nächsten Test ein neues,
leeres Ziel. Betrachten Sie eine teilweise Wiederherstellung nicht als wiederhergestellten
Datenbestand.
Unvollständige Backups zurückweisen
Ein erfolgreiches Komprimierungsprogramm beweist nicht, dass das Programm, das seine Eingabe
liefert, erfolgreich war. Die Bash-Option pipefail
lässt die Pipeline bei einem Fehler in tar | gzip fehlschlagen, selbst wenn gzip
die Komprimierung eines unvollständigen Archivs erfolgreich abschließt. Das Skript entfernt dann
nur das neue Verzeichnis, das es reserviert hat. Das vorherige Archiv und der Snapshot bleiben
unverändert. Versuchen Sie es nach Behebung des Problems mit demselben Vorgänger erneut.
Fügen Sie diesem Skript nicht --ignore-failed-read hinzu. Laut GNU-tar-Dokumentation
bewirkt diese Option, dass fehlende oder unlesbare Eingaben sowie Dateien, die sich während des
Lesens ändern, den Exit-Status nicht mehr beeinflussen. Das ist das Gegenteil davon, ein
unvollständiges Backup zu erkennen. Siehe
--ignore-failed-read.
Lassen Sie Diagnosemeldungen sichtbar und weisen Sie jeden Exit-Status ungleich null zurück,
einschließlich Status 1 für Dateien, die sich während der Erstellung geändert haben. Stoppen Sie
den schreibenden Prozess oder verwenden Sie einen geeigneten Dateisystem-Snapshot, bevor Sie das
Backup einer sich ändernden Quelle erneut versuchen.
tar --list prüft, ob tar die Archivstruktur lesen kann. Die Header-Prüfsummen von
tar decken keine Dateiinhalte ab.
gzip --test
prüft die Integrität des komprimierten Datenstroms, kann aber nicht erkennen, ob eine Eingabedatei
ausgelassen wurde. Deshalb prüft dieser Ablauf Befehlsfehler und stellt das Ergebnis zum
Vergleich wieder her. Die
GNU-tar-Hinweise zu Datenbeschädigungen
erläutern diesen Unterschied.
Vollständige Backup-Ketten zusammenhalten
Bewahren Sie in jedem erfolgreichen Backup-Verzeichnis Archiv und Snapshot zusammen auf. Das
Archiv wird für die Wiederherstellung benötigt, der Snapshot zum Fortsetzen der Kette. Ein
Stromausfall oder SIGKILL kann die Trap zur Bereinigung umgehen und ein
Verzeichnis ohne complete hinterlassen. Sobald kein Backup-Prozess mehr läuft,
prüfen Sie dieses unvollständige Verzeichnis und entfernen Sie es vor einem erneuten Versuch.
Verwenden Sie es nicht als Vorgänger.
Diese lokalen Kopien schützen nicht vor dem Verlust des Datenträgers. Kopieren Sie abgeschlossene Backup-Verzeichnisse auf einen separaten Speicher und wiederholen Sie den Wiederherstellungstest mit diesen Kopien. Dieses Beispiel implementiert weder eine SSH-Übertragung noch die Aufteilung von Archiven. Wenn ein Speichersystem ein Archiv aufteilt, müssen alle Teile vor der gzip-Prüfung und der Wiederherstellung wieder zusammengefügt werden.
Bewahren Sie das vollständige Backup und jedes inkrementelle Archiv auf, das für einen aufbewahrten Wiederherstellungspunkt benötigt wird. Das Löschen einzelner Archive nach Alter kann diese Kette unterbrechen. Beginnen Sie regelmäßig ein neues vollständiges Backup, testen Sie dessen Wiederherstellungsablauf und entfernen Sie ältere Ketten gemäß Ihrer Aufbewahrungsrichtlinie als Ganzes.
