How to dockerize a Rails application
Microservices architecture is a particular way of designing software applications so that it can be deployed independently, test independently, and scaled independently. Read What are microservices? to get a more deep understanding of this particular architecture pattern.
One of the first few steps you as a developer or ops person can do to move towards microservices architecture is to start containerizing your applications.
If Rails
is part of your stack, this blog will explain to you how to dockerize your Rails application. This blog is not a docker
tutorial, yet you will be instructed with some best practices with docker. With the assumption that docker is installed on your system and you have a working rails application with the PostgreSQL database. If you haven't installed Docker already, download it from the official docker website.
Create Dockerfile
in the root folder of the project
Dockerfile
describes all the steps that are required to create an image and would usually be contained within the root directory of the source code repository for your application.
# rails-app/cd rails-app/touch Dockerfile
First step of writing any Dockerfile
is to choose the base image. We are going to use the official ruby docker image but a slim version.
FROM ruby:2.6.3-slim
Applying labels to images and containers allows you to add metadata via key/value pairs that can later be used to search for and identify Docker images and containers. You can see the labels applied to any image using the docker inspect command.
LABEL "maintainer"="viral@fakemail.com"LABEL "app_type"="backend"LABEL "service_type"="API"
Maintainer is a deprecated field in the Dockerfile specification.
Docker runs all processes within the container as root
but it is recommended to use USER
instruction to potentially avoid any security risks. Production containers should almost always be run under the context of a non-privileged user.
USER root
One of the most important instruction ENV
allows you to set shell environment variables that can be used by your running application for configuration and during the build process to simplify the Dockerfile.
ENV APP /usr/src/appENV RAIL_ENV development
Now, we are going to introduce RUN
instruction. We will be using these commands to create the file structure you need and install the required software dependencies. You will also be able to use shell environment variables defined in the previous step. Let's write a few RUN
instructions required for our rails application.
RUN mkdir -p $APPRUN apt-get -y update && apt-get install -y --no-install-recommends \libgmp-dev \gcc make git \postgresql-client \dnsutils \libpq-dev libglib2.0-dev build-essential patch zlib1g-dev liblzma-dev \&& rm -rf /var/lib/apt/lists/*
First instruction creates an app directory and second instruction runs few commands and installs require software, a Postgres client into a single step.
Remember that every instruction creates a new Docker image layer, so it often makes sense to combine a few logically grouped commands onto a single line. It is even possible to use the ADD instruction in combination with the RUN instruction to copy a complex script to your image and then execute that script with only two commands in the Dockerfile.
Next instruction, WORKDIR
, is used to change the working directory in the image for the remaining build instructions.
WORKDIR $APP
COPY
instruction copies file from the host machine to your container image.
COPY Gemfile $APP/GemfileCOPY Gemfile.lock $APP/Gemfile.lockRUN gem install bundler -v 1.17.3RUN bundle installCOPY . $APP
Install the required gems from Gemfile
. Remember we are in the currently root folder of the app because of the WORKDIR
instruction use.
This completes installing the required dependency. Now we only need to boot up the application and expose the public endpoint so that public internet can access our application. Before we do that, it is important to remove any unnecessary dependency to reduce our overall image size which eventually comes in handy when downloading and running applications.
RUN apt-get purge -y --auto-remove \gcc make git \libpq-dev libglib2.0-dev build-essential patch zlib1g-dev liblzma-dev \&& rm -rf /var/lib/apt/lists/*
CMD
instruction which defines the command that launches the process that you want to run within the container.
CMD [ "bundle", "exec", "puma", "-C", "config/puma.rb", "config.ru" ]
And finally, we use EXPOSE
instruction to expose the application running process.
EXPOSE 3000
Here is your final Dockerfile
looks like
FROM ruby:2.6.3-slimLABEL “maintainer”=“viral@fakemail.com”LABEL “app_type”=“backend”LABEL “service_type”=“API”USER rootENV APP /usr/src/appENV RAIL_ENV developmentRUN mkdir -p $APPRUN apt-get -y update && apt-get install -y —no-install-recommends \libgmp-dev \gcc make git \postgresql-client \dnsutils \libpq-dev libglib2.0-dev build-essential patch zlib1g-dev liblzma-dev \&& rm -rf /var/lib/apt/lists/*WORKDIR $APPCOPY Gemfile $APP/GemfileCOPY Gemfile.lock $APP/Gemfile.lockRUN gem install bundler -v 1.17.3RUN bundle installCOPY . $APPRUN apt-get purge -y —auto-remove \gcc make git \libpq-dev libglib2.0-dev build-essential patch zlib1g-dev liblzma-dev \&& rm -rf /var/lib/apt/lists/*CMD [ “bundle”, “exec”, “puma”, “-C”, “config/puma.rb”, “config.ru” ]
Build your Docker image by running,
docker build -t user/repo_name:tag .
But wait, before you can run this docker image locally, make sure your database YAML file is configured properly.
# database.yamldefault: &defaultadapter: postgresqlencoding: unicodehost: dbusername: postgrespassword:# For details on connection pooling, see Rails configuration guide# https://guides.rubyonrails.org/configuring.html#database-poolingpool: <%= ENV.fetch(“RAILS_MAX_THREADS”) { 5 } %>development:<<: *defaultdatabase: rails_docker_blog_development
Now copy this docker-compose
file to your root folder of the project. And run docker-compose up -d --build
version: ‘3’services:db:image: postgresvolumes:- ./tmp/db:/var/lib/postgresql/dataweb:build: .command: bash -c “rm -f tmp/pids/server.pid && bundle exec puma -C config/puma.rb config.ru”environment:- RAILS_ENV=developmentvolumes:- .:/usr/src/appports:- “3000:3000”depends_on:- db
Finally, you need to create the database. In another terminal, run:
docker-compose run web rake db:create
That’s it.
That’s it. Your app should now be running on port 3000 on your Docker daemon.
On Docker Desktop for Mac and Docker Desktop for Windows, go to http://localhost:3000 on a web browser to see the Rails Welcome.
If you are using Docker Machine, then docker-machine IP MACHINE_VM returns the Docker host IP address, to which you can append the port <Docker-Host-IP>:3000
.
Originally published at https://viralpatel.blog
Follow us on Twitter 🐦 and Facebook 👥 and join our Facebook Group 💬.
To join our community Slack 🗣️ and read our weekly Faun topics 🗞️, click here⬇