Rails & Docker

Publié il y a presque 3 ans~3 min

Being a developer often means working as a team :juggling::juggling::juggling:. But who has never known THE famous installation that takes a day (or more...) because of environmental concerns or dependencies? Fortunately, Docker is here! Thanks to its containers logic, it allows you to encapsulate applications with their dependencies, thus freeing us from project portability concerns.

Based on a standard Ruby On Rails project, we are going to set up the various elements necessary for its dockerization.

Prerequisites

  1. Docker and Docker Compose
  2. A Ruby On Rails project (≥ 5.1) using Webpacker and PostgreSQL (≥ 10)

A demonstration project has been created for this article, so do not hesitate to refer to it for more details (scripts, ...)!

Dockerfile

Any Docker project is defined by an original image, which will then be used as a basis for building the various containers (application, database, ...). To do this, all you have to do is create a file simply named Dockerfile at the root of our project:

1FROM ruby:2.5.5-alpine
2
3ARG PRECOMPILEASSETS
4ARG RAILS_ENV
5
6ENV SECRET_KEY_BASE foo
7ENV RAILS_ENV ${RAILS_ENV}
8ENV RAILS_SERVE_STATIC_FILES true
9
10RUN apk add --update --no-cache \
11 build-base \
12 git \
13 postgresql-dev \
14 postgresql-client \
15 imagemagick \
16 nodejs-current \
17 yarn \
18 python2 \
19 tzdata \
20 file
21
22RUN gem install bundler
23# Install gems
24RUN mkdir /gems
25WORKDIR /gems
26COPY Gemfile .
27COPY Gemfile.lock .
28RUN bundle install -j4 --retry 3 \
29 # Remove unneeded files (cached *.gem, *.o, *.c)
30 && rm -rf /usr/local/bundle/cache/*.gem \
31 && find /usr/local/bundle/gems/ -name "*.c" -delete \
32 && find /usr/local/bundle/gems/ -name "*.o" -delete
33
34ARG INSTALL_PATH=/railsondocker
35ENV INSTALL_PATH $INSTALL_PATH
36
37WORKDIR $INSTALL_PATH
38COPY . .
39
40# Precompile assets (or not)
41RUN docker/potential_asset_precompile.sh $PRECOMPILEASSETS
42
43CMD ["docker/startup.sh"]

The starting point for our file is a Docker image for Ruby. A so-called "alpine" version is used here, that's to say embedding the minimum required to optimize the weight of the final generated image.

We then install the various necessary libraries (postgresql, nodejs, ...) and the gems declared in the Gemfile (with a little cleaning at the end of the task, again to optimize the final weight).

Finally, we precompile the assets (if necessary) before launching the global command declared via the CMD instruction, which will take care of launching the server (Puma) after having prepared the database (cf. startup.sh script).

Note the use of variables that will allow us to build a specific image depending on the environment (development, production, ...). These variables will be defined in each docker-compose.yml file.

Docker Compose

Our general constructor being now ready, all that remains is to define the different containers of our application, with any variations depending on the environment.

In development mode, Rails and Webpacker are separated, in order to improve the responsiveness of the whole. The docker-compose.yml file is quite simple to understand, its syntax remaining quite clear:

1version: '3.0'
2services:
3 db:
4 image: postgres:11-alpine
5 ports:
6 - 5433:5432
7 environment:
8 POSTGRES_PASSWORD: postgres
9
10 webpacker:
11 image: railsondocker_development
12 command: ["./docker/start_webpack_dev.sh"]
13 environment:
14 - NODE_ENV=development
15 - RAILS_ENV=development
16 - WEBPACKER_DEV_SERVER_HOST=0.0.0.0
17 volumes:
18 - .:/railsondocker:cached
19 ports:
20 - 3035:3035
21
22 app:
23 image: railsondocker_development
24 build:
25 context: .
26 args:
27 - PRECOMPILEASSETS=NO
28 environment:
29 - RAILS_ENV=development
30 links:
31 - db
32 - webpacker
33 ports:
34 - 3000:3000
35 volumes:
36 - .:/railsondocker:cached

Our three containers are therefore linked to images (from the Dockerfile, with the exception of PostgreSQL, for which we use an "alpine" image). They are assigned ports, and possibly commands to be executed at launch.

In some cases, we will define arguments or environment variables, useful for the Dockerfile.

Finally the links between services are declared in the app container, in the links part.

Rails configuration

Now that the Docker part is set up, we need to make some adjustments in the configuration of our Ruby On Rails project, in particular to take into account the variables defined in the Compose files:

  • config/database.yml, for host and username/password
  • config/webpacker.yml, for Webpacker server host
  • config/environments/*.rb, to report the variables defined in the docker-compose.yml (optional)

🎉

Et voilà ! Now all that remains is to run the docker-compose build command to build our Docker image, then the docker-compose up command to launch our project on localhost: 3000! Subsequently, we can add new containers (or services): mail server, ... 🙂