Code-Schnippsel: Backup über SSH mit rsync

Hier mal mein aktuelles Backup-Script mit dem ich aktuell sehr zufrieden bin. Ich hatte das ganze schon mal hier angerissen aber mittlerweile hat sich doch einiges getan. War mein Backup-Script zu Beginn einfach nur ein Befehl, der täglich mein Home mittels rsync über SSH auf meinen Server kopiert und dabei die alte Version überschrieben hat, so ist das ganze deutlich komplexer und flexibler. Mittlerweile sichere ich mein /home/, mein /etc/, mein /root/ und mein /usr/local/. Da mein Backup auf dem Backup-Server auf einer externen Festplatte gespeichert wird, hänge ich diese vor dem Backup ein und nach dem Backup aus. Ich habe auch eine Rotation eingebaut um Snapshots der letzten 7 Tage, sowie monatliche Snapshots des letzten Jahres zur Verfügung zu stellen. Um nicht unnötig Speicher zu belegen werden hardlinks genutzt, da sich große Datenmengen wie die Musiksammlung ja nicht groß ändern.
Das Script liegt unter /etc/cron.daily/ssh_backup und wird täglich von anacron ausgeführt.
Um die Dateirechte zu übertragen benutze ich auf dem Server sudo, da ich den root-LogIn per SSH nicht erlaubt habe. Sollte der Backup-Server down sein erhalte ich eine Email, die ich im Thunderbird empfange.
Nach langer Rede folgt nun mein Backup-Script:

#! /bin/bash


#### Diese Variablen bitte auf die eigenen Bedürfnisse anpassen
BACKUP_PFADE="/home/martin /etc /root /usr/local"
                        # Pfade die gesichert werden sollen
ZIELPFAD=/mnt/backup    # Pfad auf dem Backupserver (ohne abschließenden /)
ZIEL_EXT=true       # true = Wechseldatenträger, false = interner Speicher
ANZAHL_TAGE=7           # Tägliches Backup für x Tage
BENUTZER=martin         # Benutzername auf dem Backupserver
SERVER=backup           # IP-Adresse oder Hostname des Backupservers
PORT=1418               # SSH-Port des Backupservers
MAC=88:ae:1d:31:cd:17   # MAC-Adresse des Servers für WOL
HISTORY=/root/.backup_hist      # Protokoll-Datei
LISTE=/root/installed_packages.txt  # Speicherort für Liste der gespeicherten Pakete

#### Don't touch this; domdididom
#### Ab hier bitte nichts anfassen, wenn du nicht sicher weißt was du tust
MONAT=$(date +%-m)
VORMONAT=$(expr $MONAT - 1)
HOST=$(hostname)

# Backup-Server bereit?
if  ! ping -c 1 $SERVER > /dev/null 
    then

    # Backup-Server hochfahren
    wakeonlan $MAC 1>/dev/null

    sleep 60

    if ! ping -c 1 $SERVER > /dev/null 
        then
        echo "Backup konnte nicht durchgeführt werden. \nDer Server war nicht erreichbar." | mail -s 'Backup fehlgeschlagen!' $(whoami)@$HOST
        exit
    fi
fi

# Falls Zieldatenträger Wechselplatte, diese einhängen
if $ZIEL_EXT
    then
    ssh -p $PORT $BENUTZER\@$SERVER "sudo mount $ZIELPFAD" >/dev/null

    # Datei CURRENT zur Protokollierung der aktiven Backups existiert?
    if ! ssh -p $PORT $BENUTZER\@$SERVER stat $ZIELPFAD/CURRENT \> /dev/null 2\>\&1
            then
        ssh -p $PORT $BENUTZER\@$SERVER "touch $ZIELPFAD/CURRENT && echo "0" > $ZIELPFAD/CURRENT"
    fi

    # CURRENT auslesen und inkrementieren
    CURRENT=$(ssh -p $PORT $BENUTZER\@$SERVER "cat $ZIELPFAD/CURRENT")
    CURRENT=$(echo "$CURRENT + 1" | bc)
    ssh -p $PORT $BENUTZER\@$SERVER "echo $CURRENT > $ZIELPFAD/CURRENT"

fi

# Link 0 -> 12 (für Link auf letztes Backup im Januar) existiert?
if ! ssh -p $PORT $BENUTZER\@$SERVER stat $ZIELPFAD/$HOST/0 \> /dev/null 2\>\&1
    then
    ssh -p $PORT $BENUTZER\@$SERVER "ln -s $ZIELPFAD/$HOST/12 $ZIELPFAD/$HOST/0"
fi

# Bei Problemen zu Testzwecken auskommentieren
# ssh -v -p $PORT $BENUTZER@$SERVER /bin/true

# Eine Liste der installierten Pakete erstellen
dpkg --get-selections > $LISTE

# Backup per rsync
for PFAD in $BACKUP_PFADE
do
    rsync -az --delete --rsync-path='sudo rsync' --rsh="ssh -p $PORT" --link-dest=../$VORMONAT $PFAD $BENUTZER@$SERVER:$ZIELPFAD/$HOST/$MONAT
done

# Erstellen von Snapshots der letzten x Tage
ssh -p $PORT $BENUTZER\@$SERVER "sudo rm -rf $ZIELPFAD/$HOST/daily.$ANZAHL_TAGE"

if [ $ANZAHL_TAGE -gt 1 ]
    then

    for (( ANZAHL=$ANZAHL_TAGE ; ANZAHL >= 2 ; ANZAHL-- ))
    do
        ssh -p $PORT $BENUTZER\@$SERVER "sudo mv $ZIELPFAD/$HOST/daily.$(expr $ANZAHL - 1) $ZIELPFAD/$HOST/daily.$ANZAHL"
    done
fi

if [ ! $ANZAHL_TAGE -eq 0 ]
    then
    ssh -p $PORT $BENUTZER\@$SERVER "sudo mv $ZIELPFAD/$HOST/daily.latest $ZIELPFAD/$HOST/daily.1"
    ssh -p $PORT $BENUTZER\@$SERVER "sudo cp -al $ZIELPFAD/$HOST/$MONAT $ZIELPFAD/$HOST/daily.latest"
fi

# Falls Zieldatenträger Wechselplatte, diese aushängen sofern kein weiteres Backup aktiv ist (Variable Current)
if $ZIEL_EXT
        then
    CURRENT=$(ssh -p $PORT $BENUTZER\@$SERVER "cat $ZIELPFAD/CURRENT")
    if [ $CURRENT -eq 1 ]
        then
        ssh -p $PORT $BENUTZER\@$SERVER "echo "0" > $ZIELPFAD/CURRENT"
        ssh -p $PORT $BENUTZER\@$SERVER "sudo umount $ZIELPFAD" >/dev/null
        else
        CURRENT=$(echo "$CURRENT - 1" | bc)
        ssh -p $PORT $BENUTZER\@$SERVER "echo "$CURRENT" > $ZIELPFAD/CURRENT"
    fi 
fi

# Datum und Uhrzeit des Backups protokollieren
date >> $HISTORY

Das Script ist natürlich auf meine Bedürfnisse bzw. Vorlieben zugeschneidert. Aber vielleicht kann es dem ein oder anderen als Inspiration für ein eigenes Backup-Konzept dienen, oder man kann den ein oder anderen Schnippsel für eigene Scripte verwerten.

[Update 2014-10-01]
Da ich mittlerweile zwei Rechner mit Hilfe dieses Scriptes sichere habe ich nun das Script um eine Funktion erweitert. Erfolgt das Backup auf ein Wechselmedium wird eine Datei CURRENT angelegt. Diese wird mit 0 initialisiert und beim Start des Backups inkrementiert. Steht am Ende des Backups in der Datei eine Zahl > 1 ist noch ein anderes Backup aktiv und die Zahl in CURRENT wird dekrementiert und das Wechselmedium nicht ausgehängt. Steht in CURRENT eine 1 wird eine 0 zurückgeschrieben und das Laufwerk ausgehängt.
Das ganze ist eine quick and dirty Lösung, da mir erst mal keine bessere Lösung eingefallen ist. Wenn jemand einen eleganteren Weg kennt bin ich für Hinweise offen.
Ich habe jetzt ein paar mal die gleichzeitige Durchführung des Backups an beiden Rechnern forciert und war mit der Funktion zufrieden (bash -x ... ist ganz nett um Scriptänderungen zu testen).

[Update 2016-09-24] Ich habe den Ausschnitt aus der /etc/sudoers entfernt, da dieser fehlerhaft war. Mittlerweile lasse ich meinen user per rsync alles ausführen, was nicht wirklich kritisch ist, da der Server eh nicht von außen erreichbar ist.

Inhalt

Teilen: E-Mail

Hinterlasse einen Kommentar oder diskutiere im OSBN-Chat.


In den Kommentaren können folgende Formatierungen genutzt werden.