Rails & Docker
Improve your Ruby On Rails workflow with Docker
Being a developer often means working as a team 🤹🤹🤹. 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
- Docker and Docker Compose
- 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:
FROM ruby:2.5.5-alpineARG PRECOMPILEASSETS
ARG RAILS_ENVENV SECRET_KEY_BASE fooENV RAILS_ENV ${RAILS_ENV}
ENV RAILS_SERVE_STATIC_FILES trueRUN apk add --update --no-cache \
build-base \
git \
postgresql-dev \
postgresql-client \
imagemagick \
nodejs-current \
yarn \
python2 \
tzdata \
fileRUN gem install bundler
# Install gems
RUN mkdir /gems
WORKDIR /gems
COPY Gemfile .
COPY Gemfile.lock .
RUN bundle install -j4 --retry 3 \
# Remove unneeded files (cached *.gem, *.o, *.c)
&& rm -rf /usr/local/bundle/cache/*.gem \
&& find /usr/local/bundle/gems/ -name "*.c" -delete \
&& find /usr/local/bundle/gems/ -name "*.o" -deleteARG INSTALL_PATH=/railsondocker
ENV INSTALL_PATH $INSTALL_PATHWORKDIR $INSTALL_PATH
COPY . .# Precompile assets (or not)
RUN docker/potential_asset_precompile.sh $PRECOMPILEASSETSCMD ["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:
version: '3.0'
services:
db:
image: postgres:11-alpine
ports:
- 5433:5432
environment:
POSTGRES_PASSWORD: postgres webpacker:
image: railsondocker_development
command: ["./docker/start_webpack_dev.sh"]
environment:
- NODE_ENV=development
- RAILS_ENV=development
- WEBPACKER_DEV_SERVER_HOST=0.0.0.0
volumes:
- .:/railsondocker:cached
ports:
- 3035:3035
app:
image: railsondocker_development
build:
context: .
args:
- PRECOMPILEASSETS=NO
environment:
- RAILS_ENV=development
links:
- db
- webpacker
ports:
- 3000:3000
volumes:
- .:/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
, forhost
andusername
/password
config/webpacker.yml
, for Webpacker serverhost
config/environments/*.rb
, to report the variables defined in thedocker-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, ... 🙂
This article was originally published (in french & english) on my blog.