backing up your xen domains
backups are boring, but we all know how important they are. backups can also be quite powerful when working with xen virtualization, since xen allows for convenient back-up and restore of entire systems.
i've recently been working on a flexible, general-purpose script enabling incremental backups of complete xen guests, optimized for secure, distributed environments; xenBackup. if you're working with xen, you might find it useful.
the xenBackup script leverages open-source components like ssh, rsync, and rdiff-backup to create a simple, efficient and functional solution.
all code and configurations have been tested on debian etch but should be useful for other *nix flavors with subtle modifications. if you're unfamiliar with xen, you might consider starting with an earlier how-to on setting up xen on your debian etch box
a general approach
the approach you take to backups obviously depends on what your guests are doing. let's consider one of the more difficult cases, backing up a xen guest with an application server and a database running on it. ideally, you'd typically:- take regular backups of the database.
- take a regular incremental backup of the entire machine.
- write the backups onto a different server on your network, and hopefully to a different geographical location too.
- do all of this without any interruption of your service.
in this article we'll discuss a simple way of doing this.
the xenBackup script
thexenBackup script, which i've included at the end of this article, helps implement a xen backup strategy. it automates the backup of single or multiple xen guests using one of three backup methods, tar, rsync or rdiff-backup.
the usage message for xenBackup is as follows:
Usage: xenBackup [OPTION]...
Backup xen domains to a target area. different backup engines may be specified to
produce a tarfile, an exact mirror of the disk area or a mirror with incremental backup.
-d backup only the specified DOMAIN
-t target LOCATION for the backup e.g. /tmp or root@www.example.com:/tmp
(not used for tar engine)
-a backup all domains
-s shutdown domains before backup (and restart them afterwards)
-q run in quiet mode
-e backup ENGINE to use, either tar, rsync or rdiff-backup
-p purge increments older than TIME_SPEC. this option only applies
to rdiff-backup, e.g. 3W for 3 weeks. see "man rdiff-backup" for
more informationto illustrate how it could be used, let's consider a typical scenario.
scenario: a single xen server with multiple xen guests
consider a xen server with multiple guests running on it, where the database on each guest backs up locally using a database specific backup technique e.g. a regularly scheduled hot backup writing to the local file system.
xenBackup could be used to periodically backup each of the local guests from the dom0. this is safe to do on a running server since the database backup does not rely on datafile consistency, but instead on the hot backups.
alternatively, the hot backup could be avoided if each of the guests was cleanly shutdown before the backup. xenBackup supports both these modes of operation but the former is recommended.
the xenBackup command on dom0 to incrementally backup all guests to /var/backup would simply be:
$ sudo xenBackup -a -e rdiff-backup -t /var/backupthis arrangement is shown in the diagram above. additionally, this backup could be periodically pulled from another backup server using rsync over ssh. this backup server could be located on or off-site.
this could be simplified by writing the backup directly onto another server in a single xenBackup command. this is arguably less secure since you need to push the backup rather than pull it, but could be done with:
$ sudo xenBackup -a -e rdiff-backup -t root@backupserver:/var/backuprunning xenBackup on multiple dom0's the following arrangement can easily be achieved:
the backups
one of the great things about xen is that the backup allows you to reconstitute a fully working xen guest from the backup area, simply with a command like:$ sudo xen-create-image --copy=/var/backup/mymachine.rdiff-backup.mirror --ip=192.168.1.10 --hostname=mymachinexenBackup purge flag, -p). see man rdiff-backup for more information on using --restore-as-of on your backup directory.
dependencies
thexenBackup script has dependencies on rsync and rdiff-backup, if you choose to use those engines. if you do, you should install those packages:
$ sudo apt-get install rsync rdiff-backupautomation
to automate your backups, consider adding a cron entry to automatically runxenBackup on your backup server. typically you should do this at a quiet time. an example cron entry is:
00 1 * * * /usr/bin/xenBackup -q -a -t /var/backup -e rdiff-backupssh-copy-id and ssh-keygen for more information. give very careful consideration to security when determining the user to use and what machines can access what.
a word on syslog
xenBackup logs all backup output to syslog's local3 facility. all xenBackup's log output is available in the main syslog log, /var/log/syslog. in addition, a dedicated xenBackup log can be created by adding the following to your /etc/syslog.conf file:
# xenBackup logging: log all local3's messages to /var/log/xenBackup
local3.* /var/log/xenBackuptech blog
xenBackup - the source
here's the source for the backup script. please feel free to use, modify, plagiarize, mock, torture or hack up any of this bash code as your mood takes you.#!/bin/bash
#
# xenBackup - Backup Xen Domains
#
# Version: 1.0: Created
#
# initialize our variables
domains="null" # the list of domains to backup
allDomains="null" # backup all domains?
targetLocation="/tmp" # the default backup target directory
mountPoint="/mnt/xen" # the mount point to use to mount disk areas
xenDiskArea="/dev/skx-vg" # the LVM volume group to use
shutdownDomains=false # don't shutdown domains by default
quiet=false # keep the chatter down
backupEngine=tar # the default backup engine
rsyncExe=/usr/bin/rsync # rsync executable
rdiffbackupExe=/usr/bin/rdiff-backup # rdiff-backup executable
tarExe=/usr/bin/tar # tar executable
xmExe=/usr/sbin/xm # xm executable
purgeAge="null" # age at which to purge increments
globalBackupResult=0 # success status of overall job
# settings for logging (syslog)
loggerArgs="" # what extra arguments to the logger to use
loggerTag="xenBackup" # the tag for our log statements
loggerFacility="local3" # the syslog facility to log to
# trap user exit and cleanup
trap 'cleanup;exit 1' 1 2
cleanup()
{
${logDebug} "Cleaning up"
cd / ; umount ${mountPoint}
# restart the domain
if test ${shutdownDomains} = "true"
then
${logDebug} "Restarting domain"
${xmExe} create ${domain}.cfg > /dev/null
fi
}
# function to print a usage message and bail
usageAndBail()
{
cat << EOT
Usage: xenBackup [OPTION]...
Backup xen domains to a target area. different backup engines may be specified to
produce a tarfile, an exact mirror of the disk area or a mirror with incremental backup.
-d backup only the specified DOMAINs (comma seperated list)
-t target LOCATION for the backup e.g. /tmp or root@www.example.com:/tmp
(not used for tar engine)
-a backup all domains
-s shutdown domains before backup (and restart them afterwards)
-q run in quiet mode, output still goes to syslog
-e backup ENGINE to use, either tar, rsync or rdiff-backup
-p purge increments older than TIME_SPEC. this option only applies
to rdiff-backup, e.g. 3W for 3 weeks. see "man rdiff-backup" for
more information
Example 1
Backup all domains to the /tmp directgory
$ xenBackup -a -t /tmp
Example 2
Backup domain: "wiki" using rsync to directory /var/xenImages on machine backupServer,
$ xenBackup -e rsync -d wiki -t root@backupServer:/var/xenImages
Example 3
Backup domains "domainOne" and "domainTwo" using rdiff-backup purging old increments older than 5 days
$ xenBackup -e rdiff-backup -d "domainOne, domainTwo" -p 5D
EOT
exit 1;
}
# parse the command line arguments
while getopts p:e:qsad:t:h o
do case "$o" in
q) quiet="true";;
s) shutdownDomains="true";;
a) allDomains="true";;
d) domains="$OPTARG";;
t) targetLocation="$OPTARG";;
e) backupEngine="$OPTARG";;
p) purgeAge="$OPTARG";;
h) usageAndBail;;
[?]) usageAndBail
esac
done
# if quiet don't output logging to standard error
if test ${quiet} = "false"
then
loggerArgs="-s"
fi
# setup logging subsystem. using syslog via logger
logCritical="logger -t ${loggerTag} ${loggerArgs} -p ${loggerFacility}.crit"
logWarning="logger -t ${loggerTag} ${loggerArgs} -p ${loggerFacility}.warning"
logDebug="logger -t ${loggerTag} ${loggerArgs} -p ${loggerFacility}.debug"
# make sure only root can run our script
test $(id -u) = 0 || { ${logCritical} "This script must be run as root"; exit 1; }
# make sure that the guest manager is available
test -x ${xmExe} || { ${logCritical} "xen guest manager (${xmExe}) not found"; exit 1; }
# assemble the list of domains to backup
if test ${allDomains} = "true"
then
domainList=`${xmExe} list | cut -f1 -d" " | egrep -v "Name|Domain-0"`
else
# make sure we've got some domains specified
if test "${domains}" = "null"
then
usageAndBail
fi
# create the domain list by mapping commas to spaces
domainList=`echo ${domains} | tr -d " " | tr , " "`
fi
# function to do a "rdiff-backup" of domain
backupDomainUsingrdiff-backup ()
{
domain=$1
test -x ${rdiffbackupExe} || { ${logCritical} "rdiff-backup executable (${rdiffbackupExe}) not found"; exit 1; }
if test ${quiet} = "false"
then
verbosity="3"
else
verbosity="0"
fi
targetSubDir=${targetLocation}/${domain}.rdiff-backup.mirror
# make the targetSubDir if it doesn't already exist
mkdir ${targetSubDir} > /dev/null 2>&1
${logDebug} "backing up domain ${domain} to ${targetSubDir} using rdiff-backup"
# rdiff-backup to the target directory
${rdiffbackupExe} --verbosity ${verbosity} ${mountPoint}/ ${targetSubDir}
backupResult=$?
# purge old increments
if test ${purgeAge} != "null"
then
# purge old increments
${logDebug} "purging increments older than ${purgeAge} from ${targetSubDir}"
${rdiffbackupExe} --verbosity ${verbosity} --force --remove-older-than ${purgeAge} ${targetSubDir}
fi
return ${backupResult}
}
# function to do a "rsync" backup of domain
backupDomainUsingrsync ()
{
domain=$1
test -x ${rsyncExe} || { ${logCritical} "rsync executable (${rsyncExe}) not found"; exit 1; }
targetSubDir=${targetLocation}/${domain}.rsync.mirror
# make the targetSubDir if it doesn't already exist
mkdir ${targetSubDir} > /dev/null 2>&1
${logDebug} "backing up domain ${domain} to ${targetSubDir} using rsync"
# rsync to the target directory
${rsyncExe} -essh -avz --delete ${mountPoint}/ ${targetSubDir}
backupResult=$?
return ${backupResult}
}
# function to a "tar" backup of domain
backupDomainUsingtar ()
{
domain=$1
# make sure we can write to the target directory
test -w ${targetLocation} || { ${logCritical} "target directory (${targetLocation}) is not writeable"; exit 1; }
targetFile=${targetLocation}/${domain}.`date '+%d%b%y'`.$$.tar.gz
${logDebug} "backing up domain ${domain} to ${targetFile} using tar"
# tar to the target directory
cd ${mountPoint}
${tarExe} pcfz ${targetFile} * > /dev/null
backupResult=$?
return ${backupResult}
}
# backup the specified domains
for domain in ${domainList}
do
${logDebug} "backing up domain: ${domain}"
# make sure that the domain is shutdown if required
if test ${shutdownDomains} = "true"
then
${logDebug} "shutting down domain ${domain}"
${xmExe} shutdown -w ${domain} > /dev/null
fi
# unmount mount point if already mounted
umount ${mountPoint} > /dev/null 2>&1
# mount the xen disk read-only
xenDisk=${xenDiskArea}/${domain}-disk
test -r ${xenDisk} || { ${logCritical} "xen disk area not readable. are you sure that the domain \"${domain}\" exists?"; exit 1; }
${logDebug} "Mounting ${xenDisk} read-only"
mount -r ${xenDisk} ${mountPoint} || { ${logCritical} "mount failed, does mount point (${mountPoint}) exist?"; exit 1; }
# do the backup according to the chosen backup engine
backupDomainUsing${backupEngine} ${domain}
# make sure that the backup was successful
if test $? -ne 0
then
${logCritical} "FAILURE: error backing up domain ${domain}"
globalBackupResult=1
else
${logDebug} "SUCCESS: domain ${domain} backed up"
fi
# clean up
cleanup;
done
if test ${globalBackupResult} -eq 0
then
${logDebug} "SUCCESS: backup of all domains completed successfully"
else
${logCritical} "FAILURE: backup completed with some failures"
fi
exit ${globalBackupResult}- john's blog
- 1814 reads



delicious
digg
reddit
google
yahoo
Great script, I was looking
Great script, I was looking for a good way to backup my xen domains, thanks again! John!
Looks great, though it could
Looks great, though it could use support for loop mounted disk images :)
Awesome script work, John!
Awesome script work, John! Thanks!
post new comment