Like A Girl

Pushing the conversation on gender equality.

Beginner's Guides Code Like A Girl

How to dockerize your Ruby on Rails development environment

It’s always better in a container — Credit Philip Moris

I have long dreaded the complicated setup involved when starting at a new company, but the Paris real estate startup that I worked at was particularly difficult. My Linux distribution, although based on Ubuntu, doesn’t always react the same way so I would get weird error after weird error.

Docker is an open source container engine that allows you to run apps and services in isolated containers. Nothing is installed on your system, and you don’t need to track every last file when uninstalling it. After dockerizing my development environment, the setup went from hours and hours to just minutes. If you have several projects that need different versions of the same service, Docker can make it really easy. Docker is also available on all platforms and will make your setup documentation much clearer, and will also allow everyone to have the exact same environment.

Getting started

The first thing to do is to install docker. Then, depending on how you installed it, you may need to install docker compose. If you’re on linux, add your user to the docker group with the following command and logout after to see the changes:

sudo usermod -aG docker $USER

You can test your docker install with the coolest image ever, Jess Frazelle’s Hollywood image, type this in a terminal:

sudo docker run -it --rm jess/hollywood

At my company, our stack is Postgresql for the database, Elasticsearch for indexing, Redis for queuing jobs, and Ruby on Rails for the web app. I will go through the steps to put each of these in a separate container.

Create images with Dockerfile

Containers are created from images, which are read-only file systems, that contain the application you need and the environment required by the application to run. Postgresql, Elasticsearch and Redis are services that already have existing images on Docker hub that we can use, but my web app does not since it’s a proprietary software.To create a custom image you need to create a Dockerfile that describes what should be installed in it.

Images can be thought of like classes in object oriented programming, and containers like instances of that class. Images, can be based on other images and several containers of an image can run at the same time. My rails app requires Ruby 2.4.1 so I based my image off the existing one. Here is my Dockerfile for the web app that I put at the root of my app directory:

As I said earlier, our services have existing images on the Docker hub, so you need to find an image for the version you want. My version of Elasticsearch is 5.2.1 but you can find your own version here. I also need Redis 5.0 (images for other versions here) and Postgresql 9.6.9 (images for other versions here).

Let’s put them all together with docker-compose

Now that we know what images we will use, we need to create a file called docker-compose.yml (still at the root directory of the web app) in which we will orchestrate the creation of containers and their communications. Compose files can have several versions, the latest and recommended one being 3. We first need to put all our images under the service instruction. In Docker, services are “containers in production” (more info) and all the services together will make up the development environment you need.For each service that use existing images we provide the name of the image under the image instruction, and for our web app we build from the Dockerfile we created above by specifying the path of the Dockerfile. In order to access these services from our localhost, we need to bind ports. The syntax for binding is local_port:container_port. We can also bind volumes so that directories or files are shared between our localhost and our container. It can be useful for Postgresql and Elasticsearch to persist the data, and is used to share code for the web app. The syntax for this is local_path:container_path. finally, the depends_on instruction allows the other services to be started before our app and lets us start a service with all its dependencies when we start it on its own.

Here is my docker-compose.yml:

Useful commands

Everything is now in place to start your fully dockerized environment ! Use these commands to manage your environment.

Create and/or start the environment :

docker-compose up

Stop the environment :

docker-compose stop

Start a shell :

docker exec -ti [CONTAINER NAME] bash

With this command you can execute anything you want, rails c, bundle install, etc, it doesn’t have to be bash. If you want even more commands, you can check out this Docker and Docker Compose cheat sheets.

In summary

I walked through how to install Docker, create images with Dockerfile, and orchestrate the creation of containers using Docker Compose. I also listed some of the most common commands you will use to manage your environment. Your entire development environment can now be started with one command. Every new hire will be able to install the whole thing very quickly and your whole team will share the same development configuration.