production grade ruby application deployment process

Given that you can now deploy a ruby interpreter that won’t make experienced system administrators cry the next logical step is to do the same for your ruby applications. Fortunately bundler takes us almost all the way there with its ability to vendor dependencies. All you need to do is automate the process with a few rake tasks and you are pretty much good to go.

First a bit of a rant on why you should do this. Binaries and other build/development artifacts do not belong in git or any other kind of source control system. The only thing that should be in your source control system is the bare minmum metadata to get a development environment up and running and a few extra bits to cleanly package the application for deployment.

For ruby applications this means at a bare minimum you need Gemfile, Gemfile.lock, Rakefile and maybe Vagrantfile. This is very important. You are never going to have a repeatable build/deploymet process if you check-in artifacts that are a byproduct of the development environment. The best you can do is have some kind of generative process where people can quickly get up and running in their environment of choice by making sure they have all the dependencies that your application requires.

That’s really the best you can do because no two development environments are ever the same and the more byproducts and assumptions that you inadvertently add to the source code repository the worse the experience is going to be for every other developer because they are going to have to replicate minor and irrelevant details from your environment. You can side-step a whole set of headaches by following best practices for spelling out your application dependencies with a Gemfile and codifying common workflows with a Rakefile and if you are feeling generous you can also provide a Vagrantfile that codifies the process you follow to get a development environment up and running. Alright, lets see how this plays out with a toy sinatra application.

Below is pretty much all the metadata you need for almost all ruby applications. You are welcome to augment it with extra directories like lib/, doc/, test/, etc. and in non-toy applications you actually should have those directories but you should try your hardest to avoid bin/. If you do need bin/ then package it separately and make it another dependency for your application. The one exception is script type binaries like shell scripts and ruby scripts with a shebang line.

Ok, so after hacking a little bit on your application you are ready to deploy it. The goal here is to discretize the build process as much as possible starting from the bare minimum. Git clone is too coarse and does too much. We don’t need the entire history of the repository to deploy code. By generating discrete and versioned artifacts we do the bare minimum necessary to get to the next stage of the process which can be any number of things from testing to setting up a staging/integration environment.

Most well-desgined ruby applications rely on in-house and 3rd party gems. That’s why we have the Gemfile to begin with, to spell out our dependencies. Doing bundle install on production boxes is no good because if you have 100 servers each of which is going to run the application then that is a lot of duplication and an inconsiderate use of network resources. Bundler helps us out here by letting us vendor our dependencies and so that is what we are going to do. In our Rakefile we are going to spell out how to vendor the gems and package the whole thing as a tar file. I’m using tar for convenience and you could just as easily package it as a Debian package with fpm.

So to generate the application package you just run

Now that you have a self-contained application package you can do the next logical thing. Move it to a testing/staging cluster and run some integration tests or put it somewhere where your deployment pipeline can pick it up and and do a rolling deployment or any number of other clever things. The skeleton code can be found at davidk01/bare-minimum-ruby-app. You are encouraged to fork and play around with it.