Automated ZFS Snapshots
March 30, 2010
I needed something like zfs-auto-snapshot written by Tim Foster but portable so it works on all systems that support ZFS. I reviewed a few scripts on github and was unhappy with what was out there so I decided to write my own.
With zbackup.rb you can define what to snapshot and how many rotation days you want to go back.
So say you want a month of snapshots:
/usr/bin/zbackup.rb iraidz/zWork 30
Simple, no? 😉
#!/usr/bin/env ruby # Create snapshots for a 7 day rotation. # ./zbackup.rb iraidz/zWork 7 # # Add to crontab # crontab -e # 0 2 * * * /usr/bin/zbackup.rb iraidz/zWork 7 pool = ARGV[0] days_back = ARGV[1].to_i if pool.nil? or pool.empty? puts "\nDefine the pool you want to snapshot:" puts "\tex: zbackup.rb iraidz/zWork 7\n\n" exit 0 end if days_back.nil? or days_back < 1 puts "\nDefine how many days for your rotation:" puts "\tex: zbackup.rb iraidz/zWork 7\n\n" exit 0 end # response from zfs list curr_snaps = `zfs list -t snapshot -o name` # days back limit variable date_back = Time.now - (86400*days_back) curr_snaps.split(/\n/).each do |pline| if m = pline.match(/#{pool}\@([0-9]+)\-([0-9]+)\-([0-9]+)/) if date_back >= Time.local(m[1],m[2],m[3]) `zfs destroy #{pline}` end end end # take snapshot for this run if needed. month = Time.now.month day = Time.now.day year = Time.now.year if curr_snaps !~ /#{pool}\@#{year}\-#{month}\-#{day}/ `zfs snapshot -r #{pool}@#{year}-#{month}-#{day}` end
Alternate approach with no ruby dependency, similar to zfs-autosnap service, allows multiple tiers of snapshots.
# “frequent” snapshots, one every 15 minutes, keep 4
0,15,30,45 * * * * /root/bin/snap.sh pool/home frequently 4
# hourly snapshots, once an hour, keep 24 hours
0 * * * * /root/bin/snap.sh pool/home hourly 24
# daily snapshots, keep 7 days
0 0 * * * /root/bin/snap.sh pool/home daily 7
# weekly snapshots, keep 4 weeks
0 0 * * 0 /root/bin/snap.sh pool/home weekly 4
# monthly snapshots, keep 48 months.
0 0 1 * * /root/bin/snap.sh pool/home monthly 48
#!/bin/bash
# snap.sh
function usage {
cat <<EOF
Usage: $0
EOF
exit 1
}
function tailv {
local lines=${1:-10}
awk ‘BEGIN { i = 0; j = 0; }
{
out[i] = $0;
if (i >= int(LIMIT)) {
print out[j];
j = j + 1;
}
i = i + 1;
}’ LIMIT=$lines
}
[ -z “$1” ] && usage
[ -z “$2” ] && usage
[ -z “$3″ ] && usage
NAME=”$1″
TAG=”$2″
LIMIT=”$3”
zfs snapshot “${NAME}@snap:${TAG}-$(date +%Y-%m-%d-%H:%M)”
zfs list -t snapshot -o name |\
grep “^${NAME}@snap:${TAG}-” |\
sort |\
tailv $LIMIT |\
while read snapshot; do
zfs destroy $snapshot
done