Einführung

Da es für mich keine passende Backup-Lösung gab, habe ich mir diese selber geschrieben. Meine Daten werden jetzt per rsync auf meinen Backup-Server synchronisiert. Für meine Backup-Lösung braucht man nicht zwingend einen Backup-Server. Mit rsync kann man auch auf dem lokalen Rechner Dateien und Verzeichnisse synchronisieren.

Das tolle an meinem Skript ist, dass ich verschiedene Versionen von meinen Daten habe. Wenn ich eine Datei lösche und dann mein Backup starte, wird diese Datei auf dem Backup-Server nicht gelöscht! Dies geschieht mit Hardlinks. Das geniale daran ist, dass nur die neuen Daten neuen Platz auf der Harddisk beanspruchen. Obwohl ich über 10 mal die selbe Datei gesichert habe, wird deren Speicherplatz nur einmal belegt.

Voraussetzung

Meine Backuplösung basiert auf diversen Bash-Skripten. Diese müssen mit den nötigen Rechten ausgeführt werden können. Will man die Daten auf einen Server sichern, benötigt man dort ein SSH-Login. Am einfachsten geht dies mit Public-/Private-Keys, damit man nicht jedes mal ein Passwort eingeben muss.

Als Client benutze ich mein Lenovo Thinkpad T61 Notebook und mein Asus EEEPC 1000H Netbook, welche beide mit Arch Linux laufen. Bei meinem Backup-Server handelt es sich um eine Asus EEEBOX, welche mit Debian 5.0 läuft. Das Backup-Skript sollte jedoch auf jedem Linux lauffähig sein.

Übersicht

Hier eine kleine übersicht über mein Backup-Konzept:

Komponenten

Mein Backup-Script benötigt folgende Komponenten:

  • install_: Installiert die Verzeichnisstruktur für die Backups auf dem Server/Computer
  • crontab: Die Einträge in der Crontab rufen das Backup-Script auf dem Server auf. Dies kann aber auch manuell gemacht werden.
  • backup: Dieses Skript kopiert die Backup Ordner und ermöglicht so eine Versionierung
  • syncup: Mit diesem Skript werden meine Daten auf den Server synchronisiert
  • syncup.conf: Wird syncup ohne Parameter aufgerufen, werden die in dieser Datei angegebenen Dateien und Verzeichnisse gesichert.

Installation

Das Installationsskript install erstellt folgende Ordner:

  • backupdata_: Hier werden die zu sichernden Daten abgespeichert
  • daily_1 bis daily_7: Eine Version der Daten von den letzten sieben Tagen
  • weekly_1 bis weekly_4: Eine Version der Daten von den letzten vier Wochen
  • monthly_1 bis monthly_12: Eine Version der Daten von den letzten zwölf Monaten
  • yearly_1 bis yearly_5: Eine Version der Daten von den letzten fünf Halbjahren

Folgendes Skript erstellt mir die Ordnerstruktur:

#!/usr/bin/env bash
################################################################
#
# install
# Erstellt die Verzeichnisse fuer backup
#
# Copyright 2010 Emanuel Duss
# Licensed under GNU General Public License
#
# 2009-12-05; Emanuel Duss; Erste Version
#
################################################################

################################################################
# Main

cd /media/backup/backup/

# Ordner fuer die Synchronisation
mkdir backupdata

# Ordner fuer taegliche Backups
for i in `seq 1 7`
do
  mkdir daily_$i
done

# Ordner fuer woechentliche Backups
for i in `seq 1 4`
do
  mkdir weekly_$i
done

# Ordner fuer monatliche Backups
for i in `seq 1 12`
do
  mkdir monthly_$i
done

# Ordner fuer halbjaehrliche Backups
for i in `seq 1 5`
do
  mkdir yearly_$i
done
# EOF

Synchronisation

Das Skript syncup synchronisiert die als Parameter angegebene Verzeichnisse und Dateien auf meinen Backup-Server. Werden keine Parameter angegeben, werden die Dateien und Verzeichnisse aus dem Konfigurationsfile syncup.conf gesichert. Will man keinen Backup-Server nutzen, sondern eine lokale Sicherung durchführen (z. B. auf eine externe Festplatte), lässt man bei der Variable BACKUPPATH den Hostnamen weg.

#!/usr/bin/env bash
################################################################
#
# syncup
# Synrchonisiert die Backup-Daten
#
# Copyright 2009 Emanuel Duss
# Licensed under GNU General Public License
#
# 2009-12-07; Emanuel Duss; Erster Entwurf
# 2009-12-15; Emanuel Duss; Erste produktive Version
# 2010-01-17; Emanuel Duss; rsync --progress
# 2010-09-14; Emanuel Duss; $BACKUPSRV, prepare () für mount
# 2010-10-06; Emanuel Duss; rycnc -h (Human)
# 2010-11-25; Emanuel Duss; Nutze Parameter als Quellverzeichnis
# 2010-11-27; Emanuel Duss; Mehrere Verzeichnisse und Konfigfile
#
################################################################

################################################################
# Variabeln
TIMESTAMP="`date +%Y-%m-%d_%H-%M-%S`"
HOSTNAME="`uname -n`"
RSYNC="/usr/bin/rsync"
RSYNC_PARAMETER="-avhz --progress --delete"
BACKUPSRV="emanuel@eeebox"
BACKUPPATH="$BACKUPSRV:/media/backup/backup/backupdata/$HOSTNAME/"
CONFIGFILE="`dirname $0`/syncup.conf"

################################################################
# Main

# Lege die zu sichernden Files fest
if [ -n "$*" ]
then
  SOURCE=`echo $* | awk '{ gsub("/ "," "); gsub("/$",""); print }'`
elif [ -e "$CONFIGFILE" ]
then
  echo "Konfigurationsfile: $CONFIGFILE"
  SOURCE=`grep -E "^[^#]" $CONFIGFILE | tr "\n" " " | awk '{ gsub("/ "," "); gsub("/$",""); print }'`
else
  echo "Usage:"
  echo "syncup [ Verzeichnisse und Dateien ]"
  echo ""
  echo "Wird kein Parameter angegeben, wird das Konfigfile $CONFIGFILE verwendet."
  exit 1
fi

# Info ausgeben
echo
echo "Source: $SOURCE"
echo "Target: $BACKUPPATH"
echo

# Backup-HD mounten
ssh $BACKUPSRV "sudo mount /media/backup"

# Dateien synchronisieren
$RSYNC $RSYNC_PARAMETER $SOURCE $BACKUPPATH

# Backup-HD umounten
ssh $BACKUPSRV "sudo umount /media/backup"

# EOF

Entweder wird das Skript direkt mit ./syncup im aktuellen Verzeichnis aufgerufen, oder man platziert es in einen Ordner, der in der $PATH-Variable eingetragen ist (z. B. /usr/local/bin). Jetzt kann dieses Skript in der Konsole aufgerufen werden:

emanuel@discordia:~
$ sudo syncup Daten/ /etc/ /var/www
Source: /etc
Target: emanuel@eeebox:/media/backup/backup/backupdata/discordia/
sending incremental file list
etc/openvpn/client.conf
         880 100%  175.78kB/s    0:00:00 (xfer#1, to-check=539/1389)
sent 47.16K bytes  received 219 bytes  31.59K bytes/sec
total size is 6.02M  speedup is 127.00

Ich muss das Skript mit den nötigen Rechten aufrufen. Das Verzeichnis /etc ist nur durch den User root vollständig lesbar. Deshalb rief ich das Skript mit sudo auf. Für eigene Verzeichnisse ist dies nicht nötig.

Die Daten befinden sich jetzt im Ordner backupdata auf dem Backup-Server.

Das File syncup.conf sieht folgendermassen aus:

################################################################
#
# syncup.conf
# Konfigurationsdatei fuer syncup
#
# Copyright 2010 Emanuel Duss
# Licensed under GNU General Public License
#
# 2010-11-27; Emanuel Duss; Erste Version
#
################################################################
# Verzeichnisse
/home/emanuel/Daten
/home/emanuel/.config
/home/emanuel/.filezilla
/home/emanuel/.ssh
/home/emanuel/.vim
# Files
/home/emanuel/.conkyrc
/home/emanuel/.bashrc
/home/emanuel/.exrc
/home/emanuel/.fehrc
/home/emanuel/.history
/home/emanuel/.muttrc
/home/emanuel/.screenrc
/home/emanuel/.vimrc
/home/emanuel/.vimperatorrc
/home/emanuel/.xinitrc
/home/emanuel/.Xdefaults
/home/emanuel/.smbcredentials*
# Systemdaten
/etc
#EOF

Wenn syncup ohne Parameter aufgerufen wird, werden alle Dateien und Verzeichnisse aus diesem Konfigurationsfile gesichert:

emanuel@discordia:~
$ sudo syncup
Konfigurationsfile: /home/emanuel/Daten/Scripts/syncup.conf
Source: /home/emanuel/Daten /home/emanuel/.config /home/emanuel/.filezilla
/home/emanuel/.ssh /home/emanuel/.vim /home/emanuel/.conkyrc /home/emanuel/.bashrc
/home/emanuel/.exrc /home/emanuel/.fehrc /home/emanuel/.history /home/emanuel/.muttrc
/home/emanuel/.screenrc /home/emanuel/.vimrc /home/emanuel/.vimperatorrc
/home/emanuel/.xinitrc /home/emanuel/.Xdefaults /home/emanuel/.smbcredentials* /etc
Target: emanuel@eeebox:/media/backup/backup/backupdata/discordia/
sending incremental file list
etc/openvpn/up.sh
         453 100%    0.00kB/s    0:00:00 (xfer#1, to-check=540/393538)
sent 6.34M bytes  received 32.75K bytes  296.59K bytes/sec
total size is 73.20G  speedup is 11479.75

Backup

Die Daten wurden erst auf den Server synchronisiert. Wenn man eine Datei auf der Workstation löschen würde, dann wäre diese bei der nächsten Synchronisation ebenfalls gelöscht. Darum erstelle ich pro Tag, Woche, Monat und Halbjahr eine Kopie aller Daten. Dies erledigt mein Backup-Server. Da ich hierfür Hardlinks verwende, wird bei jeder Kopie kein weiterer Speicherplatz beansprucht. Lediglich neue Daten beanspruchen neuen Speicherplatz. Das ist genial!

Das Backup-Skript sieht folgendermassen aus:

#!/bin/bash
################################################################
#
# backup
# Backup mit Hardlinks
#
# Copyright 2009 Emanuel Duss
# Licensed under GNU General Public License
#
# 2009-10-22; Emanuel Duss; Erster Entwurf
# 2009-12-05; Emanuel Duss; Erste produktive Version
# 2010-04-27; Emanuel Duss; Logfile in /var/log
#
################################################################

################################################################
# Variabeln
BACKUPDIR=/media/backup/backup
LOGFILE=/var/log/backup.log
TIMESTAMP=`date +%Y-%m-%d_%H-%M-%S`

################################################################
# Funktionen
usage ()
{
        echo "Usage:
        backup ( daily | weekly | monthly | yearly )"
}

################################################################
# Backup-Funktionen
daily ()
{
        rm -r $BACKUPDIR/daily_7
        mv $BACKUPDIR/daily_6 $BACKUPDIR/daily_7
        mv $BACKUPDIR/daily_5 $BACKUPDIR/daily_6
        mv $BACKUPDIR/daily_4 $BACKUPDIR/daily_5
        mv $BACKUPDIR/daily_3 $BACKUPDIR/daily_4
        mv $BACKUPDIR/daily_2 $BACKUPDIR/daily_3
        mv $BACKUPDIR/daily_1 $BACKUPDIR/daily_2
        mkdir $BACKUPDIR/daily_1
        cp -al $BACKUPDIR/backupdata/* $BACKUPDIR/daily_1
}
weekly ()
{
        rm -r $BACKUPDIR/weekly_4
        mv $BACKUPDIR/weekly_3 $BACKUPDIR/weekly_4
        mv $BACKUPDIR/weekly_2 $BACKUPDIR/weekly_3
        mv $BACKUPDIR/weekly_1 $BACKUPDIR/weekly_2
        mkdir $BACKUPDIR/weekly_1
        cp -al $BACKUPDIR/backupdata/* $BACKUPDIR/weekly_1
}
monthly ()
{
        rm -r $BACKUPDIR/monthly_12
        mv $BACKUPDIR/monthly_11 $BACKUPDIR/monthly_12
        mv $BACKUPDIR/monthly_10 $BACKUPDIR/monthly_11
        mv $BACKUPDIR/monthly_9 $BACKUPDIR/monthly_10
        mv $BACKUPDIR/monthly_8 $BACKUPDIR/monthly_9
        mv $BACKUPDIR/monthly_7 $BACKUPDIR/monthly_8
        mv $BACKUPDIR/monthly_6 $BACKUPDIR/monthly_7
        mv $BACKUPDIR/monthly_5 $BACKUPDIR/monthly_6
        mv $BACKUPDIR/monthly_4 $BACKUPDIR/monthly_5
        mv $BACKUPDIR/monthly_3 $BACKUPDIR/monthly_4
        mv $BACKUPDIR/monthly_2 $BACKUPDIR/monthly_3
        mv $BACKUPDIR/monthly_1 $BACKUPDIR/monthly_2
        mkdir $BACKUPDIR/monthly_1
        cp -al $BACKUPDIR/backupdata/* $BACKUPDIR/monthly_1
}
yearly ()
{
        rm -r $BACKUPDIR/yearly_5
        mv $BACKUPDIR/yearly_4 $BACKUPDIR/yearly_5
        mv $BACKUPDIR/yearly_3 $BACKUPDIR/yearly_4
        mv $BACKUPDIR/yearly_2 $BACKUPDIR/yearly_3
        mv $BACKUPDIR/yearly_1 $BACKUPDIR/yearly_2
        mkdir $BACKUPDIR/yearly_1
        cp -al $BACKUPDIR/backupdata/* $BACKUPDIR/yearly_1
}

################################################################
# Main
echo "$TIMESTAMP Backup $1 start" >> $LOGFILE 2>&1
case $1 in
  daily|weekly|monthly|yearly)
    mount /media/backup
    $1 >> $LOGFILE 2>&1
    umount /media/backup
    ;;
  *)
    usage
    exit 1
    ;;
esac
echo "$TIMESTAMP Backup $1 done" >> $LOGFILE 2>&1
# EOF

Kurze Erklärung: Der älteste Ordner wird gelöscht. Dann werden alle um eins verschoben. Das Herzstück vom ganzen Backup verbirgt sich hinter der Option -l von cp. Diese Option erstellt lediglich einen Hardlink von allen Dateien. Dies ist das tolle daran. Bei der Kopie mit cp -al src dst wird kein weiterer Speicherplatz auf dem Speichermedium belegt!

Backups erstellen

In der Crontab, welche mit sudo crontab -e aufgerufen wird, erstelle ich folgende Einträge:

# Backup

# Backup daily; 1:30h
30 1   *   *   *       /usr/local/bin/backup daily
# Backup weekly; Montag, 2:00h
0  2   *   *   1       /usr/local/bin/backup weekly
# Backup monthly; 1. Tag 4:00h
0  4   1   *   *       /usr/local/bin/backup monthly
# Backup yearly; 31. Dezember 3:00h
0  3  31  12   *       /usr/local/bin/backup yearly

Jetzt werden zu bestimmten Zeiten die vberschiedenen Backups durchgeführt. Natürlich kann man die Befehle auch von Hand ausführen. Jedoch vereinfachen die Cronjobs diese Aufgabe erheblich!

Überwachung

Im Logfile können wir uns über den Status informieren:

emanuel@eeebox:~
$ tail /var/log/backup.log
2010-11-25_23-31-55 Backup daily start
2010-11-25_23-31-55 Backup daily done

Ich sehe keine Fehlermeldung. Das Backup hat funktioniert!

Restore

Ein Backup ist ohne erfolgreichen Restore gar nichts wert! Das restoren ist sehr einfach. Man kann einfach den gewünschten Ordner oder die gewünschte Datei zurückkopieren. Man muss gar nichts beachten!

emanuel@eeebox:/media/backup/backup/weekly_2/discordia
$ scp -r Daten/ discordia:/tmp/

Schluss

Wenn man das Backup einmal eingerichtet hat, hat man eine sehr zuverlässige Lösung um seine Daten zu sichern. Ich rate jedem an, seine Daten regelmässig zu sichern und auch den Restore durchzuspielen! Das Logfile sollte regelmässig nach Fehlern durchsucht werden, um die Integrität aller Backup-Daten festzustellen.

Viel Spass mit euren Backups.