* er grundig
* kan fortsætte fornuftigt på trods af fejl
* kan modificeres til dagligdagen uden for meget arbejde
* kan bruges på flere systemer uden tilpasning
Løsningen blev et 471 liniers shell script, som jeg deler så andre måske også kan få glæde af det, eller blive inspireret til nye løsninger. Scriptet er grundigt testet, og har kørt på 2 servere i snart en måned uden at vise fejl.
Kode: Vælg alt
#!/bin/bash
#
# VPS backupscript
# Made by mad hound nicky
#
# Modular built, this script can easily be configured through it's header.
# It can back up the database and selected filesystem locations, while keeping and rotating
# optional local copys, and rotating remote copys through the use of FTP, if need be.
# Good luck.
#
# License = /dev/null
#
######################################################################################
# *** Start Settings ***
######################################################################################
#
# Set to anything but "yes" to deactivate sending mail
MAIL_SEND="yes"
#
# What's the mail of the server admin. Used in the body as a reply to, in case of problems
# Standard = SERVER_ADMIN_MAIL=""
SERVER_ADMIN_MAIL=""
#
# Who should recieve the mail - Multiple adresses --> "a@a.tld,b@b.tld"
# Standard = MAIL_ADDRESS="a@a.tld"
MAIL_ADDRESS=""
#
# And what should the subject be
# Standard = MAIL_SUBJECT="Status for *** at time $(date +%d.%m.%Y) - $(date +%k:%M)"
MAIL_SUBJECT="Status for backup af *** d.$(date +%d.%m.%Y) kl.$(date +%k:%M)"
#
# Absolute path for the backup dir (with no trailing slash please)
# Note that directorys will be made if need be
# Standard = BACKUP_DIR="/home/backup"
BACKUP_DIR="/home/backup"
#
# Should any local backups be kept and rotatet
# Please note that if FTP_USE is set further down, then 1 local backup will be kept no matter this setting
# Standard = KEEP_LOCAL="yes"
KEEP_LOCAL="yes"
#
# If set, what should the backup be called - Letters and underscore only please
# The name will be $BACKUP_NAME.tar.gz.$KEEP_LOCAL_ROTATE, and if changed, then old ones have to be deleted by hand
# Standard = BACKUP_NAME="backup"
BACKUP_NAME="backup"
#
# If set above, how many local backups should be kept
# Standard = KEEP_LOCAL_ROTATE="3"
KEEP_LOCAL_ROTATE=3
#
# Set to anything but yes to deaktivate local rotation of the database
# This will deactivate backup of the databases altogether if not set to yes
# Standard = DUMP_DATABASE="yes"
DUMP_DATABASE="yes"
#
# If set, what user and password should be used by mysqldump
# Standard = DUMP_USER="backup" --- DUMP_PASS="some_random_stuff"
DUMP_USER="backup"
DUMP_PASS=""
#
# If set, what should the dump of the database be called - Letters and underscore only please
# The name will be $DUMP_NAME.sql.gz.$ROTATE_DATABASE, and if changed, then old ones have to be deleted by hand
# Standard = DUMP_NAME="dump"
DUMP_NAME="dump"
#
# If set, how many dumps of the database will need to be kept and rotatet
# Be aware that if the number is reduced a manuel delete of the dumps over the rotation number is needed
# Standard = ROTATE_DATABASE=7
ROTATE_DATABASE=7
#
# Set to anything but "yes" to deactivate the use of FTP - Please insure that 'ftp' is installed if needed
# Note that limitations prevent errorchecking of the FTP transfer itself. However, if mail is used,
# a copy of the FTP servers contents will be mailed after the rotation has taken place
# Standard = FTP_USE="yes"
FTP_USE="yes"
#
# If set above, how many backups should be kept and rotatet through FTP
# Be aware that if the number is reduced, then a manuel delete of the backups over the rotation number is needed
# Standard = ROTATE_FTP=14
ROTATE_FTP=14
#
# Set to anything but yes to deactivate weekly backup to FTP
# The name of this backup will be $BACKUP_NAME.weekly.tar.gz.$ROTATE_FTP
# Standard = FTP_USE_WEEKLY="yes"
FTP_USE_WEEKLY="yes"
#
# If FTP is set, what day should the weekly backup to FTP run - 1 = monday ... 7 = sunday
# Standard = FTP_USE_WEEKLY_DAY=5
FTP_USE_WEEKLY_DAY=5
#
# If FTP is set, how many copys should be kept and rotatet through FTP on a weekly basis
# Be aware that if the number is reduced, then a manuel delete of the backups over the rotaion number is needed
# Standard = ROTATE_FTP_WEEKLY=12
ROTATE_FTP_WEEKLY=12
#
# If FTP is set, then these 3 needs to be filled out with the proper information
# Set host to hostname or public IP address, and modify user and pass to something appropriate
# Standard = FTP_HOST="" --- FTP_USER="" --- FTP_PASS=""
FTP_HOST=""
FTP_USER=""
FTP_PASS=""
#
# Set to anything but "yes" to deactivate the use of rsync - Please insure that 'rsync' is installed if needed
# Standard = RSYNC_USE="yes"
RSYNC_USE="yes"
#
# If set above, what dir under $BACKUP_DIR should rsync use (with no slahes)
# Standard = RSYNC_DIR="rsync"
RSYNC_DIR="rsync"
#
# Also if set, from where should rsync back stuff up from
# Standard = RSYNC_ORIGIN="/from/dir1 \
# /from/dir2"
RSYNC_ORIGIN="/etc/apache2 \
/etc/dovecot \
/etc/postfix \
/home/vmail"
#
# Set to anything but "yes" to deactivate backup of single files
# Standard = COPY_SINGLES_USE="yes"
COPY_SINGLES_USE="yes"
#
# If set above, what dir under the backup dir should the singles be placed in (with no slahes)
# Standard = SINGLES_DIR="singles"
SINGLES_DIR="singles"
#
# And if set above, list of single config files to be backed up. Errors in COPY_SINGLES() will not be processed
# Standard = COPY_SINGLES() {
# cp -u /dir/file $BACKUP_DIR/$SINGLES_DIR/
# }
COPY_SINGLES() {
cp -u /etc/default/varnish $BACKUP_DIR/$SINGLES_DIR/
cp -u /etc/varnish/default.vcl $BACKUP_DIR/$SINGLES_DIR/
cp -u /etc/webalizer/webalizer.conf $BACKUP_DIR/$SINGLES_DIR/
}
######################################################################################
# *** Settings End ***
# Changes below here is not needed for daily usage
######################################################################################
#
# We really need which and can't (nor won't we) go on without it
if [ -e "/usr/bin/which" ] ; then
WHICH="/usr/bin/which"
else
# If mail is set we'll try and inform about this error in a mail
if [ "$MAIL_SEND" = "yes" ] ; then
MAIL_BODY="Programmet which blev ikke fundet, men skal bruges. Scriptet afslutter"
echo $MAIL_BODY | /usr/bin/mail -s "$MAIL_SUBJECT" $MAIL_ADDRESS
fi
exit 10
fi
#
# We prefer mail but don't really need it, and only if set will we check it
if [ "$MAIL_SEND" = "yes" ] ; then
MAIL_BIN=$($WHICH mail)
if [ "$?" -ne "0" ] ; then
MAIL_SEND="NO"
MAIL_BODY="Programmet mmail kunne ikke findes, så ingen email vil blive lavet."
echo $MAIL_BODY | /usr/bin/mail -s "$MAIL_SUBJECT" $MAIL_ADDRESS
MAIL_SEND="NO"
break
fi
# If need be we can now create the temp file needed to hold the body of the mail in the variable $MAIL_MESSAGE
MAIL_MESSAGE="/tmp/Backup_Mail_Message.$(date +%j)"
touch $MAIL_MESSAGE
if [ "$?" -ne "0" ] ; then
MAIL_BODY="Tempfilen til email'en kunne ikke oprettes, så ingen email vil blive lavet."
echo $MAIL_BODY | /usr/bin/mail -s "$MAIL_SUBJECT" $MAIL_ADDRESS
MAIL_SEND="NO"
break
fi
fi
#
# MySQL-dump is also preferred, but not needed. Plenty of other other fish in the seas
if [ "$DUMP_DATABASE" = "yes" ] ; then
MYSQLDUMP=$($WHICH mysqldump)
if [ "$?" -ne "0" ] ; then
echo >> $MAIL_MESSAGE
echo "MySQL-dump kunne ikke findes, så databasen FÅR IKKE lavet backup!" >> $MAIL_MESSAGE
DUMP_DATABASE="NO"
fi
fi
#
# Same goes for rsync
if [ "$RSYNC_USE" = "yes" ] ; then
RSYNC=$($WHICH rsync)
if [ "$?" -ne "0" ] ; then
echo >> $MAIL_MESSAGE
echo "rsync kunne ikke findes, så $RSYNC_ORIGIN FÅR IKKE lavet backup!" >> $MAIL_MESSAGE
RSYNC_USE="NO"
fi
fi
#
# And ftp
if [ "$FTP_USE" = "yes" ] ; then
FTP=$($WHICH ftp)
if [ "$?" -ne "0" ] ; then
echo >> $MAIL_MESSAGE
echo "ftp kunne ikke findes, så backuppen KAN IKKE kopieres til en FTP-server!" >> $MAIL_MESSAGE
FTP_USE="NO"
fi
fi
#
# We won't be checking cp, echo and so on, since Linux in general depends on those
#
# We'd better check the backupdir and make it if it's nonexistent
if [ ! -d "$BACKUP_DIR" ] ; then
mkdir -p "$BACKUP_DIR"
# Since the entire backup depends on this dir we'll check it
if [ "$?" -ne "0" ] ; then
# In case a mail should be sent we need to preserve the exit status of mkdir
EXIT_STATUS="$?"
if [ "$MAIL_SEND" = "yes" ] ; then
MAIL_BODY="Destinationen = $BACKUP_DIR til backuppen kunne ikke oprettes med fejl $EXIT_STATUS."
MAIL_BODY="$MAIL_BODY Scriptet afslutter UDEN at have kørt nogen backup!"
echo $MAIL_BODY | $MAIL_BIN -s "$MAIL_SUBJECT" $MAIL_ADDRESS
fi
# We can't very well do a backup without a destination
exit $EXIT_STATUS
fi
fi
#
# Lets dump any and all databases on the system if it is wanted and possible
if [ "$DUMP_DATABASE" = "yes" ] ; then
# The dump itself
$MYSQLDUMP -u$DUMP_USER -p$DUMP_PASS --all-databases > "$BACKUP_DIR/$DUMP_NAME.sql"
# And only if dump is successfull will we go on with the ratotion
if [ "$?" -ne "0" ] ; then
echo >> $MAIL_MESSAGE
echo "MySQL-dump kunne ikke gennemføres med fejl $?, så INGEN BACKUP AF DATABASEN!" >> $MAIL_MESSAGE
break
fi
#
# Note the missing fi here. It's further down as we only work with database/rotate if set
#
# We'll start by removing the oldest dump. No error needed if it's not there
if [ -e "$BACKUP_DIR/$DUMP_NAME.sql.gz.$ROTATE_DATABASE" ] ; then
rm $BACKUP_DIR/$DUMP_NAME.sql.gz.$ROTATE_DATABASE
fi
#
# Then comes the rotation itself, starting by setting the required extra variable for renaming
ROTATE_LOOP=$ROTATE_DATABASE
ROTATE_DATABASE=$((ROTATE_DATABASE-1))
# This loop will run until $ROTATE_DATABASE reaches a value of zero
while [ "$ROTATE_DATABASE" -ne "0" ] ; do
# No error needed if the dump ain't there
if [ -e "$BACKUP_DIR/$DUMP_NAME.sql.gz.$ROTATE_DATABASE" ] ; then
mv "$BACKUP_DIR/$DUMP_NAME.sql.gz.$ROTATE_DATABASE" "$BACKUP_DIR/$DUMP_NAME.sql.gz.$ROTATE_LOOP"
fi
# One rotation down, X minus 1 rotations to go
ROTATE_DATABASE=$((ROTATE_DATABASE-1))
ROTATE_LOOP=$((ROTATE_LOOP-1))
done
#
# If the dump is in existence it's time to compress the sucker and save it in the right place
if [ -e "$BACKUP_DIR/$DUMP_NAME.sql" ] ; then
gzip -c "$BACKUP_DIR/$DUMP_NAME.sql" > "$BACKUP_DIR/$DUMP_NAME.sql.gz.1"
# Let's see if gzip ran probably
if [ "$?" -ne "0" ] ; then
echo >> $MAIL_MESSAGE
echo "gzip afsluttede med fejl $?," >> $MAIL_MESSAGE
echo "så dump af databasen kunne ikke afsluttes med succes." >> $MAIL_MESSAGE
break
fi
# Appends time of completion for MySQL-dump to the mail since the databases where dumped with success
echo >> $MAIL_MESSAGE
echo "Dump af MySQL blev gennemført og roteret." >> $MAIL_MESSAGE
else
# No dump is kind of wierd. we need to know about that
echo >> $MAIL_MESSAGE
echo "MySQL dump kunne ikke findes efter dump, hvilket er lidt underligt." >> $MAIL_MESSAGE
echo "Grunden hertil bør nok undersøges før end siden." >> $MAIL_MESSAGE
fi
#
# This is the one and only missing fi from "if [ "$ROTATE_DATABASE ... "
# Without her we'd be lost http://tomorrow-series.eu/en/books/fiona-maxwell/
fi
#
# And if the dump still lives in $BACKUP_DIR it's time to free some space
# Since errors about this dump should've been taken care of we won't check the result in any way
if [ -e "$BACKUP_DIR/$DUMP_NAME.sql" ] ; then
rm "$BACKUP_DIR/$DUMP_NAME.sql"
fi
#
# Before we unleash rsync some tests should be conducted as per usual
if [ "$RSYNC_USE" = "yes" ] ; then
if [ ! -d "$BACKUP_DIR/$RSYNC_DIR" ] ; then
mkdir "$BACKUP_DIR/$RSYNC_DIR"
# And we'll check that too
if [ "$?" -ne "0" ] ; then
# If the dir could'nt be made we'll break out of this loop and skip rsync all together
echo >> $MAIL_MESSAGE
echo "Mappen $BACKUP_DIR/$RSYNC_DIR til rsync kunne ikke oprettes med fejl $?." >> $MAIL_MESSAGE
echo "Dette betyder at rsync IKKE HAR KØRT!" >> $MAIL_MESSAGE
break
fi
fi
# It shuold now be safe and sound to run rsync
$RSYNC --delete-after -avz $RSYNC_ORIGIN "$BACKUP_DIR/$RSYNC_DIR"
# Cheking now ...
if [ "$?" -ne "0" ] ; then
echo >> $MAIL_MESSAGE
echo "rsync afsluttede med fejl $?." >> $MAIL_MESSAGE
echo "Grunden hertil bør nok undersøges før end siden." >> $MAIL_MESSAGE
break
fi
# If rsync ran with success we'll want to know it
echo >> $MAIL_MESSAGE
echo "Backup med rsync blev gennemført." >> $MAIL_MESSAGE
fi
#
# Time to copy and/or update single config files if wanted
if [ "$COPY_SINGLES_USE" = "yes" ] ; then
# Better check the dest dir for the singles
if [ ! -d "$BACKUP_DIR/$SINGLES_DIR" ] ; then
mkdir "$BACKUP_DIR/$SINGLES_DIR"
# No point in going on without a dest dir, but we surely wants to know about this
if [ "$?" -ne "0" ] ; then
echo >> $MAIL_MESSAGE
echo "Mappen til enkeltfilerne kunne ikke oprettes med fejl $?," >> $MAIL_MESSAGE
echo "så enkeltfilerne er ikke blevet sikkerhedskopieret." >> $MAIL_MESSAGE
break
fi
fi
# Let the copy/update of singles begin with no errorchecking whatsoever
COPY_SINGLES
echo >> $MAIL_MESSAGE
echo "Backup af enkeltfiler gennemført." >> $MAIL_MESSAGE
fi
#
# Wether or not we'll need local backup, the tar and gunzip routine will be made because FTP might use it later on
# But since we only wants to run tar_local once, we'll check both cases in the next if
tar_local() {
# tar will throw errors if dirs and files don't exists, but it will go on and finish the job anyway
# Please note that this command spand multiple lines
tar -cf - "$BACKUP_DIR/$DUMP_NAME.sql.gz.1" "$BACKUP_DIR/$RSYNC_DIR/" "$BACKUP_DIR/$SINGLES_DIR" |\
gzip > "$BACKUP_DIR/$BACKUP_NAME.tar.gz.1"
}
#
# Now we'll look at the local backups if set
if [ "$KEEP_LOCAL" = "yes" ] ; then
# First goes the old one, if any
if [ -e "$BACKUP_DIR/$BACKUP_NAME.tar.gz.$KEEP_LOCAL_ROTATE" ] ; then
rm "$BACKUP_DIR/$BACKUP_NAME.tar.gz.$KEEP_LOCAL_ROTATE"
fi
#
# Then we'll rotate 'em all, starting by setting a variable for the loop like we did with the dump
ROTATE_LOOP=$KEEP_LOCAL_ROTATE
KEEP_LOCAL_ROTATE=$((KEEP_LOCAL_ROTATE-1))
# This loop will run until $KEEP_LOCAL_ROTATE reaches a value of zero
while [ "$KEEP_LOCAL_ROTATE" -ne "0" ] ; do
# No error needed if the backups ain't here
if [ -e "$BACKUP_DIR/$BACKUP_NAME.tar.gz.$KEEP_LOCAL_ROTATE" ] ; then
mv "$BACKUP_DIR/$BACKUP_NAME.tar.gz.$KEEP_LOCAL_ROTATE" "$BACKUP_DIR/$BACKUP_NAME.tar.gz.$ROTATE_LOOP"
fi
# One rotation down, X minus 1 rotations to go
KEEP_LOCAL_ROTATE=$((KEEP_LOCAL_ROTATE-1))
ROTATE_LOOP=$((ROTATE_LOOP-1))
done
#
# And now a new backup.tar.gz.1 can be made
tar_local
#
# If $KEEP_LOCAL is not set, but FTP is, we still need to tar and gunzip from $BACKUP_DIR
else
# Checking FTP
if [ "$FTP_USE" = "yes" ] ; then
# If an old backup exists we need to get rid of it
if [ -e "$BACKUP_DIR/$BACKUP_NAME.tar.gz.1" ] ; then
rm "$BACKUP_DIR/$BACKUP_NAME.tar.gz.1"
fi
# And now a backup to use with FTP can be created
tar_local
fi
fi
#
# It's FTP time (if set and all)
if [ "$FTP_USE" = "yes" ] ; then
# To make life easier for ourselves we'll use ftp_con() to do the hard labor
ftp_con() {
echo "open $FTP_HOST
user $FTP_USER $FTP_PASS
verbose
$1 $2 $3
close
quit" | $FTP -n
}
#
# First up the old one have to go at the FTP site with the use of the FTP function
# If it's nonexistent the server will throw a error but that does'nt really matter
ftp_con delete $BACKUP_NAME.tar.gz.$ROTATE_FTP
#
# Then the variables for the loop needs setting
ROTATE_LOOP=$ROTATE_FTP
ROTATE_FTP=$((ROTATE_FTP-1))
# And then comes the rotate itself
while [ "$ROTATE_FTP" -ne "0" ] ; do
# Again errors will be thrown if the files does'nt exists but it's of no consequence
ftp_con rename $BACKUP_NAME.tar.gz.$ROTATE_FTP $BACKUP_NAME.tar.gz.$ROTATE_LOOP
# One rotation down, X minus 1 rotations to go
ROTATE_FTP=$((ROTATE_FTP-1))
ROTATE_LOOP=$((ROTATE_LOOP-1))
done
#
# And up goes the new backup
ftp_con put "$BACKUP_DIR/$BACKUP_NAME.tar.gz.1" "$BACKUP_NAME.tar.gz.1"
#
# Better inform someone that daily backup tp FTP is done
echo >> $MAIL_MESSAGE
echo "Daglig backup til FTP gennemført." >> $MAIL_MESSAGE
#
# If weekly backup to FTP is set we'll rotate that too
if [ "$FTP_USE_WEEKLY" = "yes" ] ; then
# But only if it's the right weekday
if [ "$(date +%u)" = "$FTP_USE_WEEKLY_DAY" ] ; then
#
# Better inform someone that we'll go on with the weekly backup to FTP
echo >> $MAIL_MESSAGE
echo "Æteren siger det er $(date +%A), så fortsætter med ugebackup til FTP." >> $MAIL_MESSAGE
# If it is so, then the old one have to go
ftp_con delete $BACKUP_NAME.weekly.tar.gz.$ROTATE_FTP_WEEKLY
#
# Then the variables
ROTATE_LOOP=$ROTATE_FTP_WEEKLY
ROTATE_FTP_WEEKLY=$((ROTATE_FTP_WEEKLY-1))
# And then the rotate goes
while [ "$ROTATE_FTP_WEEKLY" -ne "0" ] ; do
# Still no care if errors are thrown
ftp_con rename $BACKUP_NAME.weekly.tar.gz.$ROTATE_FTP_WEEKLY $BACKUP_NAME.weekly.tar.gz.$ROTATE_LOOP
# One rotation down, X minus 1 rotations to go
ROTATE_FTP_WEEKLY=$((ROTATE_FTP_WEEKLY-1))
ROTATE_LOOP=$((ROTATE_LOOP-1))
done
#
# And up goes the new backup, with a twist in the filename on the recieving side
ftp_con put "$BACKUP_DIR/$BACKUP_NAME.tar.gz.1" "$BACKUP_NAME.weekly.tar.gz.1"
#
# Better inform someone that the weekly backup to FTP is done
echo "Ugentlig backup til FTP gennemført." >> $MAIL_MESSAGE
fi
fi
#
# Since no error checking is done on the FTP transfers itself, we'll mail a list of files on the FTP server
FTP_FILE_LIST=$(ftp_con ls)
echo >> $MAIL_MESSAGE
echo "Filer på FTP-serveren:" >> $MAIL_MESSAGE
echo "$FTP_FILE_LIST" >> $MAIL_MESSAGE
fi
#
# Last desprate act of the show: Send an mail if $MAIL_SEND = "yes"
if [ "$MAIL_SEND" = "yes" ] ; then
# Let's see if the file is empty
if [ ! -s "$MAIL_MESSAGE" ] ; then
echo "Alle poster i hovedet er deaktiveret, så ingenting er blevet kørt." >> $MAIL_MESSAGE
echo "Exit status 42, hav en god dag :)" >> $MAIL_MESSAGE
fi
# Time to send the mail with a contact note
echo >> $MAIL_MESSAGE
echo "Der kan ikke svares direkte på denne mail, kontakt i stedet serverens administrator på" >> $MAIL_MESSAGE
echo "$SERVER_ADMIN_MAIL, og bed om at blive fjernet fra modtagerlisten." >> $MAIL_MESSAGE
echo "Email sendt kl.$(date +%k:%M)." >> $MAIL_MESSAGE
$MAIL_BIN -s "$MAIL_SUBJECT" $MAIL_ADDRESS < $MAIL_MESSAGE
#
# Better remove the mail from /tmp if it should be there
if [ -e "$MAIL_MESSAGE" ] ; then
rm "$MAIL_MESSAGE"
fi
fi
#
# Yeah, we made it!
exit 0