How I build a Docker image for my Rails app

Jeroen van Baarsen
on
September 14, 2014

You have probably heard of the new cool kid in town, Docker. The problem with the fact that it is a new kid is that the documentation about certain tasks is pretty limited. This includes the ways you can deploy your rails app using Docker. In this blog post I’ll give you some pointers that can help you in your further quest of deploying your rails apps with docker.

There are a few things that I won’t cover, simply because I’m not sure how they should be done yet. Those things are:

  • Databases
  • Migrations
  • Assets

Since that list contains a few super important items for rails apps, you will
wonder what I will cover, well I will help you with creating your own Dockerfile
that you can use to run your app. This Dockerfile is based on the work of the
Passenger Phusion guys.

I assume you have a working Docker environment, I’ll be using
Boot2docker in this post, since I’m working on osx, and there is no native docker support yet.

When I started looking at Docker, my first reaction was, wow! that’s easy, lets
do this! But when I started working on getting my rails app into the Docker
containers, I hit a wall. When going over the internet most people were using
just rails s in their containers, and that was just not going to work for me. I had a few demands:

  • It should use Passenger
  • It should use Nginx
  • It should be fast

So with those things in mind I started looking at some good base images, since
building them from the ground would not be an option for me. So I came across
the base images provided by the Phusion team. They had fixed a few basic things,
like SSH, a correct init proces, Cron daemon, Runit for service supervision and
much more (learn more).

The files

First thing I would need was a Dockerfile, this is the one I’ve come up with, in
the code you find comments in format === # ===, they map to the text below the code:

Dockerfile

CODE: https://gist.github.com/jansn/6053b769f3dc9080e507f138628fe8b6.js

And we needed the file to configure nginx

nginx.conf

CODE: https://gist.github.com/jansn/5615589ddc98dcdbdec5fe91fb72c921.js

Lets take a closer look at all the single components:

1 The header

This is the header, every Dockerfile has one, the FROM is telling what base
image Docker should use, the MAINTAINER is for telling who created this particular Dockerfile

2 Start nginx

By default nginx is not running, you have to tell Docker to enable it. You do this via this command:

CODE: https://gist.github.com/jansn/f6389157a00bd0cbb2f05fb0d0c62fdc.js

3 Configure nginx

Since we want to be able to get to this app via every domain thats pointing to the server, we remove the default nginx config, we also copy our nginx config file to the sites-enabled folder.

4 Making sure all the folders are created

We want to be sure that the correct folders exists. Our app only lives in
/home/app/webapp so for now thats the only folder we have to create.

5 Install gems in an efficient way

One of my goals was to make things fast, the way we can achieve this, is by making sure we can cache Docker layers. Of of the most time consuming things is bundle install, so we want to be sure to cache those layers. Thats why we add the
Gemfile and Gemfile.lock to the /tmp folder, and run bundle install from
there. So when the Gemfile and Gemfile.lock did not change, the bundle install command will be skipped as well.

6 Adding the source code

Now we have all the part in place, we can add the source code of our app. This
is done by adding the complete folder to the docker image, and place it in
/home/app/webapp/

Building the image

Now we have all the code that is needed to build the actual image. To do this
run the command

CODE: https://gist.github.com/jansn/2787aa0147501b9d3b652cf1a60675e6.js

You have to replace the intercity/base part with the name you want to give the image.

This will trigger the image build process, that looks like this:

CODE: https://gist.github.com/jansn/e6be5ecaa92c8618c3a06f005884cfb6.js

After the command is done, you can check if the image is present by running.

CODE: https://gist.github.com/jansn/fe99073f365f491e3d8b720139710481.js