Rails Docker App: Setup

Alex Egg,

Because of Dockers rapid growth, it's difficult to determine if documentation on the internet is out-of-date or not. This paper is written for the Docker 1.12.3 beta that runs native containers on OSX. Before this innovation, the Docker/mac workflow was not optimal.
This is the first part of a 2 paper where we get a rails app up and running on Docker and Kubernetes. See part 2 here: http://www.eggie5.com/82-rails-docker-app-deployment-kubernetes

In this paper will document the process of upgrading this standard monolithic rails app to be managed by Docker & Compose. In the next article, document how to update a more complicated modern SOA rails app to Docker and then deploy to production with Kubernetes.

Motivation

Why Dockerize? Here’s the alternative. If I start from a fresh computer, the reproducible steps needed to bootstrap a complicated stack like rails are very complicated. Here are a few I can enumerate:

Setup ENV

  1. Install xcode build tools
  2. Install ruby and choose version manager(rbenv/rvm)
  3. Install Postgres & javascript runtime and maintain it
  4. Install git and clone the repo
  5. Install the gems (hope native components build)

Start ENV

  1. Start the database
  2. Migrate the database
  3. Start rails

Every time I work the app I have to do these 3 steps. It may not seem that bad, but I have another app suite that is composed of 3 rails apps and 4 databases. It’s a real pain to start all of them up every time I want to work. Hopefully we can fix this w/ Docker and Compose.

Docker

This blog is a standard rails monolith with a PG database.

We need to add a file called Dockerfile to the root of our rails project. In this file we will define everything we need to bootstrap the environment for which this app needs to run.

Dockerfile

FROM ruby:2.2.5
MAINTAINER Alex Egg <[email protected]>

RUN apt-get update && apt-get install -y \
    libpq-dev \
    build-essential \
    nodejs \
    qt5-default \
    wget\
    python2.7-dev \
    vim
    
ENV APP_HOME /app
RUN mkdir $APP_HOME
WORKDIR $APP_HOME

ADD ./Gemfile* $APP_HOME/
RUN bundle install

COPY . $APP_HOME/

CMD sh $APP_HOME/bin/init.sh

This will provision our image based off of a standard ruby image in Dockerhub which is based off of Ubuntu.

init.sh

export SECRET_KEY_BASE=$(bundle exec rake secret)
bundle exec foreman start

We can build our image w/ the build command

docker build -t eggie5/blog .

Database Container

You’ll notice we don’t provision the database in the Dockerfile above. We could have written the commands to download and install it, but there is already a community docker image for Postgres that we can use. This a common pattern in Docker: to compose our app of various atomic containers.

We will boot up the database container and link to it to our rails container:

docker run --name db -e POSTGRES_PASSWORD=password -e POSTGRES_USER=rails -d postgres

The above command pulled the postgres image from Dockerhub, named it db and passed in some default credentials. See the official docs for this image on docker hub: https://hub.docker.com/_/postgres/

Now let’s start our rails container and link it to the db container we just started:

docker run --name web -d -p 3000:3000 --link db:pg eggie5/blog

You’ll notice that we linked the db image to the label pg. So we have a DNS host called pg that the rails app can connect to.

Run the migrations:

docker run --name web bundle exec rake db:migrate

At this point I should be able to hit my localhost and see the site up and running.

There was a lot of manual work linking the two containers together. What if I had 3 rails apps and 4 databases? We can script the orchestration of containers using Docker Compose.

Docker Compose

In a Compose file we can link together many disparate containers. In our simple case, we have just 2 containers: the main rails app and a container that runs our PG database. In a more complicated environment you may have many different rails apps and many different databases that need to talk to each other.

docker-compose.yml

version: '2'
services:
  db:
    image: postgres:9.4.1
    ports:
      - "5432:5432"

  web:
    build: .
    ports:
      - "3000:3000"
    links:
      - db
    volumes:
      - ./rails_app:/app
    env_file:
      - .env.dev

In our Compose file above, we define two services: one for the rails app and one for the database.

Now we have all the infrastructure our environment needs, so lets start the app w/ compose:

docker-compose build
docker-compose up

Now, when I want to work on my app, I just start the environment again: docker-compose up.

To run one-off rake tasks:

docker-compose run web rake db:setup

Or rails console:

docker-compose run rails c

If I ever update the Gemfile, I need to rebuild the docker image, however, if I am just updating the rails app, I don’t need to do anything.

Complicated Example

Here is a Compose file for a rails app with 3 microservices and 4 databases:

https://gist.github.com/eggie5/2291ba0a492725d6c535d044e52323fa

Deployment

Now that this app if fully Dockerized, we can start looking at deployment options. I’ve documented this in the next part of this series: http://www.eggie5.com/82-rails-docker-app-deployment-kubernetes



customize android share menu

Permalink: rails-docker-app

Tags:

Last edited by Alex Egg, 2016-11-04 03:26:15
View Revision History