Backups

Anders als Papier oder Mikrofilm, die nicht von selbst Feuer fangen können und eine reguläre Lebensdauer von Jahrzehnten haben, sind elektronische Daten endlich. Endlich in dem Sinne, dass vorhergesagt werden kann, dass sie auf ihrem ursprünglichen Datenträger nicht die Zeit erleben werden, für die sie bestimmt sind.

Das schlimmste am Verlust von Daten ist, dass man nicht weiß, was genau man verloren hat. Einzelne Personen werden sich an ihre Bestandteile vielleicht noch erinnern, das vollständige Bild bleibt aber für immer verloren. Wenn man fragt, woran man eine ordnungsgemäße IT-Verwaltung erkennt, werden wahrscheinlich Backups die Liste der Antworten anführen. Doch sie sind trügerisch, einerseits erkennt man im Regelbetrieb nicht, ob sie (noch) durchgeführt werden. Man plant für das Ungewisse, hofft dass es niemals geschieht, weiß aber gleichzeitig erst ob alles funktioniert hat, wenn das Ereignis eintritt.

Andererseits ist es schwer jemanden wegen Fehlern im Backup zur Verantwortung zu ziehen, wenn Daten verloren gehen. Niemand weiß, was verloren gegangen ist, etwas zu beweisen, was es nicht mehr gibt ist nahezu unmöglich und Schäden können daher weitestgehend nicht beziffert werden. Ein mißlungenes Backups ist somit die Generalausrede für den Verlust von Daten. Dazu kommt, dass nicht alle Daten für die Ewigkeit bestimmt sind, sich der Aufwand für jene also nicht lohnt.

Zusammengefasst bleibt im Ernstfall immer eine Ungewissheit und daneben stellt es auch eine Chance dar, nur wichtige Daten mitzuschleppen. Was jedenfalls bleibt, ist die Strategie, die hinter dem Backup steht.

1) Vorüberlegungen

Im Folgenden einige Begriffe, wie ich sie hier verwenden werde (offizielle sprachliche oder technische Definitionen können und würden davon abweichen):

  • Backup: Asynchrone (d.h. zeitlich versetzte) Sicherung von Daten, im Sinne einer Duplizierung an einen anderen Speicherort.
  • Spiegelung: Synchrone Duplizierung von Daten an einen anderen Ort.
  • inkrementell: Aufeinander aufbauende Backups (in Abgrenzung zu vollständigen Backups).
  • Profil: Eine Zusammenfassung von Verzeichnissen und Daten, die gesichert werden sollen.

Grundsätzliches

Ich für meinen Teil habe mir angewöhnt Backups aufzuteilen, auch wenn dieser Tage Speicherplatz so günstig ist, dass grundsätzlich jederzeit ein volles Backup möglich ist. Zumindest trenne ich zwischen dem System und den Daten. So kann im Ernstfall (wenn gewünscht) der Systemausfall gleich zu einem Update genutzt werden.

Daneben bemühe ich mich, auf inkrementelle Backups zu setzen. Dadurch können häufiger, kleinere Backups durchgeführt werden, die jeweils den Zustand einer gewissen Zeitspanne repräsentieren.

Ich setze a) auf freie Software, um die Verfügbarkeit für die Zukunft sicherzustellen, und b) auf Datenformate, die auch ohne eine spezielle Software gelesen werden können. Damit soll sichergestellt werden, dass das Programm auch von einer Rettungskonsole aus benutzt werden kann. Kurz, ich setze auf Duplicity.

Umgebung

Meine Erfahrungen beziehen sich auf Linux-Server, auf die ich keinen physischen Zugriff habe und die keine grafische Oberfläche haben. Der Backup-Server ist dabei via FTP zu erreichen.

Um zu vermeiden, dass Backups von temporären Backup-Dateien entstehen, habe ich ein Verzeichnis /backup angelegt. Das gesamte Verzeichnis befindet sich als ZIP-Datei bei meinen Notfallunterlagen. Auch der Backup-FTP ist nach /backup/ftp gemounted (solange kein Backup läuft, duplicity bringt eine eigene FTP-Funktion mit), allerdings liegen die Zugangsdaten dafür in /root/.netrc.

Das keyfile und die config für duplicity und die Profildateien (dazu später mehr) liegen in /etc/backup. Das Backup-Script in der Datei /backup/tools/backup.

2) Restore nach Totalausfall

Im Fall der Fälle ließe sich ein mittels Rettungskonsole ein ganz neues System aufsetzen. Duplicity, das Backup-Script und den Ordner /backup installieren sowie das Verzeichnis /etc/backup mit den Profildaten und dem Keyfile füllen.

Dann kann man entscheiden, ob man nur die Daten rückspielt oder auch das System auf den Stand des letzten Backups bringt.

3) Duplicity und Profile

Um Profile nutzen zu können, verwende ich ein selbstgeschriebenes Skript - Dank an dieser Stelle für zahlreiche Ideengeber. Ich erkläre zunächst, wei diese Profile funktionieren und sodann wie sie aufgerufen werden. Zum Schluss findet sich das Skript selbst.

Profile erleichtern die Strukturierung

Duplicity ist hervorragend, um verschiedene Orte zu einem Backup zusammen zu fassen. Jede dieser Zusammenfassung ist in meiner Sprechart ein Profil, z.B. System, Home, Mail etc.

Die verschiedenen Profile sind schlicht  Dateien im Ordner /etc/backup, welche in Duplicity-Notation die dazugehörigen Verzeichnisse bzw. Auslassungen auflisten, z.B. für das Profil System (/etc/backup/system):

# exclusions first
- /srv/ftp
- /srv/webdav
+ /usr/
+ /bin/
+ /sbin/
+ /opt/
+ /selinux/
+ /srv/
+ /lib/
+ /lib64/
+ /boot/
+ /etc/
+ /initrd*
+ /vmlinuz*
+ /installimage*
- /sys/**
# backup specific data
+ /backup/systemdata/
+ /backup/tools/

Ein weiterer Vorteil ist die Möglichkeit, Befehle auszuführen, bevor das eigentliche Backup beginnt. Am Beispiel des Profils System (/etc/backup/system.pre):

#!/bin/bash
/usr/bin/dpkg --get-selections | /usr/bin/diff -s - /backup/systemdata/installed-packages.txt | /bin/grep "identisch" > /dev/null;
if [ $? -gt 0 ]; then
 /usr/bin/dpkg --get-selections > /backup/systemdata/installed-packages.txt;
fi

Zusammengefasst wird hier für ein Backup der Systemverzeichnisse zunächst eine Textdatei mit allen installierten Paketen erstellt (system.pre), um das System in den Ausgangszustand bringen zu können. Diese Datei wird mit den Systemverzeichnissen mitgesichert (system).

Backup erstellen

Ein crontab könnte wie folgt aussehen:

##
# System - Binary and configuration files
#          daily 00:05h
5  0 * * 2-7 /backup/tools/backup --profile system incremental
5  0 * *   1 /backup/tools/backup --profile system full
0 13 * * 1 /backup/tools/backup --profile system remove-all-but-n-full 2

Jeden tag um 00:00 Uhr wird das Backup für System ausgeführt. Einmal die Woche voll, sonst inkrementell. Nach dem Vollbackup werden mittags überflüssige Backupdateien gelöscht.

Restore durchführen

Wenn alles richtig eingerichtet ist, lässt sich ein Restore wie folgt ausführen:

/backup/tools/backup --profile system <DESTINATION>

<Destination> kann ein temporäres Verzeichnis sein, falls z.B. nur einzelne Dateien aus dem Backup gesucht werden. Oder es kann "/", das root-Verzeichnis, sein, um das System wieder herzustellen.

Das Backup-Script

/backup/tools/backup --profile system <COMMAND>

Mein Skript reicht im Wesentlichen lediglich die Duplicity-Befehle durch, <COMMAND> kann einer der folgenden sein:

  • cleanup (löscht fehlerhafte, verwaiste Dateien)
  • remove-older-than [duplicity date format], remove-all-but-n-full [1..n]
  • collection-status, list-current-files
  • full, incremental
  • restore, verify
  • drop-backup (ACHTUNG: Löscht alle Backups des Profils ohne Nachfrage!)

Das gesamte Skript:

#!/bin/bash

# System variables
NAME="meinserver.de";
TARGET="/backup/ftp";
LOGFILE="/backup/backup.log";
VERBOSITY=0; # more then 6 gives nonsense, from 6 on file names are written
TMPDIR="/backup/tmp";
FTPHOST=`/bin/fgrep machine ~/.netrc | awk '{print $2}'`;
FTPUSER=`/bin/fgrep login ~/.netrc | awk '{print $2}'`;
FTPPWD=`/bin/fgrep password ~/.netrc | awk '{print $2}'`;

# functions
function debug {
 if [ $VERBOSITY -gt 0 ]; then
  # print out debug message
  echo `/bin/date +"%Y-%m-%d %T"`" $PROFILE: $@";
 elif [ "$LOGFILE" != "" ]; then
  # write debug to file
  echo `/bin/date +"%Y-%m-%d %T"`" $PROFILE: $@" >> "$LOGFILE";
 fi
}

STARTTIME=`/bin/date +%s`;

if [ -f "/etc/backup/gpg.conf" ]; then
 /bin/chown root.root /etc/backup/gpg.conf;
 /bin/chmod 600 /etc/backup/gpg.conf;
 . /etc/backup/gpg.conf;
 if [ "$GPGKEY" = "" -o "$GPGPWD" = "" ]; then
  echo "Can't read encryption key from config. Backup aborted.";
  debug "Can't read encryption key from config. Backup aborted.";
  exit 99;
 fi
else
 echo "Can't read encryption key. Backup aborted.";
 debug "Can't read encryption key. Backup aborted.";
 exit 99;
fi

# check for profile
PROFILE=$2;
if [ "$1" != "--profile" -o ! -f "/etc/backup/$PROFILE" ]; then
 echo "No or corrupt profile for backup given. Profile needs to be the first parameter. Aborting.";
 debug "No or corrupt profile for backup given.";
 exit 2;
else
 shift;
 shift;
 debug "Backup started. Using profile $PROFILE.";
fi

# check for ftp available
FTPCHECK=`/bin/mount -l | grep $TARGET`;
if [ "$FTPCHECK" = "" ]; then
 echo "Can't access ftp site. Backup aborted.";
 debug "Can't access ftp site. Backup aborted.";
 exit 1;
fi

# check for target folder
if [ ! -d "$TARGET/$PROFILE" ]; then
 /bin/mkdir "$TARGET/$PROFILE";
 /bin/chmod 700 "$TARGET/$PROFILE";
 if [ ! -d "$TARGET/$PROFILE" ]; then
  debug "Can't create profile folder in ftp site. Aborting.";
  echo "Can't create profile folder in ftp site. Aborting.";
  exit 3;
 else
  debug "Created folder $TARGET/$PROFILE";
  /bin/chmod 700 "$TARGET/$PROFILE";
 fi
fi

## proprietary delete command
if [ "$1" = "drop-backup" ]; then
 if [ -d "$TARGET/$PROFILE" ]; then
  /bin/rm -R "$TARGET/$PROFILE";
 fi
 if [ -d "$TMPDIR/$NAME/$PROFILE" ]; then
  /bin/rm -R "$TMPDIR/$NAME/$PROFILE";
 fi
 echo "Deleted all backup files - remote and local - for profile $PROFILE";
 debug "Deleted all backup files - remote and local - for profile $PROFILE";
 exit 0;
fi

## create duplicity call
FTP="ftp://$FTPUSER@$FTPHOST";
# exclude select the files to be backed up according to profile
EXCLUDE="--exclude-globbing-filelist /etc/backup/$PROFILE --exclude /";

debug "Backup running. Doing $1 backup.";

## check correct parameters
#
# cleanup, remove-older-than, remove-all-but-n-full: add force to really delete
# collection-status, list-current-files: specify only ftp collection
# full, incr / incremental: specify source locally and target on ftp, remove command if incremental update
# restore, verify: specify target on ftp and source locally, rmeove command if restore is selected
#
if [ "$1" = "cleanup" -o "$1" = "remove-older-than" -o "$1" = "remove-all-but-n-full" ]; then
 D_COMMAND="$1 --force";
 shift;
 D_COMMAND="$D_COMMAND $@ $FTP/$PROFILE";
elif [ "$1" = "collection-status" -o "$1" = "list-current-files" ]; then
 D_COMMAND="$@ $FTP/$PROFILE";
elif [ "$1" = "full" -o "$1" = "incr" -o "$1" = "incremental" ]; then
 if [ "$1" = "incr" -o "$1" = "incremental" ]; then
  shift;
 fi
 D_COMMAND="$@ / $FTP/$PROFILE $EXCLUDE";
 L_COMMAND="scripts";
elif [ "$1" = "restore" ]; then
 shift;
 D_COMMAND="$FTP/$PROFILE $@";
elif [ "$1" = "verify" ]; then
 D_COMMAND="$1";
 shift;
 D_COMMAND="$D_COMMAND $FTP/$PROFILE / $@";
 L_COMMAND="changes";
else
 debug "Command $1 does not exist. What do you want to do?";
 echo "Command $1 does not exist. What do you want to do?";
 exit 4;
fi

# take the rest
if [ $VERBOSITY -lt 1 ]; then
 D_COMMAND="$D_COMMAND --no-print-statistics";
fi
D_COMMAND="$D_COMMAND --encrypt-key $GPGKEY --sign-key $GPGKEY\
 --verbosity $VERBOSITY\
 --tempdir $TMPDIR --archive-dir $TMPDIR/$NAME --name $PROFILE";

## running pre script
if [ "$L_COMMAND" = "scripts" -a -f "/etc/backup/$PROFILE.pre" ]; then
 debug "Running pre-script for profile $PROFILE.";
 . "/etc/backup/$PROFILE.pre";
fi

## unmount to prevent double login
/bin/umount "$TARGET";

## execute duplicity
PASSPHRASE="$GPGPWD" \
FTP_PASSWORD="$FTPPWD" \
/usr/bin/duplicity $D_COMMAND

## check result
if [ "$L_COMMAND" = "changes" -a $? -ne 0 ]; then
 if [ $? -gt 0 ]; then
  echo "Profile $PROFILE has unsaved changes pending.";
 else
  echo "There are no differences between backup and local files in profile $PROFILE.";
 fi
fi

## mount again
/bin/mount "$TARGET";

## running post script
if [ "$L_COMMAND" = "scripts" -a -f "/etc/backup/$PROFILE.post" ]; then
 debug "Running post-script for profile $PROFILE";
 . "/etc/backup/$PROFILE.post";
fi

ENDTIME=`/bin/date +%s`;
TOTALTIME=$((ENDTIME-STARTTIME));

debug "Backup finished. Took $TOTALTIME second(s). ";

Geben Sie einen Kommentar ab

Kommentare

Bisher hat niemand diese Seite kommentiert.

RSS Feed für die Kommentare auf dieser Seite | RSS feed für alle Kommentare