Why And How to Dockerize your Ruby on Rails Application
It seems like every other startup has added a Docker angle to their pitch. Many developers choose Rails because it allows them to be productive very quickly, but this can come with the tradeoff of dependency, frustration with version management and databases, and file size bloat. Docker helps alleviate a lot of these problems by limiting the scope of dependencies, isolating processes from one another, and allowing us to use optimized images. Of course, another layer of abstraction from your code could very well introduce problems of its own.
It can be a tad confusing at first to denote the differences between Dockerfiles, images, and containers. In a nutshell, Rails applications are launched in ephemeral, replaceable containers and containers are based on images. Most often than not, images are built from other images using a Dockerfile.
From a developer perspective, with Docker, you no longer have to worry about "it working in your local environment but not in production" problem and the, "It's working on my Ubuntu machine but not on my colleague's Mac five feet away." Thanks to a definition file, it is extremely easy to port an app between environment, from local to the lab, and later on to production. Docker happens to be incredibly fast, and thanks to the use of some Linux kernel features like namespaces, you have isolation for networks, filesystem, and processes. This consistency is an especially big win for the users and an incredible opportunity for developers. With Docker, you can reduce the highly disruptive environmental issues to almost zero percent.
So you see, Docker is a tool allowing you to package a service or application along with its dependencies into a standardized unit. This unit is labeled as a Docker image, and it includes everything the application needs to run. The Docker image holds the code, system libraries, runtime, and everything else you would need to make it run if you didn't have Docker.
The advantages of using Docker
If you find yourself constantly looking for ways to better your productivity and improve the whole software development experience, you'll appreciate the following benefits Docker offers.
- Docker lets you encapsulate your application in a certain way that makes it easy for you to move it between environments. It works well in all environments and on all machines capable of running Docker.
- Docker can simplify development across a team if you’re running against a patched version of Ruby, an older version of MySQL, or other dependencies which make setting up a development environment difficult.
- All developers on your team, with Docker, can get your multi-service application working on their workstation in an automated, repeatable, and efficient way. All they have to do is run a few commands, and minutes later it all works.
- Your applications can be started in milliseconds since they are inside of a pre-built Docker image, making it easy to scale up and down. Tasks like installing dependencies, which take up a lot of your time only need to be run at build time once. You can move it around to a lot of hosts, once the image has been built. This not just helps with scaling up and down quickly, but it also makes your deployments more predictable and resilient.
- You might be putting yourself at a disadvantage if you're using only one language. It becomes possible to broaden your horizons by experimenting with new languages and frameworks since you a Docker container lets you isolate an application. You won't have to worry about other developers having to set up your technology of choice. All you have to do is hand them a Docker image and ask them to run it.
- The toolset that comes with Docker allows operation managers and developers to work together while deploying an application. Docker behaves like an abstraction. It lets you distribute an application, while members of another team don't need to know how to setup or configure its environment. It also becomes easy to distribute your Docker images privately or publicly. You can keep a tab on changes, new versions and more.
- Start by selecting a Rails base image. A good base image is the best place to begin. It must give you all the dependencies you require to build an application in development and run it in production. For Rails development, you want the Ruby language itself accompanied by the system libraries necessary to create gems like nokogiri, pg, or mysql2.
- Next prepare your infrastructure or server environment. And get your Docker running. Just follow the Docker documentation to do this, it's pretty straightforward. Choose your platform, follow the instructions, and there you go.
- Next, we work on the Rails image. Docker makes use of a definition file to describe what the environment will contain. Docker uses an image, which is a read-only template, to run your code through a container. The image has all the magic stuffed into it. It has the OS binaries, libraries, dependencies and all the applications that you need to run your code or application.
- Now, we write a Dockerfile. There are only a handful of commands we’ll need to use with Docker. They are:
- FROM: This describes the image we’ll be basing our container off of.
- RUN: As always, to run commands.
- WORKDIR: This happens to define the base directory from where all our commands are executed.
- ADD: It copies files from the host machine to the container. Each command generates a new cached image, and this can be built upon later. Files with higher churn, or packages which might change, must be placed way below in the Dockerfile to leverage the cache to its fullest.
- Even though you've successfully built a Docker image to house your Rails app, there are other dependencies. You might have to configure a database like Postgres and ensure the database container and the application container can communicate with each other. This is where Docker Compose truly shines.
- Run your specs and wrap up. This is just an introduction to how to handle one application running inside Docker. Your apps likely have different sets of dependencies, so get comfortable with Dockerfiles quickly.