Introduction
Docker is an open-source project that can simplify your workflow during development process, everything the application needs to run is included. The Docker image contains the code, runtime, system libraries and anything else which permits to run the applications inside an isolated containers.
There are a dozen reasons to use Docker during the development, the main benefits to dockerize an application are
- consistency provides repeatable development, build, test, and production environments
- isolation makes sure each container has its own resources that are isolated from other containers.
- portability allows you to package and share your code with others.
This tutorial will show you how to set up a development environment for a Ruby on Rails application using Docker Compose.
Prerequisites
To follow this tutorial, you will need to ensure docker and docker-compose are working on your local machine
➜ ~ docker --version
Docker version 19.03.8, build afacb8b
➜ ~ docker-compose --version
docker-compose version 1.25.4, build 8d51620a
➜ ~
Generate a new rails project
The first step will be to generate a new rails project, we can skip webpack and bundle install tasks and delegate it to the container in a short. We will use PostgreSQL as a database and the following command generate a new application which includes pg and configure PostgreSQL instead of SQLite.
➜ ~ rails new rails-docker -d postgresql --skip-webpack-install --skip-bundle
Writing the Dockerfile
Using a Dockerfile allows you to define your container environment and avoid discrepancies with dependencies or runtime versions.
FROM ruby:2.6-alpine
WORKDIR /app
RUN apk add --update --no-cache \
bash \
binutils-gold \
build-base \
curl \
file \
g++ \
gcc \
git \
less \
libstdc++ \
libffi-dev \
libc-dev \
linux-headers \
libxml2-dev \
libxslt-dev \
libgcrypt-dev \
make \
netcat-openbsd \
nodejs \
openssl \
pkgconfig \
postgresql-dev \
python \
tzdata \
yarn
EXPOSE 3000
Defining Services with Docker Compose
Using Docker Compose, we will be able to run the multiple containers required for our setup. We will define our Compose services in our main docker-compose.yml file.
version: '3.4'
services:
app:
build: .
image: rails-docker
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
environment:
BUNDLE_PATH: /usr/local/bundle
DATABASE_HOST: postgres
RAILS_ENV: development
depends_on:
- postgres
ports:
- '3000:3000'
stdin_open: true
tty: true
volumes:
- ${PWD}:/app
- bundle:/usr/local/bundle
- node_modules:/app/node_modules
postgres:
image: postgres:12.2-alpine
environment:
POSTGRES_USER: root
POSTGRES_HOST_AUTH_METHOD: trust
volumes:
- postgresql:/var/lib/postgresql/data
volumes:
postgresql:
bundle:
node_modules:
Configure the Ruby on Rails Application
Before to run a rails application we need to launch several tasks in order to have the environment ready. The first one is to install all ruby dependencies running bundle install
docker-compose run app bundle install
Starting with Rails 6, Webpacker is the default JavaScript compiler. It means that all the JavaScript code will be handled by Webpacker instead of the old assets pipeline aka Sprockets. As a default node package manager rails application uses yarn
and required dependencies can be installed with the command
docker-compose run app yarn install
Finally we're almost ready to launch the main application, we just need to setup database and create the schema.rb. Let's do it with well know command db:create and db:migrate
docker-compose run app rake db:create db:migrate db:seed
Testing the Application
First, build the container images and create the services by running docker-compose up with the -d flag, which will run the containers in the background:
docker-compose run -p 3000:3000 app
Active Storage Support
Active Storage facilitates uploading files to a cloud storage service like Amazon S3, Google Cloud Storage, or Microsoft Azure Storage and attaching those files to Active Record objects.
If your application needs to support storage and/or images processing you need to add in the Dockerfile additional dependencies
RUN apk add --update --no-cache \
imagemagick \
poppler \
poppler-utils
You can add it with the new RUN
directive (if you're using a docker multi stage build feature) or append it in the previous.
Procfile
If your application needs to launch multiple processes during development, you can use a Procfile approach to manage the processes.
You need to install foreman
before to create and configure a Procfile, since our image is based on ruby the simple command in the Dockerfile will install all the required libraries.
RUN gem install foreman
The next step is to configure it creating a Procfile.dev
with a list of processes, for example:
web: bundle exec puma -C config/puma.rb
worker: bundle exec sidekiq -C config/sidekiq/default.yml
and change the command
in the docker-compose.yml configuration
command: bash -c "/usr/local/bundle/bin/foreman start -f Procfile.dev"
Conclusion
By following this tutorial, you have created a development setup for your Rails application using Docker containers. You’ve made your project more modular and portable by decoupling your application’s state from your code.
Containerization is a wonderful way to abstract the infrastructure layer of your application, to ship code as efficiently as possible, but now what is next?
- Push the Docker image into a private registry
- Deploy your application in the cloud
Thanks for reading so far! Looking forward to hear your comments, questions, improvements! I am very grateful for your feedback
Dockerize a Ruby on Rails Application using Compose via @safeforge
Click to tweet