Setting up a Rails development environment on OS X using Docker
One of our designers wanted to make some changes to our job offer page. At first it seemed trivial - just change some HTML inside a rails app. Then we realised that we would need to setup the whole development environment on his Mac. With the help of homebrew this didn’t sound like a challenge at all. But then it came to my mind that this might be the perfect case for setting up a development environment using docker and docker-compose (formerly fig).
Setting up docker on Mac OS X
Docker does not have native Mac OS X support yet, but there are already tools that allow running docker inside a virtual machine with the feeling of having it installed locally. After struggling with boot2docker, kitematic and vagrant I finally found a setup that just works using docker-machine.
This setup is using homebrew and Virtual Box, but you can install docker binaries in any way you like, as well as use VMWare instead.
Update your homebrew
This may sound obvious, but I’ve spent enough time fighting with older versions of docker and docker-compose that it’s worth mentioning here. Simply run
brew update
Install docker-compose
docker-compose (fig) is a simple utility for running a set of containers defined in a YAML file. It removes the pain of managing separate containers for app, database and supporting services and lets you focus on your app development. Head over to the great documentation for more details. We will use it to define our app dependencies as well as the rails app container itself.
brew install docker-compose
Install docker-machine
docker-machine allows for quick provisioning of docker-ready nodes, from local virtual machines (Virtual Box, VMWare) to cloud servers (EC2, DigitalOcean, Rackspace and more). This is a single point of management for multiple local or remote environments from one terminal.
brew install docker-machine
Validate your setup
After installing all docker packages you should be able to access the following commands. Please pay attention to the version numbers, there is a significant chance that earlier versions of those tools won’t work.
λ docker --version
Docker version 1.6.0, build 4749651
λ docker-compose --version
docker-compose 1.2.0
λ docker-machine --version
docker-machine version 0.2.0 (HEAD)
Getting Virtual Box
Since we want to create a local development environment, we will use Virtual Box. You can download the installed package from Virtual Box Downloads page
Creating a new docker node inside Virtual Box
docker-machine virtual machine drivers are based on boot2docker. Invoking the following command will setup a new Virtual Box VM using boot2docker image.
docker-machine create -d virtualbox docker-vm
You can check the status of your new vm using:
λ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM
docker-vm * virtualbox Running tcp://192.168.99.100:2376
As you can see above, our new machine has an IP address 192.168.99.100
.
This address is required to tell the docker command line tool where the docker daemon is running.
Fortunately, we don’t need to remember it.
Setting up your shell
docker-machine comes with a handy command called env
.
λ docker-machine env docker-vm
export DOCKER_TLS_VERIFY=1
export DOCKER_CERT_PATH="/Users/teamon/.docker/machine/machines/docker-vm"
export DOCKER_HOST=tcp://192.168.99.100:2376
Those ENV
variables tell docker CLI the location of the desired host you want to operate on.
In order to make your life easier, add this line to your .zshrc
/.bashrc
file to have the environment setup correctly in every new terminal:
# .zshrc/.bashrc
eval "$(docker-machine env docker-vm)"
(bonus) Edit /etc/hosts
While this step is not necessary, it might be easier to use an easy-to-remember name instead of an IP address.
Edit your /etc/hosts
file and add this line at the very bottom:
192.168.99.100 docker
NOTE: Your IP address might be different. You can always check it using
docker-machine ip
command
And it’s done — now you have a fully functional docker environment running on OS X!
Easy development of Ruby on Rails apps with docker-compose
Now to the most exciting part.
!!! VERY VERY VERY IMPORTANT !!! You MUST put your application directory under the
/Users
directory. If you don’t, the file sharing between OS X file system and Virtual Box VM will not work. Symlinking won’t work either.
docker-compose requires at least two files to be added to your app’s directory: Dockerfile
and docker-compose.yml
.
Dockerfile
The minimal content of your Dockerfile
should contain something like this:
FROM ruby:2.2.0
RUN apt-get update -qq && apt-get install -y build-essential nodejs
RUN mkdir /app
WORKDIR /tmp
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
RUN bundle install
WORKDIR /app
CMD ["rails", "server", "-b", "0.0.0.0"]
docker-compose.yml
Our app requires two companion services: mysql and redis. We can easily define those using a friendly YAML syntax:
web:
build: .
volumes:
- .:/app
ports:
- "3000:3000"
links:
- db
- redis
db:
image: library/mysql:5.6.22
environment:
MYSQL_ROOT_PASSWORD: password # required by mysql image
redis:
image: redis
MySQL database configuration
We also need to configure the config/database.yml
file to point to our mysql container which is accessible under db
hostname.
development:
adapter: mysql2
encoding: utf8
host: db # <---
database: app_development
username: root
password: password # <--- same as MYSQL_ROOT_PASSWORD above
Redis configuration
Connecting to redis also needs to be tweaked a little - now the redis URL will be redis://redis:6379/0
(using the redis
host).
Up and running
Now we can build our app and start everything together with a single command:
docker-compose up
You should see output similar to foreman’s, with three differently colored log files. And if everything went fine, you should be able to see your app at http://docker:3000 (or http://192.168.99.100:3000 if you haven’t edited /etc/hosts
)
Tips & tricks
It might be useful to have a separate database.docker.yml
file and a bin/docker-setup
script to set everything up automatically after a fresh cloning of the repository.
# bin/docker-setup
#!/bin/bash
set -e
cp -f config/database.docker.yml config/database.yml
cp -f config/application.docker.yml config/application.yml
# build app image
docker-compose build
# start mysql and redis in background
docker-compose start db
docker-compose start redis
# setup database
docker-compose run web rake db:create db:migrate
# ensure all containers are up and running
docker-compose up
The result
I can’t think of a better summary than this quote from one of our designers Paweł:
Design can seldomly by validated without actually looking at what you made. And when what you made is code, it’s getting harder by the minute. One can deploy after every little change which is counter productive or wait until those changes accumulate which isn’t fast. Docker helps with that. I’m happy now.
Looking for comments section?
Send me an email instead to [email protected]