sophisticated Backups mit Rsync

Backups sind wichtig.
Jeder der einmal vor einer kaputten, ratternden Festplatte gesessen hat, weiß wie frustrierend das Wissen ist, alle seine Daten ins informationstechnische Nirwana entschwinden zu sehen.
Ist ein Backup der persönlichen Daten noch mit relativ geringem Aufwand möglich (so es denn regelmäßig veranstaltet wird), so wird ein Backup eines Server-Systems in vielen Dingen anspruchsvoller.
Dies fängt damit an, dass reinen Datenmengen oftmals das Vielfache eines Ein-Benutzer-Systems betragen. Okay in heutige Zeit, wo ein jeder zig GBs an Musik und Videos auf dem Rechner hat, muss man das natürlich relativieren.
In diesem Beitrag möchte ich ein Bash-Script vorstellen, welches folgende Dinge leistet:

  1. Sichern von N-Hosts über RSYNC
  2. Sichern von N-Verzeichnissen auf jedem Host
  3. Inkrementelles Sichern (es werden nur Unterschiede gesichert/übertragen)
  4. Zusammenführen von täglichen (inkrementellen) Backups zu wöchentlichen Vollbackups.
  5. die aktuellen Backups sind jeweils unter einem Hardlink “latest” erreichbar.

Nachfolgend erfolgt eine Beschreibung über den Ablauf des Scriptes.
Es wird Momentan durch eine Sicherung von 3 Serversystemen und ca. 20 GB an reinen Nutzdaten getestet.

  1. Stelle sicher, dass alle notwendigen Verzeichnisse vorhanden sind:
    • Verzeichnis “log” für log-Dateien der Sicherungen
    • Verzeichnis “backup” für die eigentlichen Sicherungen
    • Je Backupquelle ein Verzeichnis
    • Innerhalb jedes Quellverzeichnisses ein Verzeichnis für tägliche und eines für wöchentliche Sicherungen
  2. Stelle sicher, dass alle Config-Dateien vorhanden sind:
    • sources.txt mit den Namen der zu sichernden Hosts/Verzeichnisse
    • exclude.txt mit den Dateien, welche nicht gesichert werden sollten (z.B. Thumbs.db)
    • innerhalb jedes Quellverzeichnisses eine Datei namens src-root.txt (mit dem ROOT der Sicherung)
      und src-folders.txt (mit den einzelnen Verzeichnissen)

Der Aufbau des Root-Verzeichnisses sieht nun wie folgt aus.

ROOT
|
|-log
|-sources.txt
|-exclude.txt
|-backup
|-host1.de
|-host2.de
|-src-root.txt
|-src-folders.txt
|-daily
|-weekly

Ein Beispiel für eine sources.txt sieht wie folgt aus:

host1.de
host2.de

Ein Beispiel für eine exclude.txt sieht wie folgt aus:

Thumbs.db
.DS_Store

Eine src-root.txt könnte so aussehen:

user@host1.de:

Eine src-folders.txt könnte so aussehen:

/home
/etc
/var/log
/var/www

Ablauf der Sicherung:

  • Generiere Log-Datei.
  • Iteriere über alle Quellen aus sources.txt
  • Überprüfe ob alle Quellen Verzeichnisse zum Sichern enthalten.
  • Überprüfe ob lokal Verzeichnisse für die Sicherung existieren – wenn nicht lege sie an.
  • Erstelle tägliches Backup – existiert ein vorheriges Backup, so sichere nur die Änderungen.

Dies passiert mit folgendem Befehl:

rsync -zrva –exclude-from=exclude.txt –link-dest=<hardlink-zu-altem-backup>  <sourcefolder>/ <backupfolder>/daily/<tag>/<zu-sicherndes-Verzeichnis>
Hierbei werden alle Benutzerrechte mitgesichert.
Setzte Hardlink vom letztem täglichen Backup auf aktuelles tägliches Backup.
Überprüfe ob Sonntag ist ( date +%w = 0)
Wenn ja, synchronisiere letztes tägliches Backup mit wöchentlichem (neuen) Backup.
Setzte Hardlink vom letztem wöchentlichen Backup auf aktuelles wöchentliches Backup.
Lösche alle täglichen Backups.
Setzte Hardlink vom letztem täglichen Backup auf aktuelles wöchentliches Backup.
So werden maximal 7 tägliche inkrementelle Backups erstellt und pro Jahr 52 wöchentliche Full-Backups.

#!/bin/sh

# Philipp's backup-scripte version 2

ROOT=/tank/backup/server
BACKDIR=$ROOT/backup
D=`eval date +%Y-%m-%d`
W=`eval date +%Y-%W`
w=`eval date +%w`
LATEST=latest
EXCLUDE=$ROOT/exclude.txt
SOURCES=$ROOT/sources.txt

LOG=$ROOT/log/$D.log

# Array of all needed folders
folders=( $ROOT/log $ROOT/sources )
files=( $EXCLUDE $SOURCES $LOG)

for folder in ${folders[@]}; do
    if [ ! -d $folder  ] ; then mkdir -p $folder; fi
done

for file in ${files[@]}; do
    if [ ! -f $file  ] ; then touch $file; fi
done

log () {
    if [  -z "$1" ] ; then echo "" > $LOG ; fi
    echo $1
    echo $1 >> $LOG    
}

# Ab hier gehts dann richtig los :-)

log

log "STARTING BACKUP..."
log "date: $D"

#stopping start time
#TIME0= expr `date +%s`;

# backup following sources
for SOURCE in `cat $SOURCES`; do
    log ""
    log "=================================================================="
    log "====== backup: $SOURCE "
    if [ ! -d $BACKDIR/$SOURCE/daily/$D  ] ; then mkdir -p $BACKDIR/$SOURCE/daily/$D; fi    
    if [ ! -f $BACKDIR/$SOURCE/src-root.txt  ] ; then touch $BACKDIR/$SOURCE/src-root.txt; fi    
    if [ ! -f $BACKDIR/$SOURCE/src-folders.txt  ] ; then touch $BACKDIR/$SOURCE/src-folders.txt; fi      
    #Logindaten lesen
    LOGINDATA=`cat $BACKDIR/$SOURCE/src-root.txt`
    if [ ! "$LOGINDATA" = "" ]; then
        log "====== benutze $LOGINDATA" 
        log "=================================================================="
        log ""
        for FOLDER in `cat $BACKDIR/$SOURCE/src-folders.txt`; do
            log "backup up... $FOLDER"
            mkdir -p $BACKDIR/$SOURCE/daily/$D/$FOLDER
            ### wenn schon latest-day vorhanden, dann sichere nur ânderungen
            if [ ! -d $BACKDIR/$SOURCE/daily/$LATEST  ] ; then 
                rsync -zrva $OLD --exclude-from=$EXCLUDE --link-dest=$BACKDIR/$SOURCE/daily/$LATEST/$FOLDER  $LOGINDATA/$FOLDER/ $BACKDIR/$SOURCE/daily/$D/$FOLDER | tee -a $LOG
            else
                rsync -zrva $OLD --exclude-from=$EXCLUDE $LOGINDATA/$FOLDER/ $BACKDIR/$SOURCE/daily/$D/$FOLDER | tee -a $LOG
            fi   
        done
        ### setze latest-day von altem Stand auf aktuelles Backup
        if [ -d $BACKDIR/$SOURCE/daily/$D ] ; then
            if [ -d $BACKDIR/$SOURCE/daily/$LATEST ] ; then rm $BACKDIR/$SOURCE/daily/$LATEST; fi
            ln -s $BACKDIR/$SOURCE/daily/$D $BACKDIR/$SOURCE/daily/$LATEST    
        fi

        ## wenn ende der Woche, dann erstelle Snapshot der Woche
        if [ "$w" = "0" ]; then
            log "create week-backup for $W"
            for FOLDER in `cat $BACKDIR/$SOURCE/src-folders.txt`; do
                mkdir -p $BACKDIR/$SOURCE/weekly/$W/$FOLDER
                rsync -zrva $OLD --exclude-from=$EXCLUDE $BACKDIR/$SOURCE/daily/$D/$FOLDER/ $BACKDIR/$SOURCE/weekly/$W/$FOLDER | tee -a $LOG
            done
            ### setze latest-week von altem Stand auf aktuelles Backup
            if [ -d $BACKDIR/$SOURCE/weekly/$LATEST ] ; then rm $BACKDIR/$SOURCE/weekly/$LATEST; fi
            ln -s $BACKDIR/$SOURCE/weekly/$W $BACKDIR/$SOURCE/weekly/$LATEST | tee -a $LOG 
            log "updating weekly latest"

            ### lââsche alte Backups
            log "delete all daily folders"
            rm -R $BACKDIR/$SOURCE/daily/*

            ### setze latest-day von altem Stand auf aktuelles Backup
            if [ -d $BACKDIR/$SOURCE/daily/$LATEST ] ; then rm $BACKDIR/$SOURCE/daily/$LATEST; fi
            ln -s $BACKDIR/$SOURCE/weekly/$W $BACKDIR/$SOURCE/daily/$LATEST | tee -a $LOG
            log "updating daily latest"
        fi
    fi
done

#stopping end time
#TIME1= expr `date +%s`;
#ERG = expr `$TIME1 - $TIME0`;

log "=================================================================="
log "DONE"
#log "using $ERG seconds"
log "=================================================================="