The Docker official images have created a canonical way to build Docker images for any web application. In another post, I wrote about the building tool that uses Heroku Buildpacks to create Docker containers. Though the building tool made sense before Docker added the ONBUILD command to Dockerfiles, using a more pure Docker system gives you a few benefits.

  1. You get more control of the images being built around your code because the operating systems are just Dockerfiles you can modify yourself if you don’t like them
  2. You get to pick versions easier because there are native Docker tags for different versions of languages and frameworks
  3. You get the benefit of community support as enhancements are contributed into the Docker community, you can take advantage of them easier if you are using Docker standards

So, How Do you Create a Rails App and Dockerize it? Easy.

Let’s create a new Rails app:

$ rails new foobar
$ cd foobar
$ echo 'FROM rails:onbuild' > Dockerfiles
$ docker build -t my-rails-app .
$ docker run -d -p 3000:3000 my-rails-app
$ curl
<!DOCTYPE html>
    <title>Ruby on Rails: Welcome aboard</title>

What does this do? To find out let’s examine the rails: onbuild Dockerfile:

FROM ruby:2.1.5
# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1

RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

ONBUILD COPY Gemfile /usr/src/app/
ONBUILD COPY Gemfile.lock /usr/src/app/
ONBUILD RUN bundle install

ONBUILD COPY . /usr/src/app

RUN apt-get update && \
apt-get install -y nodejs --no-install-recommends && \
rm -rf /var/lib/apt/lists/*
RUN apt-get update && \
apt-get install -y \
mysql-client postgresql-client \
sqlite3 --no-install-recommends && \
rm -rf /var/lib/apt/lists/*

CMD ["rails", "server"]

So, now it is clear which version of Ruby we are using, where the source code files are going, and how it runs our application.

What if you are on a Mac, but need the Gemfile.lock to be tied to the Linux versions of libraries? There is a clever command you can run to do this:

$ docker run --rm -v "$(pwd)":/usr/src/app -w /usr/src/app ruby:2.1.5 bundle install

This starts a temporary container with a filesystem tied to your local directory so that the Gemfile.lock file that is generated will be written to your local filesystem, not just to your container’s filesystem. Neat!

How is using ONBUILD better than using the building tool? For one, you can customize this a lot easier. For example, if you want to run your Rails app using the thin event based/non-blocking server instead of webrick, you can overwrite the CMD in your app’s Dockerfile after adding the thin gem to your Gemfile:

$ echo 'CMD ["thin", "start"]' >> Dockerfile

(Don't forget to re-run the command mentioned above to avoid Gemfile.lock errors when re-building the image.)

$ docker build -t my-rails-app .
$ docker run -d -p 3000:3000 my-rails-app

This kind of flexibility is what I love about using Docker.