How to Upgrade Any Rails Application Using Docker

Every time we start a new Rails upgrade project, we need to setup a whole new environment in our local machines. Sometimes that leads us down the rabbit hole which ends up breaking our environment for other client projects.

After years upgrading Rails applications, we learned that the best way to isolate our client projects' environments is using Docker.

That's why we decided to use Docker and docker-compose for all of our client projects. This year I had the opportunity to share our process in a series of workshops: Upgrade Rails 101: The Roadmap to Smooth Upgrades

A couple of weeks ago I ran the latest iteration of our workshop at Southeast Ruby '19. I showed what it would look like if we were to upgrade the e-petitions application.

In order to set up all the services I used this docker-compose configuration:

version: "2"

services:
  cache:
    image: memcached:1.4-alpine
    ports:
      - "11211:11211"
    restart: always

  db:
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD:
    image: postgres:9.6
    expose:
      - "5432"

  app:
    stdin_open: true
    tty: true
    restart: always
    env_file: .env.test
    build: .
    ports:
      - "8080:8080"
    depends_on:
      - db
      - cache
    volumes:
      - .:/usr/src/app:consistent

This application uses Memcached; Postgres 9.6; Ruby 2.3.8; and Rails 4.2. That's why you will find these services in its docker-compose.yml:

  • db (Postgres)
  • cache (Memcached)
  • app (Rails 4.2)

In order to setup the app service, I defined a Dockerfile like this:

FROM ruby:2.3.8-jessie

RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ jessie-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list

RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -

RUN apt-get update -yqq \
  && apt-get install -yqq --no-install-recommends \
    build-essential \
    postgresql-client-9.6 \
    nodejs \
    vim \
    libpq-dev \
    qt5-default \
    libqt5webkit5-dev \
  && apt-get -q clean
RUN apt-get update

WORKDIR /usr/src/app

COPY Gemfile Gemfile.lock ./

RUN gem install bundler -v=1.17.3

RUN bundle install

COPY . .

As every other Rails application, it is not 100% standard. For instance, to setup the database you need to load the structure.sql file. So I decided to write a quick script (bin/docker-setup) to setup the test database:

#!/usr/bin/env ruby
require 'pathname'

# path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../',  __FILE__)

Dir.chdir APP_ROOT do
  puts "== Creating databases =="
  system "rake db:create"
  system "psql -q -h db -U postgres -f db/structure.sql epets_test"
end

That way, if you want to build the services you can just do this:

docker-compose up -d
docker-compose exec app ./bin/docker-setup

Taking this approach makes everything easier to reproduce. Any developer can use this configuration to spin up a few virtual machines. It doesn't matter if their machines are running Windows; Linux; or Mac.

During the workshop, the biggest blocker we had was download speed. It took between 10 and 20 minutes to get everything set up. The two steps that took the most time were:

  • Downloading and installing Docker
  • Building the images for the first time

Once that was done, the process went smoothly. We got to calculate test coverage; setup dual booting; run our test suite with two different versions of Rails; and fix a bunch of problems related to the jump from Rails 4 to 5.

If you are interested in applying these steps to your own application, you can check out the companion page for the Upgrade Rails 101 workshop.

The slides for my presentation are over here: Upgrade Rails 101 Workshop at Southeast Ruby '19


Are you using Docker to simplify your Rails upgrades? If not, what are you using to normalize environments in your team? I hope you found this article useful and I look forward to reading your comments below!