Snapshot-Backup
Diese Scripts erlauben die Sicherung eines Verzeichnisbaumes in mehreren Versionen, wobei der Platzbedarf optimiert ist (gleiche Dateien in verschiedenen Versionen belegen den Platz nur einmal). Der Platzspartrick besteht darin, dass mit Hard-Links gearbeitet wird.
Voraussetzungen
- Eine Partition für die Sicherung (am besten auf einer anderen Platte)
- Eintrag in crontab
- Anacron installiert: Anacron ist für Rechner gedacht, die nicht im Dauerlauf betrieben werden. Denn will man mit cron eine Tagessicherung machen, so muss man eine Uhrzeit festlegen.
Ablauf
- Es gibt 4 Snapshots: sicherung.0, ... sicherung.3
- Alle 4 Stunden wird per cron-Job sicherung.3 gelöscht, sicherung.0..sicherung.2 umbenannt in sicherung.1..sicherung.3 und ein neuer sicherung.0 erzeugt.
- Jeden Tag wird sicherung.0 kopiert in <wochentag> (Mo, Di ...).
- Jede Woche wird sicherung.0 kopiert in w_<wochennummer>.
- Das "Sicherungsprogramm" ist rsync.
- Die Sicherungspartition ist normalerweise schreibgeschützt und nur während der Sicherung zum Schreiben angemeldet.
Das Script
#!/bin/sh
# mk_snapshot.sh
# ------------- system commands used by this script --------------------
HOST=`hostname`
. /home/bin/$HOST/snapshot.data
ID=id
ECHO=echo
MOUNT=/bin/mount
RM=rm
MV=mv
CP=cp
TOUCH=/bin/touch
HOST=`hostname`
DATE=/bin/date
SET=set
RSYNC=/usr/bin/rsync
# ------------- file locations -----------------------------------------
if [ -z "$MOUNT_DEVICE" ] ; then
echo "MOUNT_DEVICE not defined"
exit 1
fi
if [ -z "$SNAPSHOT_RW" ] ; then
echo "SNAPSHOT_RW not defined"
exit 1
fi
if [ ! -f "$EXCLUDES" ] ; then
echo "Excluding file $EXCLUDES not found"
exit 1
fi
if [ ! -d "$DIR_TO_SAVE" ] ; then
echo "DIR_TO_SAVE not found"
exit 1
fi
if [ -z "$DIR_HOURLY" ] ; then
echo "DIR_HOURLY not defined"
exit 1
fi
# ------------- the script itself --------------------------------------
# make sure we're running as root
if [ `$ID -u` != 0 ] ; then
$ECHO "Sorry, must be root. Exiting..."
exit
fi
# attempt to remount the RW mount point as RW; else abort
$MOUNT -o remount,rw $MOUNT_DEVICE $SNAPSHOT_RW
if [ $? != 0 ] ; then
$ECHO "snapshot: could not remount $SNAPSHOT_RW readwrite"
exit
fi
# rotating snapshots of $DIR_TO_SAVE
# step 1: delete the oldest snapshot, if it exists:
if [ -d $SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.3 ] ; then
$RM -rf $SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.3
fi
# step 2: shift the middle snapshots(s) back by one, if they exist
if [ -d $SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.2 ] ; then
$MV $SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.2 $SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.3
fi
if [ -d $SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.1 ] ; then
$MV $SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.1 $SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.2
fi
# step 3: make a hard-link-only (except for dirs) copy of the latest snapshot,
# if that exists
if [ -d $SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.0 ] ; then
$CP -al $SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.0 $SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.1
fi
# step 4: rsync from the system into the latest snapshot (notice that
# rsync behaves like cp --remove-destination by default, so the destination
# is unlinked first. If it were not so, this would copy over the other
# snapshot(s) too!
$DATE "+%Y.%m.%d/%T mk_snapshot.sh:"
$RSYNC -a --delete --delete-excluded \
--exclude-from="$EXCLUDES" \
$DIR_TO_SAVE/ $SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.0
# step 5: update the mtime of $DIR_HOURLY.0 to reflect the snapshot time
$TOUCH $SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.0
# and thats it for home.
# now remount the RW snapshot mountpoint as readonly
$MOUNT -o remount,ro $MOUNT_DEVICE $SNAPSHOT_RW
if [ $? != 0 ] ; then
$ECHO "snapshot: could not remount $SNAPSHOT_RW readonly"
exit
fi
:code:
Das Script kommt auf vielen Rechnern zum Einsatz, daher sind die rechnerspezifischen Daten ausgelagert:
:code:(sh)
#!/bin/sh
#/home/bin/rex/snapshot.data
MOUNT_DEVICE=/dev/sda21
SNAPSHOT_RW=/sicherung
EXCLUDES=/home/bin/rex/snapshot_exclude.lst
DIR_TO_SAVE=/home
DIR_HOURLY=sicherung
In der Crontab steht:
1 */4 * * * /home/bin/std/mk_snapshot.sh >>/var/log/rex/snapshot.log
Das folgende Script kopiert die Tages-, Wochen und Monatsversionen:
#! /bin/sh
# Aufruf: snapshot_rotate.sh [ day week month ]
#
ID=id
ECHO=echo
MOUNT=/bin/mount
RM=rm
MV=mv
CP=cp
TOUCH=/bin/touch
HOST=`hostname`
DATE=/bin/date
SET=set
# make sure we're running as root
if [ `$ID -u` != 0 ] ; then
$ECHO "Sorry, must be root. Exiting..."
exit 1
fi
source /home/bin/$HOST/snapshot.data
if [ -z "$MOUNT_DEVICE" ] ; then
$ECHO "MOUNT_DEVICE not defined"
exit 1
fi
if [ -z "$SNAPSHOT_RW" ] ; then
$ECHO "SNAPSHOT_RW not defined"
exit 1
fi
if [ -z "$DIR_TO_SAVE" ] ; then
$ECHO "DIR_TO_SAVE not defined"
exit 1
fi
if [ -z "$DIR_HOURLY" ] ; then
$ECHO "DIR_HOURLY not defined"
exit 1
fi
MODE=$1
if [ -z "$MODE" ] ; then
MODE=day
fi
case "$MODE" in
day)
TARGET=`$DATE "+%A"`
break;;
week)
TARGET=`$DATE "+woche_%V"`
break;;
month)
TARGET=`$DATE "+%b"`
break;;
*)
$ECHO "unknown mode (instead of: day week month)"
exit 1
esac
# attempt to remount the RW mount point as RW; else abort
$MOUNT -o remount,rw $MOUNT_DEVICE $SNAPSHOT_RW
if [ $? != 0 ] ; then
$ECHO "snapshot: could not remount $SNAPSHOT_RW readwrite"
exit
fi
SOURCE=$SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.0
if [ ! -d $SOURCE ] ; then
SOURCE=$SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.1
fi
if [ ! -d $SOURCE ] ; then
SOURCE=$SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.2
fi
if [ ! -d $SOURCE ] ; then
SOURCE=$SNAPSHOT_RW$DIR_TO_SAVE/$DIR_HOURLY.3
fi
if [ ! -d $SOURCE ] ; then
$ECHO "no source available! $SOURCE"
exit 2
fi
$DATE "+%Y.%m.%d/%T: snapshot_rotate.sh $MODE"
TRG=$SNAPSHOT_RW$DIR_TO_SAVE/$TARGET
$SET -x
if [ -d $TRG ] ; then
$RM -fR $TRG
fi
$CP -al $SOURCE $TRG
$SET +x
# attempt to remount the RW mount point as RW; else abort
$MOUNT -o remount,ro $MOUNT_DEVICE $SNAPSHOT_RW
if [ $? != 0 ] ; then
$ECHO "snapshot: could not remount $SNAPSHOT_RW readonly"
exit
fi
Eintrag in /etc/anacrontab:
1 30 vlinux.backup.day nice -n -9 /home/bin/std/snapshot_rotate.sh day >>/var/log/rex/snapshot.log
7 60 vlinux.backup.week nice -n -9 /home/bin/std/snapshot_rotate.sh week >>/var/log/rex/snapshot.log