Automating ec2 deployments with Ruby

Recently I've had a couple of clients choose Amazon EC2 for their deployment environments, so I've been spending more time playing with EC2 lately than I ever had before. I set out to create a repeatable and time-efficient deployment process, and the result of my work is an easy to use ruby script, detailed below.

First, I have to tip my hat to the creators of chef -- with chef it was insanely easy to get an instance configured just the way I like it, without any intervention. Being able to quickly spin up new instances EC2 made it easy to test my chef scripts, and before long I had an image I was ready to bundle.

Now some people don't like having custom images. They'd rather use chef (or something similar) to configure a bare-bones image every time. I have nothing against that approach, I just happen to prefer having my own image that has all the packages, etc. that I want ready to go.

Once I had my custom image ready, I wanted to have a way to quickly deploy it. This included a few moving parts: the ID of the AMI, the ID of the EBS volume that should be attached (for persistent storage of the database files), and the public IP that should be associated with the running instance. Combining these three elements for a production-ready, single-instance deployment. And here is a ruby script that makes that deployment a one-command affair:

Using the excellent RightAws gem, I first grab the id of the custom AMI I want to use, then I launch a new instance based on that AMI, making sure the instance is launched in the same availability zone where the EBS volume is located. The script then does a simple sleep loop while waiting for the instance to show up as "running". After the EBS volume is attached, then we wait a bit before connecting via SSH to mount the EBS volume (it is setup in /etc/fstab in my custom image) and run any setup commands, which, in this case, is starting postgres (since it can't be started before the volume with the database files is mounted). After that's all done, the elastic IP is associated with the instance, and we're ready to roll.

The commented-out lines assume you want maximum security for your instance and have global SSH access turned off in your security group. In that case, to be able to connect to SSH we'd need to fetch the public IP of the machine running this script, change the security group to allow SSH access from that IP, then undo that change after we make the SSH connection to close the firewall again.

I've gotten a lot of mileage out of this script so far. :) One day I'll get around to smartly handling SSH errors (so it can attempt connecting right away, and retry a few times in case the instance isn't ready yet), and to adding some configurability for the AMI ID, EBS ID, and EIP. Until then, this works well for someone just getting started with EC2.

Comments