Local incremental snap shots with rsync

Reference only! Archived older discussions about backup and restore of the DiskStation to any client.

Moderators: Synology Inc, Honorary Moderator

Local incremental snap shots with rsync

Postby tiritas » Mon Nov 17, 2008 8:18 pm

After reading the Keeping incremental directory-tree snapshots using hardlinks and rsync/wget wiki article, I was impressed by how such backups can be done in a way that does not consume too much space. The concepts on which the wiki article was based are described in Easy Automated Snapshot-Style Backups with Linux and Rsync.

I decided to streamline the process described in the two articles and to improve the included snapshot creation code. The result can be found below and so far it seems to be working very well. I plan to post the script on the wiki, but I an a complete Linux and shell scripting newbie (this is my very first shell script), so I wanted to get some feedback on whether I'm breaking any rules, as well as other suggestions on how to improve the script.

The script backs up a set of folders to a specific local directory (I recommend backing on on an attached USB, or SATA drive) and makes snap shots of older backups, so that older versions of a file can be retrieved in addition to the latest backup up version. The code is an improvement on the techniques described in the wiki article, in the following ways:

  • Daily, weekly and monthly backup snapshots are created only if necessary (the script looks for the last time a particular type of backup was made. The original code would create a snapshot whenever the script was called.
  • Daily, weekly and monthly backup folders are deleted, not based on how many there are, but based on how old they are.
  • Only a single crontab entry is required for snapshot creation and the backup itself.
  • Backup and rotation results are logged in a file.
  • Backup and rotation results are emailed to the administrator (optional).

Note that the script can also be used for making snapshots of the backups created via the Synology remote backup feature (just leave the SRC parameter empty and point to the location where these backups are made). Similarly, it should be able to "pull" data from a remote rsync server, by including the server's host name in the SRC parameter. I have not tried either of these configurations.

Code: Select all
#!/opt/bin/bash

# Usage: rotate_backup.sh <rootpath> <dir> <src>
#
# <rootpath>:   The root location where backups are stored
# <dir>:      The name of the backup directory
# <src>:      The source directory being backup up. May contain
#            multiple directories, separated with spaces (enclose
#            the whole thing in quotes). If empty, then no backup
#            is performed.
#
# Example:
# ./rotate_backup.sh "/volumeUSB1/usbshare/" "RsyncLocalBackup" "/volume1/music /volume1/photo /volume1/video"
#
# Requirements: This script requires the following:
#   1.   Bash package
#   2.   Coreutils package, because the version of cp that comes with
#      the current Synology OS does not support hardlinks
#   3.   Findutils package, because the version of find that comes with
#      the current Synology OS does not support all the necessary
#      switches.
#   4.   Optional: Nail package for emailing logs to the administrator.


# The number of days to keep in each type of backup.
# Set the value to 0 to skip a particular type
DAILYHISTORY=6      # 6 daily backups
WEEKLYHISTORY=27   # 3 weekly backups
MONTHLYHISTORY=185   # 6 monthly backups

# If ADMIN_EMAIL is empty, then the log is not emailed to the administrator
ADMIN_EMAIL=tiritas@foxweb.com

#-----------------------------------------------------
# This function makes a hardlink copy of the backup forlder. The new folder
# name starts with "@", and contains the backup type and timestamp.
# The function also deletes old backup folders (based on the BACKUP_TYPE_AGE
# parameter.
function RotateFolder()
{
   BACKUP_TYPE=$1
   BACKUP_TYPE_INTERVAL=$2
   BACKUP_TYPE_AGE=$3
   
   if [ $BACKUP_TYPE_AGE -gt 0 ]; then
      HASBACKUP=`/opt/bin/findutils-find $ROOTPATH@$DIRNAME.$BACKUP_TYPE.* -maxdepth 0 -daystart -mtime -$(($BACKUP_TYPE_INTERVAL+1)) 2>/dev/null`
      if [ -z $HASBACKUP ]; then
         echo "Creating $BACKUP_TYPE copy of backup"
         # Create a new directory appended with date
         NEWDIR=@$DIRNAME.$BACKUP_TYPE.`date +\%Y\%m\%d`
         rm -rf $ROOTPATH$NEWDIR
         /opt/bin/coreutils-cp -al $ROOTPATH$DIRNAME $ROOTPATH$NEWDIR
      fi
   fi

   # Delete directories that are older then the specified age for this type
   /opt/bin/findutils-find $ROOTPATH@$DIRNAME.$BACKUP_TYPE.* -maxdepth 0 -daystart -mtime +$BACKUP_TYPE_AGE -exec rm -rf {} \; 2>/dev/null
}


#-----------------------------------------------------
# This function does all the work
function MainFunction()
{
   ROOTPATH=$1
   DIRNAME=$2
   SRC=$3

   echo "
------------------------------------------
`date`
" >> $LOG_FILE

   # Make sure that we are using a newer version of cp. Older versions do not support the -al switch to create hardlinks
   if [ ! -f "/opt/bin/coreutils-cp" ]; then                       
      echo "The cp command from the coreutils package was not found (default cp will not work). Install coreutils package" >&2
      return 1
   fi

   if [ ! -f "/opt/bin/findutils-find" ]; then                       
      echo "The find command from the findutils package was not found (default find will not work). Install findutils package" >&2
      return 1
   fi

   if [ -z "$ROOTPATH" ]; then                       
      echo "Must specify ROOTPATH" >&2
      return 1
   fi

   if [ -z "$DIRNAME" ]; then                       
      echo "Must specify DIRNAME" >&2
      return 1
   fi

   if [ ! -d "$ROOTPATH" ]; then                       
      echo "ROOTPATH directory ('$ROOTPATH') does not exist" >&2
      return 1
   fi

   # Make historical hardlink copies of our backup directory
   if [ -d "$ROOTPATH$DIRNAME" ]; then                         
      echo "Checking whether rotation is necessary for $ROOTPATH$DIRNAME"
      echo
      # We already have at least one backup
      RotateFolder daily 1 $DAILYHISTORY
      RotateFolder weekly 7 $WEEKLYHISTORY
      RotateFolder monthly 30 $MONTHLYHISTORY
   else
      echo "$ROOTPATH$DIRNAME does not exist. No rotation is necessary"
      echo
   fi

   if [ -z "$SRC" ]; then                       
      echo "No backup source was specified. Skipping backup."
   else
      # Perform the actual backup
      echo "Backing up \"$SRC\" to $ROOTPATH$DIRNAME"
      echo
      rsync -av --delete --delete-excluded --exclude=#recycle/ --exclude=@eaDir/ $SRC $ROOTPATH$DIRNAME/
   fi

   # Change the backup directory modification date, by adding a file
   # to the root of the backup location, so that purging works
   if [ -d "$ROOTPATH$DIRNAME" ]; then                         
      rm -f $ROOTPATH$DIRNAME/@BackupTime
      date > $ROOTPATH$DIRNAME/@BackupTime
   fi
}



#-----------------------------------------------------
# Script starts here
# The reason for wrapping MainFunction, which does the actual work,
# is so that we can email the log to the administrator.


# The LOG_FILE has the same name as this script, but with a .log extension
# and is located in /var/log/. It contains a cumulative log.
LOG_FILE=/var/log/`expr match "$0" '.*\/\(.*\)\..*'`.log

# The EMAIL_LOG_FILE only contains the output of the current run only and
# is used to email the results to the administrator.
EMAIL_LOG_FILE=/tmp/`expr match "$0" '.*\/\(.*\)\..*'`.log

MainFunction "$1" "$2" "$3"  2>&1 | tee $EMAIL_LOG_FILE | tee -a $LOG_FILE

# Mail the log to the administrator
if [ ! -z "$ADMIN_EMAIL" ]; then
   if [ ! -f "/opt/bin/nail" ]; then                       
      echo "Nail package not found. The log cannot be mailed to the administrator." >&2
   else
      /opt/bin/nail -s "$HOSTNAME: Local Backup Results" "$ADMIN_EMAIL" < $EMAIL_LOG_FILE
   fi
fi

rm -f $EMAIL_LOG_FILE


As I mentioned above, I'm a complete beginner, when it comes to writing shell scripts. If you spot something, please let me know.

Important: You need to disable native Local Backup on your DiskStation, or at least make sure that $ROOTPATH$DIRNAME does not point to the same location as the destination of Local Backup. If not, then the two methods of backing up will step on each other and you will probably end up with broken snapshot directories.

Thanks,
Pando
Last edited by tiritas on Wed Nov 26, 2008 10:51 pm, edited 2 times in total.
tiritas
I'm New!
I'm New!
 
Posts: 3
Joined: Mon Nov 17, 2008 7:40 pm

Re: Local incremental snap shots with rsync

Postby itari » Tue Nov 18, 2008 8:45 am

nice work!

2 hints:
(1) look at the options: /opt/bin/rsync -aHKcxv --del --link-dest=lastbackup source newbackup
(2) date .... echos to stdout - you need not to backquote

How du you make a restore of a snapshot? Maybe you want to restore to the snapshot of last week? Total restore is also one of the missing features in the DS manager.

itari
User avatar
itari
Sharp
Sharp
 
Posts: 169
Joined: Mon Jun 16, 2008 9:17 am
Location: Germany

Re: Local incremental snap shots with rsync

Postby tiritas » Tue Nov 18, 2008 7:19 pm

itari wrote:(1) look at the options: /opt/bin/rsync -aHKcxv --del --link-dest=lastbackup source newbackupitari


The -c option can really slow down the backup, because the system has to create a checksum for every single file in the backup set. The -H option may be useful if the source contains hard-linked files, so feel free to add it. The script already uses the -a and -v options, although you may want to remove the -v (verbose) option if a lot of files are being changed on a regular basis to prevent email and file logs from getting too big. I am not sure how -K and -x would help in this situation.

Regarding the --link-dest option, I am not using it, because I'm using a separate "cp -al" instead, so that I can have better control over the creation of snapshot folders.

itari wrote:(2) date .... echos to stdout - you need not to backquote


I'm pretty sure that the only times that date is being backquoted is when it is included in strings, such as in the first echo statement at the top of MainFunction. It's a little confusing, because in that case the string spans multiple lines. Am I missing something? Please provide an example.

itari wrote:How du you make a restore of a snapshot? Maybe you want to restore to the snapshot of last week? Total restore is also one of the missing features in the DS manager.


Snapshots appear as regular directories with names like @RsyncLocalBackup.daily.20081118. These directories contain a complete list of files from the backup that occurred at time the snapshot was created, so you can simply restore by copying files (either from the CLI, with File Station, or, if you are not restoring large amounts of data, via shares on client computers). Note that the snapshot directories are created as hardlinks with "cp -al" command, which means that your shapshots do not occupy additional space on your disk system, unless files were subsequently edited. For details please refer to the excellent Easy Automated Snapshot-Style Backups with Linux and Rsync article.

-Pando
tiritas
I'm New!
I'm New!
 
Posts: 3
Joined: Mon Nov 17, 2008 7:40 pm

Re: Local incremental snap shots with rsync

Postby jabraham » Mon Apr 13, 2009 2:29 pm

Do you have any updates on this? I am copying your script. I'm using the Synology as a backup store, and it's using rsync to pull the original data over from my Mac.

--
John
jabraham
I'm New!
I'm New!
 
Posts: 9
Joined: Sat Sep 01, 2007 7:09 am

Re: Local incremental snap shots with rsync

Postby vicne » Sun Dec 13, 2009 4:31 pm

Thanks for your great work (as well as Frank Schmidt and Mike Rubel before you :-) )

Although I haven't tested it thoroughly, it looks promising and is exactly what I needed. I think it deserves its place on the wiki.

However, I think you should edit the script and remove your email address as ADMIN_EMAIL, because I was ready to execute it when I realized I would have sent my backup results to you, which is a privacy breach for both of us...

Best regards,

Vicne
DS-109 (FW DSM 2.2-0942) with a 1.5 TB HD154UI Samsung internal HDD and an external 1TB IOMega USB drive
vicne
Beginner
Beginner
 
Posts: 20
Joined: Wed Nov 25, 2009 1:38 am

Re: Local incremental snap shots with rsync

Postby iugrifma » Sat Jan 09, 2010 3:27 pm

Dear tiritas,
many thanks for your hard work and insight into implementing 'rsync'. You've saved me quite some time, cheers! :wink:
One small Synology specific point I would point out.

Your rsync options exclude '@eaDir', which in general I'd agree with you is worthless when backing up most of your system.
However, your example includes the /volume1/photo shared folder. This is the EXCEPTION to the rule when ignoring '@eaDir'
because photostation relies on these dir's to create the thumbnails for it's web output. If you delete or loose these directories
and their contents you just get the "creating thumbnail" icon when looking in photostation [you cant find anything anymore].
Plus it's a royal pain the butt to recreate them as they are indexed in the photostation SQL database.

So when backing up /volume1/photo remember to include '@eaDir' :)

Griffo.
* Model: DS-209+ * Firmware: 2.3-1139
* Modification(s) - Upgraded RAM 1024 MB.(+Optware +OpenVPN 2.1)
* Volume(s) = 1x[RAID 1]-(2x Seagate ST31500341AS).
* Network: 1000xBASE T, Full duplex.
* Services enabled: Most of them! * Hibernation Time: 10 minutes
* External Devices: via eSATA-(1x Seagate ST31500341AS).
User avatar
iugrifma
Sharp
Sharp
 
Posts: 198
Joined: Thu Apr 02, 2009 12:25 pm
Location: Hachau, Austria

Re: Local incremental snap shots with rsync

Postby iugrifma » Fri Jan 29, 2010 9:01 am

Hi!
I've been using your script to implement backups on my NAS and I've spotted something:
You need to set the mail log file with a unique identifier if you plan to (or acidentally) run multiple or over lapping backups.
Otherwise one session can fail with file not found (email log file) because another session has deleted it.

I overcame the issue by adding a new variable called
Code: Select all
# Email Logfile unique identifier.
LOGTIME=`date +\%N`


It attaches the nanosecond value of the time the script started, which is pretty unique.

And amended the assigning of the email log file to:
Code: Select all
EMAIL_LOG_FILE=/tmp/rsync_$LOGTIME.log


Apart from that, running like a dream, cheers. :wink:
* Model: DS-209+ * Firmware: 2.3-1139
* Modification(s) - Upgraded RAM 1024 MB.(+Optware +OpenVPN 2.1)
* Volume(s) = 1x[RAID 1]-(2x Seagate ST31500341AS).
* Network: 1000xBASE T, Full duplex.
* Services enabled: Most of them! * Hibernation Time: 10 minutes
* External Devices: via eSATA-(1x Seagate ST31500341AS).
User avatar
iugrifma
Sharp
Sharp
 
Posts: 198
Joined: Thu Apr 02, 2009 12:25 pm
Location: Hachau, Austria

Re: Local incremental snap shots with rsync

Postby jahlives » Fri Jan 29, 2010 9:47 am

Nice work :)
But what is the difference to a program like rsnapshot which already can perform shnapshots via rsync? Furthermore rsnapshot allows you to execute user scripts which output can be backuped too. This is extremly helpful for backups of databases like postgres or mysql
DS107+ | 2.2-0942 | 2x1TB HD103UJ (intern,eSata) | Icecast | dnsmasq | svn via ssh | squid&squidGuard | Roundcube
DS408 | 2.2-0942| 4x1TB WD10EACS as RAID 5 | Postfix-Dovecot with SA, TLS and SMTP Auth @ Dovecot | Dovecot-Sieve
DS107+ (R2) | 2.2-0942 | 160 GB WD | 2x160GB WD (Raid1, usb)
Mediaclients | Popcorn Hour A-110
My Pages | http://tobisworld.ch | http://traveling-thailand.info | http://911-research.info | http://syno.tobisworld.homeip.net
User avatar
jahlives
Sharp
Sharp
 
Posts: 168
Joined: Thu Nov 29, 2007 10:01 am
Location: Hooker in Kernel


Return to Backup/Restore (Archived)

Who is online

Users browsing this forum: No registered users and 1 guest