Dockerize Existing Ruby on Rails API with docker-compose.yml
Docker is a great tool to develop and manage your Ruby on Rails applications locally. It allows you to easily isolate your ruby environment, database and other services like cronjob etc. Where as docker-compose file brings together different services to be used by each other. In this guide we’re going to cover:
- Creating a new Rails 5 App (Optional)
- Setting up the App for dockerizing
- Adding Dockerfile and configure docker-compose.yml to run rails server
Pre-reqs
You should have docker for mac installed. You can get it here. Basic knowledge of Docker and Rails is also helpful.
If you are looking to add delayed_job and cron to your dockerized app you can find it here (Add background jobs and cron to your dockerized ruby on rails app).
Step 1 — Create a Rails 5 App
First, we are going to create a Rails 5 API app with Postgres. If you already have an app you can skip this section.
To create a new rails project, type the command below in your terminal:
$ rails -v
Rails 5.2.1$ rails new rails-docker -d postgresql --api
This should create an app with the folder name rails-docker
. Go into the folder and open it in your favorite text editor. I am using sublime text:
$ cd rails-docker
$ subl .
At this point, you can create the database by running rails db:create
and do rails s
to start the server but you don’t have to do this, as we will be doing it using docker-compose.
Step 2 — Setting up the App for dockerizing
We will make some changes to our app before we dive into creating Dockerfile.
First, we will add dotenv-rails
gem in our project. We are using this to maintain environment variables for the App. So add the following line in your Gemfile
under development and test group:
gem 'dotenv-rails'
Now open config/database.yml
and replace all the content with the following:
development:
url: <%= ENV['DATABASE_URL'].gsub('?', '_development?') %>test:
url: <%= ENV['DATABASE_URL'].gsub('?', '_test?') %>production:
url: <%= ENV['DATABASE_URL'].gsub('?', '_production?') %>
and also create .env
file in your project root folder and add the below line in it:
DATABASE_URL="postgres://root:mysecretpassword@db:5432/rails_docker_db_name?encoding=utf8&pool=5&timeout=5000"
Here we are using the same DATABASE_URL
env variable and appending environment name to the database name. So for the development environment, database name will be rails_docker_db_name_development
.
That's all for the project setup. Let’s move onto the interesting part.
Step 3 — Adding Dockerfile and configure docker-compose.yml
Let's add theDockerfile
to the project root folder with the following content:
FROM ruby:2.4.3ENV APP_HOME /rails-docker# Installation of dependencies
RUN apt-get update -qq \
&& apt-get install -y \
# Needed for certain gems
build-essential \
# Needed for postgres gem
libpq-dev \
# The following are used to trim down the size of the image by removing unneeded data
&& apt-get clean autoclean \
&& apt-get autoremove -y \
&& rm -rf \
/var/lib/apt \
/var/lib/dpkg \
/var/lib/cache \
/var/lib/log# Create a directory for our application
# and set it as the working directory
RUN mkdir $APP_HOME
WORKDIR $APP_HOME# Add our Gemfile and install gems
ADD Gemfile* $APP_HOME/
RUN bundle install# Copy over our application code
ADD . $APP_HOME# Run our app
CMD RAILS_ENV=${RAILS_ENV} bundle exec rails db:create db:migrate db:seed && bundle exec rails s -p ${PORT} -b '0.0.0.0'
Command breakdown:
FROM ruby:2.4.3
tells Docker to derive our image off of the Ruby image with version 2.4.3.ENV APP_HOME /rails-docker
defines an environment variable that will be available to this instance.- Next line we are installing all the dependencies needed for our app to run.
RUN mkdir $APP_HOME
andWORKDIR $APP_HOME
creates a directory and makes it as a working directory. This means we will be in this directory by default.- Now we are adding
Gemfile and Gemfile.lock
to our docker directory that we just created and runbundle install
to install all the gems. - Next, we are adding the rest of the project to our app directory and specifying our default command using
CMD.
If you noticed we areRAILS_ENV
andPORT
env variables which we will now add in.env
file.
Add the following in your .env
file:
RAILS_ENV="development"
PORT=3000
We will also add .dockerignore
file in the root directory with the following content which will tell the docker to not copy these folders or files to the container whenever we build the image.
.dockerignore
.git
logs/
tmp/
Now we will create docker-compose.yml
file which will define our web
and db
service.
version: '3'
services:
db:
image: postgres
environment:
POSTGRES_USER: root
POSTGRES_PASSWORD: mysecretpassword
volumes:
- pgdata:/var/lib/postgresql/data
web:
build: .
command: bundle exec rails s webrick -b '0.0.0.0'
volumes:
- .:/rails-docker
ports:
- "3000:3000"
depends_on:
- db
tty: true
stdin_open: true
volumes:
pgdata:
Here, we are defining 2 services, First is db
service which uses Postgres image from docker hub. Notice we used db
service as the host in our DATABASE_URL
in the .env file. Second is web
service which is built through our Dockerfile
and we have provided the command to run this service. We also mapped the port of container to our host port i.e. 3000. And we have mentioned that web
is dependent on db
service. That’s it, now let's try to build it. Type following in your terminal:
$ docker-compose build
Once the build is complete, you should see the following output:
Now run the below commands to create and migrate the database and run the server:
$ docker-compose run web rails db:create db:migrate
$ docker-compose up
Go to your browser and open http://localhost:3000
and you will see the app home page.
Congrats, you have completed the first milestone. Give yourself some treat ;)
You can grab the final code here: https://github.com/ankitsamarthya/dockerized-rails
Conclusion
This is just a start, there's a lot to learn about docker and docker-compose. Don’t worry I will walk you through the process from development to production to set up continuous deployment with AWS ECS.
You can continue to part 2 of this tutorial here (Add background jobs and cron to your dockerized ruby on rails app) where I show you how you can add delayed_job and cron in this dockerized app.
See you soon. Until then, keep coding. :)