Github Continuous Deployment to EC2

After searching far and wide for EASY solutions for continuous deployment to EC2 from our Github repository I decided to write my own, and detail it here so that other people could find it easily on google.

Here are the tools I use for this:

  • EC2 running Linux (Ubuntu)
  • Ruby and sinatra (but you can use something else if it floats your boat)
  • Nginx frontend
  • runit for keeping services running, and logging

I’m not going to go into details on how to setup EC2 – just get a Linux host of some sort (we’re using Ubuntu) up and running there, and assign an easy to remember CNAME to point to it (below I call this “yournewhostname.yourdomain.com”).

Firstly create a user account for your deployment on your EC2 instance, and create ssh keys for this user – we’ll call this user “deployment”. Then create a github account for that user, and add your ssh public key into that new account’s github configuration. [This is necessary only for private repositories – if you’re using a public repo then it’s not required]. You then need to make a “Team” for this user, so that account can get “pull” request rights on your project. We call our team “EC2 Deployment”. Set the team up so it gets pull rights only. Add this new user to that team.

Then in your Github repository, go into Admin, Teams, and add the “EC2 Deployment” team to your project. Now while you’re in Admin, go into Service Hooks, and add a Post-Receive URL hook pointing to http://yournewhostname.yourdomain.com/.

Now configure nginx to forward requests to yournewhostname.yourdomain.com to a server on localhost on a unique port. I use 10001, but pick whatever works.

Now you need a Ruby sinatra app running on 10001 which receives the POST data, performs a pull request, and restarts the service. This has to run as root (to restart the service) so make sure you audit this code!

Finally, here’s the sinatra code that processes the request:

post '/' do
 if (!params[:payload])
   return "Stop sending me rubbish"
 push = JSON.parse(params[:payload]);

 if (push['ref'] != 'refs/heads/master')
   return "Not a push on master - ignorning"

 repo = push['repository']['name'];
 Dir.chdir("/var/deployments/" + repo)
 if (system("/bin/su", "deployment", "-c", "/usr/bin/git pull"))
   puts "git pull of #{repo} successful"
   if (system("/usr/bin/sv", "kill", "."))
    return "Restarted!"
 return "Thanks";

I removed a lot of the logging here, but you get the idea. It’s pretty simple, and doesn’t do any testing or anything like that, but I’ve provided this here for you as an example, not as the final solution.

PS: We’re hiring. Come work for us!


2 thoughts on “Github Continuous Deployment to EC2

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s