Monday, June 25, 2018

Docker with Zeus for Rails Development Bliss


I've been running my development environment in Docker for a couple of months, despite the fact that we deploy natively in the various non-dev environments. It's actually working pretty well, chiefly because I am using Zeus as the container command. With this setup, running Zeus client commands for the server, console,  specs, etc, are near-instantaneous.

First -- why would I do this? I had a few reasons.
  1. Some components in my native setup on MacOS quit working. In particular, Zeus (application preloader)
  2. I moved to a new laptop at work, and I wanted to make it easier to get up and running (it was!)
  3. I had already done the legwork to get the stack running as Docker containers (a proof-of-concept spike to port our app to Elasticbeanstalk). It wasn't a huge amount of additional work.
Getting the app to run in Docker locally was fairly straightforward. You can find a lot of good blog posts and Stack Exchange answers explaining how to set up your Rails dev environment in Docker. However, none really matched my workflow. I like having the Zeus preloader running and one or two terminal windows with Zeus client sessions (one for 'zeus s' server, one for 'zeus c' and 'zeus test' sessions). At least I *did*, until Zeus quit working in our project.  I had begrudgingly switched to Spring, but I dislike that Spring doesn't start everything at once. I'd have to wait for "spring rails s", then wait *again* through a cold boot to run "spring rails test", etc. But that's another story.

But Zeus works fine in a Docker container, and I'm happily using it every day now. I use "zeus start" as the docker-compose command for the rails project, like this (simplified a bit):

# docker-compose.yml
version: '3.1'
services:
  db:
    image: postgres
    ports:
      - "5432"
    volumes:
      - ./setup:/setup
  web:
    stdin_open: true
    tty: true
    build: .
    command: "zeus start"
    volumes:
      - .:/app
    tmpfs:
      - /app/tmp
      - /app/log
    env_file:
      - docker.env
    ports:
      - "3000:3000"
    depends_on:
      - db

A shell alias will make Zeus shell commands issued in my host system run via the container:

alias zeus='docker-compose exec web zeus'

With that, zeus commands are now executed in the Docker container transparently with none of the latency I've grown accustomed to with using Spring.

zeus s
zeus g migration addColumn
zeus c
zeus test spec/controllers

And so on. Emacs rspec-mode will use docker and zeus at once when it assembles the command, so test runs are instantaneous. This was the only config I needed:

(setq rspec-use-docker-when-possible t)
(setq rspec-docker-command "docker-compose exec")

Resulting in:

-*- mode: rspec-compilation; default-directory: "~/Projects/customer_portal/" -*-
RSpec Compilation started at Mon May  7 21:18:58

docker-compose exec web bash -c "zeus rspec --options /app/.rspec /app/spec/models/queue_manager_objects/intention_proxy_spec.rb"

Randomized with seed 9956
..

Development bliss! The only problem I have right now is running Selenium for our integration tests. Those weren't working natively on my Mac before, either -- so not a big change -- but I was hoping that it would be simpler in Docker. I'll create another post when I get them working.