Reading Time: 7 minutes
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 set up 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.
Let’s start with Docker
My biggest recommendation, if this is your first time using Docker, is to go through the tutorial and installation (I’m using a Mac, but there are tutorials for all the platforms). Next, go through the Rails tutorial setup on the Docker website. This will give you the fundamentals to understand how Docker works and setting up a container to run a Rails app with Postgres.
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.0 RUN apt-get update -qq && apt-get install -y build-essential libmysqlclient-dev RUN mkdir /inventory_manager WORKDIR /inventory_manager ADD Gemfile /inventory_manager/Gemfile ADD Gemfile.lock /inventory_manager/Gemfile.lock RUN bundle install ADD . /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_manage
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).
source 'https://rubygems.org' gem 'rails', '4.2.5' gem 'rails-api', '0.4.0' gem 'mysql2' gem 'puma' # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible # gem 'rack-cors' # Use ActiveModelSerializers to serialize JSON responses gem 'active_model_serializers', '~> 0.10.0.rc3' group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug' end group :development do # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' end
Create the Docker Image
Now it’s time to create the Docker image.
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:
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 .
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, let’s run the web server to see if we can see the Rails status page.
Make sure to find your docker ip by running docker-machine ip
docker-compose up web
docker-machine ip default
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.
Want to test and deploy your microservices with Codeship Pro? Find out more here.
PS: If you liked this article you can also download it as a PDF eBook here: Breaking up your Monolith into Microservices or watch our re-run of our webinar: An Introduction to Building Your Apps with Microservices.