Building Microservices with Docker and the Rails API gem

Charles Wang
Inside thredUP
Published in
6 min readJan 19, 2016
photo credit: http://www.packet.net

Why Choose Docker + Rails API ?

At the company that I work for, we have been using Docker to have consistent development environments for all of our engineers. This makes onboarding a new engineer a piece of cake. I’m still particularly new to Docker and want to share my experience just getting a basic Rails API application setup as a microservice. Like most companies that struggle over the long run with a monolithic Rails app, a microservice architecture provides loose coupling, strong cohesion, independent deploys, and much more.

I’ve built quite a few applications where we’ve used Rails as an API, which is a little overkill since you don’t need all the view logic and additional middleware. Instead, we’ll be using the Rails API gem to build out our microservice APIs. The Rails API gem is integrated into the Rails 5 release, which is currently in beta. I’ve explored this option using Docker, but struggled with setting it up so I’ll be writing about using a Rails 4 setup and adding the rails-api gem as a dependency to the Gemfile.

You may ask yourself why use Rails API as the tech stack for your microservice. I think the answer lies on what works best for your team. There are plenty of talks on the internet about whether or not your team should move towards microservices and what technology to use. There are a ton of other great technologies where using Node.js, Go, Scala, or Java might fit your situation. I’ve spent some time exploring some of these options and I think they are all great, but my team knows Rails the best and we can develop quickly with it. If I were to pick another tech stack that would fit well with my team, I would go with Node.js since a lot of our new apps are being built with it. I think the overall goal is to make sure your services are small enough to be rewritten if needed.

Lets start with Docker

My biggest recommendation, if this is your first time using Docker, is to go through the tutorial and installation at https://docs.docker.com/mac/ (I’m using a mac, but there are tutorials for all the platforms). Next, go through the Rails tutorial setup on the Docker website https://docs.docker.com/compose/rails/ . This will give you the fundamentals to understand how Docker works and setting up a container to run a Rails app with Postgres.

The Setup

This tutorial is for Mac users, but you can adopt it for a Linux distribution or figure out the equivalent on a Windows platform. First create the directory for your project within the terminal. I’ll be making a project called “inventory manager”, but you can name your project whatever.

mkdir inventory_manager && cd inventory_manager

Then create a few files within the root directory of the project.

touch Dockerfile docker-compose.yml Gemfile Gemfile.lock

Setting up the Dockerfile

The Dockerfile is everything we need to set up our Docker container’s environment.

FROM ruby:2.3.0RUN apt-get update -qq && apt-get install -y build-essential libmysqlclient-devRUN mkdir /inventory_managerWORKDIR /inventory_managerADD Gemfile /inventory_manager/GemfileADD Gemfile.lock /inventory_manager/Gemfile.lockRUN bundle installADD . /inventory_manager

So what is happening in this file? Here we are creating an image using the latest version of Ruby (2.3.0) at the time of this writing from https://hub.docker.com/. We then run the following apt package manager commands on the container. We’ll be using the MySQL client library for development. We’ll then add the local Gemfile and Gemfile.lock to our container’s filesystem and then install the dependencies. Lastly, we add all the contents of the project directory to the container.

Setting up the docker-compose.yml

We need to setup our docker-compose.yml file so that the Docker Compose tool can orchestrate the communication between our Rails API app and our MySQL container.

db:
image: mysql:latest
ports:
— “3306:3306”
environment:
MYSQL_ROOT_PASSWORD: mypassword
web:
build: .
command: puma
ports:
— “9292:9292”
links:
- db
volumes:
— .:/inventory_manager

This configuration sets us up with a mysql container and also a container called web that builds from the Dockerfile and runs the Puma webserver on port 9292. We then link the Rails app container to the mysql container so that they can communicate to each other.

Setting up the Gemfile.

Inside the Gemfile we will add the Rails gem, the Rails API gem, the mysql client adapter, Puma web server, and the Active Model Serializers (this is for your formatted JSON responses).

Create the Docker image

Now its time to create the Docker image.

docker-compose build
running the docker build command will go through your Dockerfile to build the image and then install gems.
docker image successfully created

You will need to run this command to rebuild your Docker image anytime you make a change to the Gemfile or the Dockerfile . You can see that an image is created by typing:

docker images

Create the Rails API application structure

Next we’ll create the application structure by running the Rails API commands in the Docker image. In our docker-compose.yml we have named “web” as the container to run our commands against.

docker-compose run web rails-api new .
Creating the rails api application structure

Setting up the database

Now we need to configure our database.yml file so we can start writing a migration.

development:
adapter: mysql2
encoding: utf8
reconnect: false
database: inventory_manager_dev
pool: 5
username: root
password: mypassword
host: db
test:
adapter: mysql2
encoding: utf8
reconnect: false
database: inventory_manager_test
pool: 5
username: root
password: mypassword
host: db

The host is “db”, which is the same name we’ve defined in our docker-compose.yml file. This host’s value must match what you’ve defined as your database container in the docker-compose.yml file.

Lets test to see if our web applications runs

Now that the app structure has been built, lets run the web server to see if we can see the Rails status page.

docker-compose up web
Runs both our database and web app in their respective containers and then runs the puma server listening on port 9292
my docker-machine ip is running on port 192.168.59.100

Make sure to find your docker ip by running docker-machine ip <docker vm host> (for me this is aliased to default).

docker-machine ip default

Next Steps

Now that we’ve got our app running we can start development by creating data models, controllers, etc…

We can create more microservices in this manner and facilitate communication between apps using JSON over http by exposing different web server ports on the same Docker host. Another form of communicating between services is by utilizing messaging such as RabbitMQ. I’ll most likely be writing another blog post on RabbitMQ and communicating between services and how to get that setup.

I hope this post helps others as this was definitely a learning experience for me and I’m continuing to learn more and more about Docker. Feel free to comment and make any suggestions on improving the setup.

Resources

https://hub.docker.com/r/clwang/rails-api-mysql/

Want to know more? Hit me up at charles@thredup.com

--

--