Als ich heute mit Dennis, der jetzt seine Backup-Strategie aufstockt, über mögliche Backup-Implementationen diskutiert habe, kam mir die Idee, meine persönliche Vorgehensweise zu erläutern.
Die Hardware
Neben den Clients, die die zu sichernden Daten beherbergen, steht mir folgende Hardware für Backups zur Verfügung:
- NAS – Eigenbau-Gerät mit energiesparendem AMD-Prozessor, 4 GB Arbeitsspeicher und sechs 1 TB-Festplatten im Software-RAID 5 (4.5 TB nutzbar)
- Bandroboter – ein gebrauchter HP StorageWorks DAT72-Autoloader mit einem DDS5-Bandlaufwerk und 10 Schächten für Medien
- Externe Festplatte (genau genommen ein DAS) – eine Onnto DataTale mit vier 2 TB-Festplatten im RAID5 (6 TB nutzbar)

Datenüberblick
Die erste Frage dürfte jetzt sein, was überhaupt gesichert wird. Sicherungen werden auf meinem NAS pro „Sicherungsziel“ angelegt – solche Sicherungsziele sind beispielsweise:
- Der Webserver, auf dem dieser Blog läuft 😉
- Mein Notebook
- Die virtuellen Maschinen meines Hypervisors
- …
Für jedes Sicherungsziel wird auf dem NAS ein Ordner angelegt. Innerhalb dieses Ordners existiert ein Unterordner „latest“ – in diesem Ordner landen dann die jeweils zu sichernden aktuellen Daten. Wenn eine Sicherung isoliert von neueren aufbewahrt werden soll, genügt es den Ordnernamen zu ändern. So habe ich schnell und unkompliziert einen „Snapshot„. Auf dem NAS befindet sich im Backup-Ordner für jedes Sicherungsziel eine Blacklist-Datei (Sicherungsziel_blacklist), die eine Liste von Ordnern und Dateien, die nicht auf Band gesichert werden sollen, enthält.

Skripte und Logdateien
Für die Sicherung der einzelnen Sicherungsziele habe ich mir diverse Skripte programmiert, die mir einige händische Arbeit abnehmen und den Prozess so beschleunigen.
Sicherungen eines Ziels erfolgen mithilfe des folgenden Skripts:
#!/bin/bash ########################################### # # # !!! AUTOMATISIERTE SICHERUNG !!! # # # ########################################### #Variablendeklaration backups_path="/mnt/storage/data/Backups" exclude_file_prefix="blacklist_" max_size_gb=200 dev_streamer="$1" #Datumprefix und Logfile erstellen logprefix="$(date +"%d_%m_%Y_%H_%M")" if [ -d "/var/log/backup/tape/$2" ]; then #Ordner vorhanden echo "Log directory for this target (/var/log/backup/tape/$2) exists." else #Ordner nicht vorhanden mkdir -p /var/log/backup/tape/$2 echo "Log directory for this target (/var/log/backup/tape/$2) didn't exist - I created it for you. :)" fi echo "log file from $(date)" >> /var/log/backup/tape/$2/$logprefix.log echo "parameters were: $1 $2 $3 $4" >> /var/log/backup/tape/$2/$logprefix.log #Wurde ein Streamer angegeben? if [ "$1" = "" ]; then #Nein echo "ERROR: You must specify a streamer device (e.g. nst0)!" | tee -a /var/log/backup/tape/$2/$logprefix.log exit 1 fi #Wurde ein Ziel angegeben? if [ "$2" = "" ]; then #Nein echo "ERROR: You must specify a target (e.g. host)!" | tee -a /var/log/backup/tape/$2/$logprefix.log echo "Aborting..." | tee -a /var/log/backup/tape/$2/$logprefix.log exit 1 fi #Flag setzen if [ "$3" = "--from" ]; then #Ein vorheriges Backup wurde angefordert --> wurde ein Datum angegeben? if [ "$4" = "" ]; then #Nein echo "ERROR: You need to tell me the backup date in DD_MM as parameter after --from!" | tee -a /var/log/backup/tape/$2/$logprefix.log echo "Aborting..." | tee -a /var/log/backup/tape/$2/$logprefix.log exit 1 else sel_flag="$4" fi else #Setze Flag sel_flag="latest" fi #Ordner fuer Backup vorhanden? if [ -d "$backups_path/$2/$sel_flag" ]; then #Ja echo "Backup exists" | tee -a /var/log/backup/tape/$2/$logprefix.log else #Nein echo "ERROR: There is no backup #$sel_flag for that target!" | tee -a /var/log/backup/tape/$2/$logprefix.log echo "Aborting..." | tee -a /var/log/backup/tape/$2/$logprefix.log exit 1 fi #Ueberpruefe ob das Backup auf das Tape passt backup_size="$(du -cs $backups_path/$2/$sel_flag --exclude-from=$backups_path/$exclude_file_prefix$2 | grep insgesamt | cut -f1)" echo -n "Backup size is $backup_size kbytes" | tee -a /var/log/backup/tape/$2/$logprefix.log if [ "$backup_size" -gt "$(($max_size_gb*1024*1024))" ]; then #Nein echo ", more than $max_size_gb gbytes - that won't fit!" | tee -a /var/log/backup/tape/$2/$logprefix.log echo "Aborting..." | tee -a /var/log/backup/tape/$2/$logprefix.log exit 1 else #Ja echo " - that will fit." | tee -a /var/log/backup/tape/$2/$logprefix.log fi #Streamer vorhanden? if [ "$(ls /dev | grep $dev_streamer)" = "" ]; then echo "ERROR: Streamer device not found" | tee -a /var/log/backup/tape/$2/$logprefix.log echo "Aborting..." | tee -a /var/log/backup/tape/$2/$logprefix.log exit 1 fi #Ist das Geraet belegt? echo -n "Checking whether the device is currently in use..." | tee -a /var/log/backup/tape/$2/$logprefix.log if [ "$(mt -f /dev/$dev_streamer status | grep busy)" = "" ]; then #Nein echo "NO" | tee -a /var/log/backup/tape/$2/$logprefix.log else #Ja echo "YES" | tee -a /var/log/backup/tape/$2/$logprefix.log echo "Aborting..." | tee -a /var/log/backup/tape/$2/$logprefix.log exit 1 fi #Ist ein Band eingelegt? echo -n "Checking whether a tape is inserted..." | tee -a /var/log/backup/tape/$2/$logprefix.log if [ "$(mt -f /dev/$dev_streamer status | grep DR_OPEN)" = "" ]; then #Ja echo "YES" | tee -a /var/log/backup/tape/$2/$logprefix.log else #Nein echo "NO" | tee -a /var/log/backup/tape/$2/$logprefix.log echo "Aborting..." | tee -a /var/log/backup/tape/$2/$logprefix.log exit 1 fi #Spiele Band zurueck echo "Rewinding tape..." | tee -a /var/log/backup/tape/$2/$logprefix.log mt -f /dev/$dev_streamer rewind | tee -a /var/log/backup/tape/$2/$logprefix.log #Gebe Dateien aus echo "The FOLLOWING files will be backupped:" | tee -a /var/log/backup/tape/$2/$logprefix.log echo "" | tee -a /var/log/backup/tape/$2/$logprefix.log du -h "$backups_path/$2/$sel_flag/" --exclude-from="$backups_path/$exclude_file_prefix$2" | tee -a /var/log/backup/tape/$2/$logprefix.log #Sichere Daten echo "About to backup files..." | tee -a /var/log/backup/tape/$2/$logprefix.log tar cvzf "/dev/$dev_streamer" "$backups_path/$2/$sel_flag/" --exclude-from="$backups_path/$exclude_file_prefix$2" | tee -a /var/log/backup/tape/$2/$logprefix.log
Eine Sicherung des Ziels „hostA“ auf das Bandlaufwerk „/dev/st0“ erfolgt beispielsweise wie folgt:
./backup.sh st0 hostA
Für jedes Sicherungsziel habe ich noch ein dediziertes Wochen- und Monatsskript, welches mithilfe von mtx das entsprechende Band gleich in den Bandroboter einlegt und das o.g. Backup-Skript mit den notwendigen Paramtern startet.
#!/bin/sh TAPE_DEV="/dev/sg8" TAPE1="7" TAPE2="8" mtx -f $TAPE_DEV load $TAPE1 ./backup.sh st0 hostA mtx -f $TAPE_DEV unload $TAPE1 mtx -f $TAPE_DEV load $TAPE2 ./backup.sh st0 hostA mtx -f $TAPE_DEV unload $TAPE2
Für die Sicherung meines Webservers habe ich ein Skript geschrieben, welches den FTP-Server mit curlftpfs mountet und die Dateien mithilfe von rsync kopiert.
#!/bin/bash ########################################## # # # Webspace-Spiegelung mittels RSync # # --------------------------------- # # # # 2011 By Christian Stankowic # # info@christian-stankowic.de # # # ########################################## #Variablendeklaration path_curlftpfs="/usr/bin/curlftpfs" path_rsync="/usr/bin/rsync" path_tee="/usr/bin/tee" ftp_server="..." ftp_mount="/mnt/ftp" ftp_user="..." ftp_pass="..." log_path="/var/log/backup/ftp" copy_path="/mnt/storage/data/Backups/web/latest" #Datumprefix und Logfile erstellen logprefix="$(date +"%d_%m_%Y_%H_%M")" echo "log file from $(date)" >> $log_path/$logprefix.log echo "parameters were: $1 $2 $3 $4" >> $log_path/$logprefix.log #FTP mounten curlftpfs $ftp_server $ftp_mount -o user=$ftp_user:$ftp_pass #Spiegel gemountet? echo "Checking if ftp is mounted..." | $path_tee -a $log_path/$logprefix.log if [ "$(mount | grep $ftp_mount)" = "" ]; then echo "Error: Mirror ($ftp_mount) NOT mounted" | $path_tee -a $log_path/$logprefix.log exit 1 else echo "Mirror ($ftp_mount) mounted" | $path_tee -a $log_path/$logprefix.log fi #Spiegelung $path_rsync -a -r --delete -v $ftp_mount $copy_path | $path_tee -a $log_path/$logprefix.log #FTP unmounten umount $ftp_mount
Die Spiegelung der NAS-Daten auf das DAS erfolgt mit einem einfachen rsync-Skript:
#!/bin/bash ###################################### # # # !!! SPIEGEL AUF DATATALE !!! # # # ###################################### #Variablendeklaration backups_path="/mnt/storage/data/Backups" #Datumprefix und Logfile erstellen logprefix="$(date +"%d_%m_%Y_%H_%M")" echo "log file from $(date)" >> /var/log/backup/mirror/$logprefix.log echo "parameters were: $1 $2 $3 $4" >> /var/log/backup/mirror/$logprefix.log #Spiegel gemountet? echo "Checking if mirror is mounted..." | tee -a /var/log/backup/mirror/$logprefix.log if [ "$(mount | grep /mnt/mirror)" = "" ]; then echo "Error: Mirror (/mnt/mirror) NOT mounted" | tee -a /var/log/backup/mirror/$logprefix.log exit 1 else echo "Mirror (/mnt/mirror) mounted" | tee -a /var/log/backup/mirror/$logprefix.log fi #Spiegelung rsync -a -r --exclude-from "/opt/bak/exclude_rsync" --delete -v /mnt/storage/data/ /mnt/mirror/data/ | tee -a /var/log/backup/mirror/$logprefix.log
Alle Skripte legen übrigens Logdateien unterhalb von /var/log/backup an – so kann ich nachvollziehen, ob die automatisierten Backups fehlerfrei durchlaufen sind.
# cd /var/log/backup/tape/hostA # 29_01_2012_20_42.log log file from So 29. Jan 20:42:48 CET 2012 parameters were: st0 hostA Backup exists Backup size is 1226994 kbytes - that will fit. Checking whether the device is currently in use...NO Checking whether a tape is inserted...YES Rewinding tape... The FOLLOWING files will be backupped: 24K ... ...
Zeitliche Planung
Sicherungen sind nur dann sinnvoll, wenn man sie auch konsequent und regelmäßig durchführt.
Meine Planung sieht derzeit wie folgt aus:
- Tägliche automatisierte Generierung von Sicherungsdaten auf bestimmten Hosts – i.d.R wichtige Server-Systeme
- Wöchentliche händische Synchronisation der Notebook-Daten auf das NAS
- Wöchentliche händische Sicherung des Webservers inklusive Datenbanken mithilfe von MySQLDumper und eines FTP-Skripts
- Wöchentliche skriptgesteuerte doppelte Sicherung auf Band und Synchronisation mit DataTale
Bänder, Bänder, Bänder,…
Die Sicherung auf Band erfolgt in der Regel immer doppelt. Das hat zur Folge, dass ich von jedem Backup zwei Bänder hab – ein Band landet im Schrank, das andere bewahre ich an einem anderen Ort (außerhalb meiner Wohnung) auf.
Ich habe für jedes „Sicherungsziel“ habe ich dedizierte Bänder für wöchentliche, monatliche und jährliche Backups. Auf dem Jahresband landet die Jahressicherung, während das Monatsband jeden Monat mit dem aktuellen Datenbestand überschrieben wird. Ebenso ist es mit dem Wochenband, was jede Woche überschrieben wird. Die Sicherungen sind übrigens Full-Backups – ich praktiziere keine inkrementellen Backups auf Band. Der wöchentliche Backup-Vorgang nimmt 2 bis 4 Stunden Zeit in Anspruch.
Das Ganze mag jetzt ein wenig übertrieben klingen – ich sichere allerdings auch geschäftliche Daten, die ich 10 Jahre vorhalten muss. Und wenn ich ohnehin schon profesionell Backups anfertige, können davon meine privaten Daten auch profitieren, oder? 🙂

Videos
Einen Überblick über den DDS-Bandroboter und dessen Funktionen gibt es im ersten Clip:
Wer schon immer mal wissen wollte, wie ein Bandroboter arbeitet, kann sich das Ganze hier mal anschauen:
Mein DAS habe ich vor einiger Zeit mit neuen Festplatten versorgt – zu sehen ist das im letzten Clip: