Like it say’s
Over the last 18 months, the one key thingI have learnt about amazon is don’t use EBS, in any way shape or form, in most cases it will be okay, but if you start relying on it it can ruin even the best architected service and reduce it to a rubble. So you can imagine how pleased I was to find I’d need to write something to make EBS snapshots.
For those of you that don’t know, Alfresco Enterprise has an amp to connect to S3 which is fantastic and makes use of a local cache while it’s waiting for the s3 buckets to actually write the data and if you’re hosting in Amazon this is the way to go. It means you can separate the application from the OS & data, which is important for the following reasons:
1, EBS volumes suck, so where possible don’t use them for storing data, or for your OS,
2, Having data else where means you can, with out prejudice delete nodes and your data is safe
3, It forces you to build an environment that can be rapidly re-built
So in short, data off of the server means you can scale up and down easily and you can rebuild easily, the key is always to keep the distinctively different areas separate and do not merge them together.
So facing this need to backup EBS volumes I’d thought I’d start with snapshots, I did a bit of googling and came across a few ebs snapshot programs that seem to do the job, but I wanted one in Ruby and I’ve used amazon’s SDK’s before so why not write my own.
The script
#!/usr/bin/ruby require 'rubygems' require 'aws-sdk' #Get options access_key_id=ARGV[0] secret_access_key=ARGV[1] if File.exist?("/usr/local/bin/backup_volumes.txt") puts "File found, loading content" ec2 = AWS::EC2.new(:access_key_id => access_key_id, :secret_access_key=> secret_access_key) File.open("/usr/local/bin/backup_volumes.txt", "r") do |fh| fh.each do |line| volume_id=line.split(',')[0].chomp volume_desc=line.split(',')[1].chomp puts "Volume ID = #{volume_id} Volume Description = #{volume_desc}}" v = ec2.volumes["#{volume_id}"] if v.exists? puts "creating snapshot" date = Time.now backup_string="Backup of #{volume_id} - #{date.day}-#{date.month}-#{date.year}" puts "#{backup_string}" snapshot = v.create_snapshot(backup_string) sleep 1 until [:completed, :error].include?(snapshot.status) snapshot.tag("Name", :value =>"#{volume_desc} #{volume_id}") else puts "Volume #{volume_id} no longer exists" end end end else puts "no file backup_volumes.txt" end
I started writing it with the idea of having it just backup all EBS volumes that ever existed, but I thought better of it. So I added a file “backup_volumes.txt” so instead it will lead this and look for a volume id and a name for it, i.e.
vol-1264asde,Data Volume
if you wanted to backup everything it wouldn’t take much to extend this, i.e. change the following:
v = ec2.volumes["#{volume_id}"]
To
ec2.volumes.each do |v|
or at least something like that…
Anyway, the file takes the keys via the cli as params to the script so it makes it quite easy to run the script on one server in several cron jobs with different keys if needed.
It’s worth mentioning at this point that within AWS you should be using IAM to restrict the EBS policy down to the bear minimum something like this is a good start:
{ "Statement": [ { "Sid": "Stmt1353967821525", "Action": [ "ec2:CreateSnapshot", "ec2:CreateTags", "ec2:DescribeSnapshots", "ec2:DescribeTags", "ec2:DescribeVolumeAttribute", "ec2:DescribeVolumeStatus", "ec2:DescribeVolumes" ], "Effect": "Allow", "Resource": "*" } ] }
For what it’s worth, you can spend a while reading loads of stuff to work out how to set up the policy or just use the policy generator
Right, slightly back on topic, It also tags the name of the volume for you because we all know the description field isn’t good enough.
Well that is it. Short and something, something… Now the disclaimer, I ran the script a handful of times and it seemed good, So please test it :)
[…] EBS volumes is very precious so obviously we want it backed up. A few weeks ago I started writing a backup script for EBS volumes, well the script wasn’t well tested it was quickly written but it worked. I decided that I […]