Snapshots per rsync: Unterschied zwischen den Versionen
Rabe (Diskussion | Beiträge) Script und Funktionsweise. |
Rabe (Diskussion | Beiträge) K Katfix |
||
| Zeile 139: | Zeile 139: | ||
[[Kategorie:Script]] | [[Kategorie:Script]] | ||
[[Kategorie:Backup]] | [[Kategorie:Backup]] | ||
[[Kategorie:Anwendungsbeispiel]] | |||
Aktuelle Version vom 15. Juli 2007, 13:22 Uhr
Dieses Script wird verwendet, um regelmäßige Snapshots kompletter Subdirectories zu archivieren. Dabei verwendet es im Kern die beiden Tools rsync und GNU copy (unter Linux bekannt als cp).
Funktionsweise[Bearbeiten]
Beim ersten Aufruf wird eine Urkopie (snap_0)erstellt (Zeile 67-71), diese wird per rsync vom Quellverzeichnis aus geschrieben (Zeile 77). War der rsync erfolgreich, wird das Verzeichnis work_* in snap_* umbenannt (Zeile 77-85).
Bei allen weiteren Aufrufen werden dann lediglich Deltas erzeugt, indem vom jüngsten snap_*-Verzeichnis per GNU copy zunächst eine Hardlink-Kopie erzeugt wird . Dabei wird lediglich die Verzeichnisstruktur real kopiert, alle normalen Dateien werden als Hardlink erstellt und nehmen daher keinen weiteren Platz in Anspruch. work_1 enthält nun eine exakte Kopie von snap_0 (Zeile 63-66)
Es folgt ein Abgleich per rsync mit dem Quellverzeichnis (Zeile 77). Dabei werden alle Dateien gelöscht und neu geschrieben, die sich geändert haben. Hat eine Datei mehr als eine Referenz, so wird beim Löschen lediglich diese Referenz entfernt, d.h. in den vorhergehenden Snapshots bleibt diese Datei erhalten. Ist der rsync erfolgreich, wird work_1 in snap_1 umbenannt (Zeile 77-80).
Schlägt ein rsync-Aufruf fehl, so bleibt die unvollständige oder fehlerhafte Kopie als work_* erhalten, das darauffolgende Delta arbeitet immer auf dem zuletzt erfolgreichen snap_*. Gründe für einen fehlgeschlagenen rsync sind beispielsweise Dateien, die zwischen dem Generieren der Dateiliste und dem Kopieren verschwinden (vanished).
Denkbar wäre, die Zeile 77 zu ersetzen:
77 if /usr/local/bin/rsync -aHW --delete --delete-before "${DIR}/" "${WORKDIR}"; then
nach
77 RSYNCCMD="/usr/local/bin/rsync -aHW --delete --delete-before ${DIR}/ ${WORKDIR}"
78 if ${RSYNCCMD} || ${RSYNCCMD}; then
Dadurch wird der rsync im Fehlerfall 2x ausgeführt. Der zweite rsync sollte damit auch deutlich schneller durchlaufen und damit die Zeit zwischen der Erstellung des Katalogs und dem tatsächlichen Kopieren der Dateien drastisch verkürzen, was die Fehlerwahrscheinlichkeit idR senkt.
Schlägt rsync immer fehl, kann dieses jedoch toleriert werden, so kann die Zeile 77 einfach durch || true ergänzt werden, damit der if-Block ausgeführt wird.
Im Beispiel wurden die Verzeichnisse snap_1 genannt, im realen betrieb werden hier timestamps verwendet, siehe Zeile 28-32.
Anwendung[Bearbeiten]
Anders als bei einer ausgewachsenen Versionsverwaltung müssen die Daten nicht explizit in einem Repository angemeldet werden, die Dateien "wissen" nichts von snapshots. Dieses Verfahren eignet sich beispielsweise dafür, statische Binärdaten wie etwa Multimedia-Dateien etc. zu versionieren.
Bearbeitet man beispielsweise ein Bild und speichert es unter gleichem Namen wieder ab, geht die alte Version im Quellverzeichnis erstmal verloren. Im snapshot-Verzeichnis jedoch bleibt die alte Version erhalten.
Verzeichnisse, die überwiegend wachsende Dateien enthalten, wie etwa Logfiles, sind nur begrenzt sinnvoll für die snapshots, da diese Dateien bei JEDEM Durchlauf komplett neu kopiert werden. Dabei entstehen zwar funktionierende Snapshots, jedoch werden auch geringe Änderungen mit dem vollen Speicherplatz archiviert, keine Deltas.
Script[Bearbeiten]
1 #! /bin/sh
2 #
3 # Verzeichnis-Snapshots erstellen mit gnu-copy(hardlinks) und rsync
4 #
5 #
6 #
7 # Aufruf:
8 # $0 /path/to/dir/
9 #
10 # --> /data/snapshot/path_to_dir/<timestamp>/...
11
12
13 SNAPSHOTHOME=/data/snapshot
14
15 # Symlinks etc auflösen, benötigen den absoluten pfad
16 abspath()
17 {
18 (cd $1 && pwd -P) 2>/dev/null
19 }
20
21 # Zielverzeichnis ermitteln
22 snapdir()
23 {
24 echo -n "${SNAPSHOTHOME}/"
25 echo "$1"| tr '/ ' '__'
26 }
27
28 # Zeitstempel ermitteln
29 timestamp()
30 {
31 date +%Y%m%d%H%M%S
32 }
33
34 # jüngsten (vollständigen) snapshot in $SNAPDIR ermitteln
35 getlatest()
36 {
37 ls -1dr "$SNAPDIR"/snap_20*/ | head -n 1 2>/dev/null
38 }
39
40 if [ $# -ge 1 ]; then
41 while [ -n "$1" ]; do
42 DIR="$(abspath "$1")"
43 if [ -d "${DIR}" ]; then
44 SNAPDIR=$(snapdir "${DIR}")
45 echo "$0: Bearbeite ${DIR} ... "
46
47 # Anlegen, falls nicht vorhanden
48 if [ ! -d "${SNAPDIR}" ]; then
49 mkdir -p "${SNAPDIR}" 2>/dev/null
50 fi
51
52 # Weitermachen, falls jetzt vorhanden
53 if [ -d "${SNAPDIR}" ]; then
54 echo "$0: Snapshot von $DIR nach $SNAPDIR"
55 TS=$(timestamp)
56 LATESTSNAP="$(getlatest)"
57 WORKDIR="${SNAPDIR}/work_${TS}"
58 FINISHDIR="${SNAPDIR}/snap_${TS}"
59
60 # jüngsten snapshot ermitteln
61 # falls vorhanden mit GNU-copy als hardlink-Kopie anlegen
62 # oder leeres Verzeichnis erstellen
63 if [ -n "${LATESTSNAP}" ]; then
64 # Alter snapshot besteht, gcp-hardlink erzeugen
65 echo "$0: Hardlink-Kopie ${LATESTSNAP} --> ${WORKDIR} erzeugen (gcp)"
66 /usr/local/bin/gcp -al "${LATESTSNAP}" "${WORKDIR}"
67 else
68 # Kein Alter snapshot vorhanden, neu (leer) anlegen
69 echo "$0: Leeres ${WORKDIR} anlegen ..."
70 mkdir "${WORKDIR}"
71 fi
72
73 # Quellverzeichnis ins Arbeitsverzeichnis rsync'en
74 # DIR -> WORKDIR
75 if [ -d "${WORKDIR}" ]; then
76 echo "$0: rsync ${DIR}/ -> ${WORKDIR} ..."
77 if /usr/local/bin/rsync -aHW --delete --delete-before "${DIR}/" "${WORKDIR}"; then
78 if mv "${WORKDIR}" "${FINISHDIR}" ; then
79 echo "$0: ${DIR} --> ${FINISHDIR} abgeschlossen. OK."
80 else
81 echo "$0: Fehler beim Abschließen: "${WORKDIR}" --> ${FINISHDIR}" >&2
82 fi
83 else
84 echo "$0; Fehler bei rsync ${DIR}/ --> ${WORKDIR}" >&2
85 fi
86 else
87 echo "$0: ${WORKDIR} nicht gefunden, konnte nicht erstellt werden" >&2
88 fi
89 else
90 echo "$0: $SNAPDIR nicht gefunden, konnte nicht angelegt werden oder kein Verzeichnis" >&2
91 fi
92 else
93 echo "$0: $1 nicht gefunden" >&2
94 fi
95 shift
96 done
97 else
98 echo "$0: Fehlende Parameter" >&2
99 echo "Aufruf:" >&2
100 echo "$0 /path/to/dir [/path2/to/dir] ..." >&2
101 exit 1
102 fi
103
104