Automated ZFS Snapshots
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? 😉
[code lang=”ruby”]
#!/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
[/code]
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