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
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
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:
- Create a directory in the container called myapp
- Copy Gemfile from local directory to the container, under myapp
- Copy Gemfile.lock as well
- Bundle install in local directory
- 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.
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
We now choose the latter one. Therefore, Gemfile will looks like:
gem 'rails', '220.127.116.11'
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:
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:
command: bundle exec rails s -p 3000 -b '0.0.0.0'
What’s the meaning of them?
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.
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
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:
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.
- Write the container’s base with
- Install rails through Gemfile (and Gemfile.lock) or
gem install rails(not included).
- Copy rails code to container with
RUN bundle installin Dockerfile
- Define services, namely, db and web(rails), in docker-compose.yml. Specifically, write down build, setting up command, volumes, ports, and db.
- Connect rails to db. Add username and password to configs.
- Generate a project with
docker-compose run web rails new . --force --database=postgres --skip-bundle. Or if you already have a project,
- Run your project.
docker-compose up, and create db,
docker-compose run web rake db:create