Skip to content

Instantly share code, notes, and snippets.

@jsgarvin
Last active August 29, 2015 14:08
Show Gist options
  • Save jsgarvin/888bcba00ce199445b66 to your computer and use it in GitHub Desktop.
Save jsgarvin/888bcba00ce199445b66 to your computer and use it in GitHub Desktop.
Automatic rotating snapshots of AWS EC2 Linux instances
#!/usr/bin/ruby
#
# Take automatic rotating snapshots of your AWS EC2 linux instance(s).
#
# Slap this file into /etc/cron.hourly/ on your EC2 instance,
# edit the constants at the top of the class to your liking,
# and give it the execute bit.
#
# Known to work on Ubuntu 14.04 64bit. Presumed to work on most other flavors.
#
# Requirements:
# - ruby
# - gem 'aws-sdk', '~> 2.0' #known to work with 2.0.6.pre
require 'net/http'
require 'aws-sdk'
class Snapshot
ACCESS_KEY_ID = someaccesskeyid
SECRET_ACCESS_KEY = somesecretaccesskey
AWS_REGION = 'us-west-2' #or whatever you prefer
ITERATION_MAP = { # Keys must be in order of less frequent to more frequent.
:monthly => { timestamp_format: '%m/%y', :keep => 2 },
:weekly => { timestamp_format: '%m/%y-%U', :keep => 4 },
:daily => { timestamp_format: '%m/%d/%y', :keep => 5 },
:hourly => { timestamp_format: '%m/%d/%y-%H', :keep => 8 }
}
def self.create
new.save
end
def save
instance.volumes.each do |volume|
if iteration_for(volume)
volume.create_snapshot(description: iteration_description_for(volume))
end
purge_old_snapshots_for(volume)
end
end
private
def instance
@instance ||= Aws::EC2::Instance.new(instance_id, :client => client)
end
def instance_id
@instance_id ||= Net::HTTP.get_response(
URI.parse('http://169.254.169.254/latest/meta-data/instance-id')
).body
end
def client
@client ||= Aws::EC2::Client.new(
:region => AWS_REGION,
:access_key_id => ACCESS_KEY_ID,
:secret_access_key => SECRET_ACCESS_KEY)
end
def iteration_description_for(volume)
description_for(iteration_for(volume))
end
def iteration_for(volume)
@volume_iterations ||= Hash.new do |h, key|
h[key] = detect_iteration_for(key)
end
@volume_iterations[volume]
end
def detect_iteration_for(volume)
ITERATION_MAP.keys.detect do |iteration|
volume.snapshots.none? {|s| s.description == description_for(iteration) }
end
end
def description_for(iteration)
"Autosnap #{iteration.capitalize} [#{timestamp_for(iteration)}]"
end
def timestamp_for(iteration)
Time.now.strftime(ITERATION_MAP[iteration][:timestamp_format])
end
def purge_old_snapshots_for(volume)
old_snapshots_for(volume).each do |old_snapshot|
old_snapshot.delete
end
end
def old_snapshots_for(volume)
Array.new.tap do |old_snapshots|
ITERATION_MAP.keys.each do |key|
old_snapshots << volume.snapshots.
select {|s| s.description.match(/Autosnap #{key.capitalize}/) }.
sort {|a,b| b.start_time <=> a.start_time }[ITERATION_MAP[key][:keep],99].
to_a
end
end.flatten
end
end
Snapshot.create
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment