A Docker Compose Rails Example

By now, we’ve known how to build an image through Dockerfile. Actually, Dockerfile can help us build images of any projects. Today, I’m going to analyze an example on setting up Rails with Docker. And this is also from Docker’s official website.

Define a rails container

As you know, Docker will run projects in containers. The very first step of setting up a new rails project need to be to define the container’s environment. What should we include in a container that rails can run?

Ruby, of course. And other kind of packages.

Therefore, the first line of Dockerfile can be

FROM ruby:2.3.3
RUN apt-get update -qq && apt install -y build-essential libpq-dev nodejs

All the packages appear in second line is to install ubuntu tools. I’ll skip the introductions of them, and actually, I can’t tell it clearly at this time. But one thing I can tell is these packages seems to be required in each rails container. If I can’t tell you guys why these stuff are required, I, at least, will summery which packages are needed.

How will you build you rails

The Dockerfile above is not enough. You could imagine it can help you install gems or something like bundle install. We’ll define this step in Dockerfile, so add the following lines:

RUN mkdir /myapp
WORKDIR /myapp
ADD Gemfile /myapp/Gemfile
ADD Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
ADD . /myapp

In my point of view, what these lines do respectively are:

  1. Create a directory in the container called myapp
  2. Copy Gemfile from local directory to the container, under myapp
  3. Copy Gemfile.lock as well
  4. Bundle install in local directory
  5. Copy all stuff to the container

But what are the stuff built from bundle install. Let’s go ahead to see what is needed in Gemfile.

Install rails

Although we have’t actually set up docker, you can know the container will have ruby environment, since it’s built based on that. And it seems like that ruby image include bundle tools. So, at this moment, one thing last to start rails is rails itself. Of course, you can install rails from gem install rails, besides, adding gem 'rails' to Gemfile also works. Both methods enable you to use rails new.

We now choose the latter one. Therefore, Gemfile will looks like:

source 'https://rubygems.org'
gem 'rails', '5.0.0.1'

And touch Gemfile.lock is required, though, for unknown reasons.

Set up and link your services with docker-compose

Think about our project. Is there anything else not satisfied? Where is the database?

In rails I used without docker, db is created when we use rails db:create. Rails have a convention on creating db, and database.yml follow that convention. Since in docker we run database in a different container, we’d better rewrite database.yml to link it to the right container. But before that, I need to set up a db. So write the following codes in docker-compose.yml:

version: "2"
services:
db:
image: postgres

You can see, we use an image named postgres. It will run as the db container. And why version got 2 is not clear.

Another thing we will do in docker-compose is set up rails. In the former step, we only installed rails, instead of making it start.

If we call the rails service web, your docker-compose will have these codes:

version: "2"
services:
db:
image: postgres
web:
build: .
command: bundle exec rails s -p 3000 -b '0.0.0.0'
volumes:
- .:/myapp
ports:
- "3000:3000"
depends_on:
- db

What’s the meaning of them?

First, build: . is just like docker build, which I introduced in a previous article. It tell docker that Dockerfile is in the current directory and build it.

Second, command: bundle exec rails s -p 3000 -b '0.0.0.0' is very readable for a rails developer, right? (Btw, I get an idea from this sentence that the degree of readability varies among people, instead of a intrinsic characteristic of things, and you can guess what determines your readability at this time and how will it change in the future).

What I can’t explain are volumes, ports, depends_on. I guess volumes represent your working directory, ports tell your ports accept traffic, and depends_on link rails and db.

Create a rails project

Would you run rails new to create a project, and how will docker create a project inside the container?

There is a trick. The first time you want to generate a project, you need to do docker-compose run web rails new . --force --database-postgresql.

I loss the information docker returned to me, but I guess docker run Dockerfile at first to build ruby env and gem install rails. and then rails new . And this is proved by docker official guide.

Next time, when you edit Gemfile and what to build the project again, or what compose it on your server, just run docker-compose build, since you’ve generated the skeleton(many rails files) in the container, just build it again, instead of generating it again.

Connect to database

As we’ve said before, database connection should be written down at database.yml. There should be nothing mysterious. Look at the codes:

development: &default
adapter: postgresql
encoding: unicode
database: myapp_development
pool: 5
username: postgres
password:
host: db
test:
<<: *default
database: myapp_test

Remember these information are only used to connect your db, instead of creating db. If you use the convention of rails to set up db, there is no need to change the yml file. However, we run postgres in a container like real db environment on server, so we need to add username and password to the yml file. At this time, we only use default configs.

Run your rails

After this, we can run rails with docker-compose up, and create db with docker-compose run web rake db:create. This order is suggested by the tutorial. But I guess it will be acceptable or even better with the reverse order.

In addition, one notice the tutorial saying is if you got A server is already runing, delete /tmp/pids/server.pid, as I’ve encountered before.

Recall

  1. Write the container’s base with FROM ruby:2.3.3 and RUN apt-get
  2. Install rails through Gemfile (and Gemfile.lock) or gem install rails(not included).
  3. Copy rails code to container with WORKDIR ADD RUN bundle install in Dockerfile
  4. Define services, namely, db and web(rails), in docker-compose.yml. Specifically, write down build, setting up command, volumes, ports, and db.
  5. Connect rails to db. Add username and password to configs.
  6. Generate a project with docker-compose run web rails new . --force --database=postgres --skip-bundle. Or if you already have a project, docker-compose build
  7. Run your project. docker-compose up, and create db, docker-compose run web rake db:create
Like what you read? Give Sining Liu a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.