Applications built on top ruby framework such as Ruby on Rails are powerful, scalable and comparatively easier to build than most other frameworks. However, it is an entirely different situation when it comes to deploying them consistently across various environments. This is where, Docker enters the picture.
Maintaining consistency from development to production is a common challenge faced by developers irrespective of their web development framework choice. And to solve this problem, the most popular solution popularly loved by developers is to dockerize their application.
Before understanding how to dockerize Ruby on Rails application, let’s start by understanding the fundamentals of Docker.
Understanding Docker – What is Docker?
Docker is somewhat like a magic box for developers. Let’s take a basic analogy for understanding how. Assume you are a chef who is preparing a meal in the kitchen. You have a certain recipe (your Ruby on Rails app) that you feel like sharing with others. However, every individual who wants to make your dish can have a different kitchen setup, which could cause problems.
In the world of software development, this ‘kitchen’ refers to the environment where your ‘meal’ (application) runs. It includes all the necessary ingredients – operating system, libraries and configurations, needed to run your app smoothly. Now if the environment or ‘kitchen’ of your friend differs from that of yours, it will lead to conflicts or problems in replicating the same ‘meal’.
However, if you are to use the magic tool that Docker is, you can pack your ‘meal’ with the ‘kitchen’ in a ‘magic box’ (container) ensuring the person receiving your ‘meal’ gets the perfect meal alongside the right kitchen settings. This helps deliver consistency irrespective of where you deploy your app.
To put it in technical terms, Docker simplifies deployment process through containers. Containers are the self-sufficient units that contain everything needed by your Ruby on Rails application.
Docker has certain helpers in terms of some of its key elements that helps with this magic trick. Let’s understand their role in dockerizing your Ruby on Rails app.
Docker Key Concepts to Know
Docker is one of the most popular tools for bringing simplicity and consistency to an often-complex process of managing applications. It allows you to build, share and run applications anywhere without having to worry about the tedious environment setup or management. At the core of Docker’s magic are certain key concepts that form the backbone of its functionality. These concepts streamline the packaging, distribution, and execution of your Ruby on Rails application.
Let’s learn more about them:
A Dockerfile is like the blueprint of your app’s ideal home design. How builders use architectural plans to build a house, Docker makes use of Dockerfile to build an ‘image’ that acts as a self-containing package that contains your application, dependencies, and all the necessary configurations.
This file needs to include the entire roadmap:
- Setting up the foundation – choosing a base image
- Adding layers – installing dependencies
- Configuring the interiors – setting environment variables
With a properly crafted Dockerfile, your Ruby on Rail application can be deployed to any environment without worrying about compatibility complexities that generally accompany software development.
Basic Dockerfile Commands
|Defines what image you need to start from. For our case, it is advisable to use Ruby’s official image as a starting point.
|Specifies build-time arguments, allowing parametrization during the image building process.
|Executes commands when building images. Generally used for installing dependencies, setting configurations or running scripts.
|Responsible for setting environment variables in the image, useful for configuring runtime behaviour of the containers created from the Rails image.
|Specifies the default command that should be triggered when a container starts from its image. It defines the primary application process.
|User is used for setting a username or a unique UID to use when running the image. It provides user context for the container.
|Sets the working directory for any subsequent instructions mentioned in the Rails Dockerfile.
Next in line is Docker Compose. As the name suggests, Docker Compose is responsible for orchestrating a symphony of containers. Or it can be considered similar to writing a script to a play. Each container has a specific role, and Docker Compose is responsible to ensure they harmonize effortlessly. For apps requiring multiple services in form of web servers and databases, Docker Compose allows users to define and manage these services from a single, comprehensible file.
Images in Docker are a set of instructions in form of executable code used for building a Docker Container. They can be thought of as basic templates for creating Docker containers.
A Docker Image needs to include:
- Rub on Rails Code
Images are the building blocks of your containers that allow your Ruby application to be transported and executed seamlessly across various environments. Docker images makes sharing your Ruby on Rails as easy as sharing a recipe, while ensuring it is prepared and served the same way, irrespective of the environment it is used in.
Containers are the live instances of your app that are brought to life via Docker images. They are used for encapsulating your app and its dependencies while providing a portable and lightweight environment. Containers also provide isolation for your applications to run without depending on your host system. This isolation makes it easier to use your Ruby on Rails application for development, testing or production phases.
Getting Started with Docker for Rails
Now that we have a basic understanding of how Docker works, let us show you the ropes from setting up Docker on your machine to creating Rails dockerfile and running your app in rails docker container, each step will be covered in proper details to ensure you are able to set up Ruby on Rails on Docker seamlessly.
Installing Docker on your Development Machine
You would start by installing Docker on your development machine (Mac, Windows, Linux, others). The instructions for installing Docker on specific devices can be found on their official Docker Docs Website:
It is a pretty straightforward installation process with slight variations depending on your OS. Once you have installed Docker, you can verify its installation by running this command on your terminal or command prompt:
Running this command should display the Docker version you installed. This confirms that you have successfully installed Docker.
I. Setting up Basic Ruby on Rails Application
If you plan to dockerize existing Rails app, you can skip this part. However, if you are setting up a new Rails application, make use of this command:
$ rails new myapp --database=postgresql
Once you are done, navigate to your app’s directory
This will create a new Rails app with PostgreSQL as the database. Now we will use Bundler for installing the dependencies:
$ bundle install
We will configure the database connection as per our needs. Go to the
config/databse.yml directory and use these settings:
This will point your app to connect to a PostgreSQL database container that we will set up afterwards using Docker.
II. Writing a Rails Dockerfile
Create a file named ‘
Dockerfile’ in the Rails app root directory. Open this file in a text editor and define this file with the necessary instructions. Here is how a sample Rails Dockerfile will look like:
# We will use the official Ruby runtime for the parent image
# Install Dependencies
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client
# Create app directory and set it is working directory or WORKDIR
RUN mkdir /myapp
# Copy all the GEM Files and Install Bundles
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp
# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
In this Ruby Dockerfile we are using an official Ruby 2.7 base image as starting point and installing dependencies such as NodeJS and Postgres, copies of the Gemfiles and running bundle install for copying rest of the codebase.
We also added an entrypoint at the end which is a script that will run before starting the Rails server and configure the database.
# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid
# Then exec the container's main process (what's set as CMD in the Dockerfile).
III. Building and Running Dockerized Ruby on Rails App Locally
We successfully created the Dockerfile and set up our Ruby on Rails app. Now let’s build the Ruby Docker Image. Make sure to do this in the same directory as your Dockerfile:
docker build -t myrailsapp
Next, you can create a container for the database –
$ docker run -d --name db -e POSTGRES_DB=myapp_development -e POSTGRES_PASSWORD=mypass postgres
Once your build is complete, you will be able to run your Rails app in the Docker container on a localhost:
docker run -p 3000:3000 myrailsapp
Upon visiting the URL – http://localhost:3000’ in your machine’s web browser you will be able to see the Dockerized Ruby on Rails app, running locally.
Next Steps – Using Docker Compose for Rails
We created a basic Rails docker app that can be run on a local host by using a Dockerfile. However, if you are planning to build a full-fledged project foe real-world application, we will need to rely on multiple services and containers that can coordinate together.
What is Docker Compose for Rails?
For this, we will make use of Docker Compose for our Rails app. Compose as we discussed earlier is like the script for your Ruby on Rails application architecture that comes in a YAML file. It describes all the services (containers) which are a part of your app stack.
How does it help manage Multi-Container Applications?
Docker Compose simplifies managing multi-container applications by providing a single file instance in form of ‘
docker-compose.yml’ where you can specify the services, networks, volumes and other such aspect of your app code. This helps maintain a consistent environment across various containers, which makes managing dependencies and interactions a whole lot easier.
How to Create Docker Compose File for Ruby on Rails with a Database?
The Docker Compose file generally includes sections for services, web, networks and volumes. If your app has a database, you can also define or specify the environment variables, ports, and other such configurations required for each service.
While using Ruby on Rails Docker Compose isn’t a compulsion for creating a basic Ruby on Rails app, it is advisable to use it for applications with growing complexity or expanding services. Let’s see how this would look like if we continue with our previous example:
Sample docker-compose.yml file for a Ruby application:
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
This will help us set up a PostgreSQL service and a Rails service that come with porting, volumes, mapping and dependency linking automatically configured.
Next, you can simply run:
Upon running this command, Docker Compose will read the configuration settings of ‘
docker-compose.yml’ file and start the defined services. The rails app and PostgreSQL database will be initialized in a ready to interact format.
How to Add Docker in your Testing Workflow?
Testing your Ruby on Rails app with Docker has many advantages. Docker simplifies setting up consistent testing environments that can be run in isolation just like other containers in Docker.
Creating a Testing Rails Dockerfile
You begin by creating a Dockerfile specifically for testing. You will add all the necessary dependencies and configurations needed for running and executing your test suite. A Rails Dockerfile for testing can look like:
# Example Testing Dockerfile
FROM ruby:2.7 as testing
COPY . /app
RUN bundle install
CMD ["rails", "test"]
Running Rails Tests Inside Docker Containers
Once your testing dockerfile is ready, you will make use of Docker Compose for defining testing service which replicates or mirrors your production environment’s dependencies. The test should be done in an environment that accurately resembles to the deployment environment you plan to launch your Ruby on Rails app on. This testing file will be named something as –
Ruby on Rails Testing Docker Compose Code Example
# Example docker-compose.test.yml
By setting up this test suite with Docker containers, you will be able to provide consistency and reproducibility across various environments.
How to setup continuous integration with Rails Docker?
You will start by selecting the CI/CD platform that seamlessly integrates with Docker and Ruby on Rails. You can try Jenkins, GitLab CI or even GitHub Actions for this. Configure your CI pipeline to employ Docker for test execution, image building and packaging your Rails app.
Ensure smooth transition from Dev to Production Environment for Ruby on Rails
Moving between environments need to be done strategically and cautiously. It is important to maintain a certain degree of consistency across different stages of the Rails application and setting up Docker Compose for Rails.
Providing Consistent Environment Across Rails Stages
We already know how Docker provides invaluable ease of encapsulating Rails dependencies, configurations and settings within containers. You need to deploy the same Ruby Docker image across different stages and utilizing environment variables for stage-specific customizations.
Orchestrating Docker Compose for Rails
Docker Compose is instrumental in orchestrating multi-container setup for Rails. The docker-compose.yml file needs to encapsulate configurations for both – production and development environments. This ensures consistency between environments and also makes transitioning between the two environments and different stages of your Ruby on Rails SDLC much easier.
Ruby on Rails Dockerization Best Practices
Yes, the overall process to dockerize Ruby on Rails app is pretty straightforward. However, it is filled with an array of possibilities and requires careful navigation by taking a thoughtful approach to ensure seamless integration of Ruby on Rails in Docker. Hence, we will delve into some of the Ruby on Rails dockerization best practices that will help you steer towards a seamless, efficient and scalable ROR deployment and fortified against potential vulnerabilities.
Here are the best practices to Dockerize Rails App:
1. Always use official Base Images
Your Dockerfile should always begin with the official base image of whichever Rails version you plan to use. This ensures you get a clean, standardized and efficient starting point, reducing need for unnecessary dependencies.
2. Make use of Layer Caching
When structuring your Dockerfile you should place commands that are less likely to change as frequently (dependencies) before the ones that would change more often (app code). This will enable you to take advantage of Docker’s layer caching mechanism. According to this mechanism, whenever you add a new instruction in the dockerfile, it adds a new layer to the image. These layers are then cached, and Docker determines which layers have changed based on the instructions provided in dockerfile. This makes the build process much more efficient, reduces bandwidth and storage, leading to faster CI/CD pipelines.
3. Reduce Image Size
Keep the image size as minimal as possible by cleaning up any unnecessary artifacts by using the multi-stage builds. This helps minimize the number of layers.
FROM ruby:2.7 AS builder
# ... Build stage
COPY --from=builder /myapp /myapp
# ... Final stage
4. Use Environmental Variables wisely
Leverage these environmental variables for configuration settings that are likely to vary in different environments like database, URLS and APIs keys.
5. Don’t Hardcode Sensitive Information
Hardcoding sensitive Ruby on Rails app information is never a good idea. Don’t hardcode passwords in the Dockerfile. Make use of environment variables for injecting them during runtime instead.
By adhering to these best practices to dockerize your Rails app, you ensure that your dockerized Rails app is efficient, easily configurable, and highly secure. This lays the groundwork for a robust and maintainable deployment process.
This is your ultimate guide on how to dockerize Ruby on Rails application. Dockerizing a Rails app provides a portable and consistent environment. Using Docker Composer orchestration helps streamline the management of containers. You can use Docker for Rails to simply your updates and scaling requirements with ease, enhancing your Security of Ruby on Rails application, performance, testing, development and deployment.