Dockerize a Rails App

Heard lots about Docker and you want to try it out? This tutorial will demonstrate one way to get a simple Rails app running in Docker.

You're going to need a linux environment to follow along. If you're using a Mac I recommend you just follow the directions I wrote about getting set up with Vagrant. That will cover installing Docker and Docker Compose. If you already have a linux environment, you can just install them as follows.

Installing Docker and Docker Compose

$ sudo apt-get update && apt-get install wget
$ wget -qO- https://get.docker.com/ | sh
$ sudo usermod -aG docker vagrant
$ sudo docker run hello-world

The last command should show you a bunch of things including: Hello from Docker.

  • Docker Compose - This will help us manage multi-container apps. Installation docs are here or just copy and paste:
$ sudo -i
$ curl -L https://github.com/docker/compose/releases/download/1.3.3/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
$ chmod +x /usr/local/bin/docker-compose
$ exit

Dockerizing your app

Clone your app down first. I'm using a demo rails app called storedom from the good people at Turing.

$ git clone git@github.com:turingschool-examples/storedom.git ~/src/storedom

The problem with that app is that it is using SQLite. We're going to use PostgreSQL so we can have two Docker containers running and talking to each other. To do that, we'll need to change a few files: database.yml, and the Gemfile

#config/database.yml
development: &default  
  adapter: postgresql
  encoding: unicode
  database: storedom_dev
  pool: 5
  username: postgres
  password:
  host: postgres

test:  
  <<: *default
  database: storedom_test

production:  
  <<: *default
  database: storedom
#Gemfile
source 'https://rubygems.org'

gem 'rails', '4.1.4'  
gem 'pg'  
gem 'sass-rails', '~> 4.0.3'  
gem 'uglifier', '>= 1.3.0'  
gem 'coffee-rails', '~> 4.0.0'  
gem 'jquery-rails'  
gem 'turbolinks'  
gem 'jbuilder', '~> 2.0'  
gem 'faker'  
gem 'haml-rails'  
gem 'therubyracer'  
gem 'less-rails-bootstrap'

group :development do  
  gem 'spring'
end

group :development, :test do  
  gem 'capybara'
  gem 'pry', :require => 'pry'
end  
Dockerfile

The Dockerfile is a set of instructions Docker uses to build your container. You start with a base image and then layer on various commands that will install software, organize directories, etc. The commands are carried out in sequence.

Here is an example that will work for storedom:

#Dockerfile
FROM ruby:2.2.0

WORKDIR /usr/src/app/

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev

RUN curl -o /usr/local/bin/gosu -SL "https://github.com/tianon/gosu/releases/download/1.2/gosu-$(dpkg --print-architecture)" && chmod +x /usr/local/bin/gosu

COPY Gemfile /usr/src/app/Gemfile  
COPY Gemfile.lock /usr/src/app/Gemfile.lock

RUN bundle install

COPY . /usr/src/app  
# copy our project into the WORKDIR

CMD ["rails", "s"]  
# this is the default command we will run when starting the container*

Docker has good documentation on building a Dockerfile, but you can see a quick explanation of the commands we are using below:

FROM

Our base image is ruby 2.2.0. This is a basic Docker image with Ruby 2.2.0 installed on top of Ubuntu. It is maintained by Docker. The rest of our commands will build off this starting point.

WORKDIR

The WORKDIR is the working directory for all the other commands.

RUN

The first RUN installs two libraries that we will need. The second installs gosu, a tool we use to avoid permissions hell.

COPY

COPY copies files into the container. We are copying in the Gemfile and Gemfile.lock so that we can install our gems in the container. After they are copied into the container, we RUN bundle install to install all our gems. Then we copy our project into the container.

CMD

The default command for when the container is started. There are a few acceptable form for CMD. This is the preferred form.

Docker Compose

Docker Compose, formerly called fig, is a tool that helps manage building, running, and linking Docker containers. It consists of a YAML file that sits in your project directory, called docker-compose.yml, and a CLI.

Below is a docker-compose.yml that we can use to get for this project.

#docker-compose.yml
rails:  
  build: .
  command: bundle exec rails s -p 3000 -b '0.0.0.0'
  volumes:
    - .:/storedom
  ports:
    - "3000:3000"
  links:
    - postgres:postgres

postgres:  
  image: postgres
  ports:
    - "5432"

So what do those things mean? Luckily, the file is reasonably self-explanitory and well documented, but we can go into a little detail about what each key does.

Container Name

This sample app needs two containers running: rails and postgres. The first will run the rails app and the second will run the postgres database. They are defined at the top level of docker-compose.yml, and we can use these friendly names to refer to the containers in the Docker Compose CLI.

build

build maps to a path to a directory that contains a Dockerfile. We have a Dockerfile in the same directory as the docker-compose.yml, and therefor just use..

command

command overrides the default command as defined in the Dockerfile [ ] check the actual command used in the Dockerfile. Do we even need to use command?

volumes

volumes mounts a path on the host to a path in the new container. Here, we are mapping the current directory to the /storedom directory in the container.

ports

Similar to our Vagrant setup, ports maps ports on the host to ports in the container. We're keeping everything on 3000 just to make it all easy. Using a string here isn't mandatory, but is idiomatic.

This is one of the really cool parts of Docker Compose. links links containers together; in this case, we are giving our app container, rails, access to our database container postgres.

image

Every container in Docker Compose needs either build or image. image will pull down the mapped image.

Tying it all together

Now, you should have everything you need to run your app in Docker containers. Run $ docker-compose up and Docker Compose will build your containers and run them. Learn more about the Docker Compose CLI in the docs.

Go out and cargo-cult to your heart's content. Get it? Because Docker? And cargo!