posted on

Rails , Docker , Development , Fig


I've previously posted a short guide on creating a Docker container to run a Rails application. This covered the basics of creating a Dockerfile and starting a container but to use Docker as part of your development environment you need to do a little more work.

Most Rails applications will want at the very least a database to connect to and often a cache store or worker queue. To simplify running all these services in our development environment we can start a container for each service, however managing all these containers by hand and linking them together can quickly become a tedious task, thankfully there are utilities to simplify this task.

Fig is a project from Docker that allows you to define the services required by your application in a YAML file and then manage the entire stack with a few simple commands.

Prerequisites

To follow along with this example you'll need to have Docker installed. If your using OS X or Windows you'll need to install Boot2Docker.

When you have docker/boot2docker install clone the example rails project from Github - https://github.com/invisiblelines/docker-fig-rails-example.git

The repo contains a sample Rails application with Dockerfile. In your terminal change into the example project directory and follow along from there.

Installing Fig

Installing Fig is a single shell command to pull the latest release from Github.

curl -L https://github.com/docker/fig/releases/download/1.0.1/fig-`uname -s`-`uname -m` > /usr/local/bin/fig; chmod +x /usr/local/bin/fig

Alternatively you can install Fig using Pip

sudo pip install -U fig

Defining our Services

To make use of fig we need to create a fig.yml in the root of our application. We'll start small by just adding our application container and a PostgreSQL database.

db:
  image: postgres:9.4
  environment:
    POSTGRES_USER: 'docker'
    POSTGRES_PASSWORD: 'mysupersecretpassword'
app:
  build: .
  volumes:
    - .:/var/www
  ports:
    - "5000:5000"
  links:
    - db
  environment:
    RACK_ENV: development

In the example above we define two services, db and app.

db uses prebuilt postgres image from dockerhub. I've specified the exact tag to use rather than just using the latest tag. I've then specified environment variables for the user to be created.

app uses the Dockerfile in the application root rather than a prebuilt image. It specifies volumes and ports to be mapped, and adds a link to the db container.

Before running the application we need to build the Docker image for our application, this can be done directly with Fig:

fig build app

This step should only need to be repeated if the Dockerfile or Gemfile is changed.

We can now start our development stack with one command:

fig up app

You should now see output in your terminal showing the startup of the postgres server and the application server. Fig will start any services linked to the contaier you launch automatically.

Before going any further we need to create a database for this application. We can do this by running a one off command with Fig in another terminal tab:

fig run app bundle exec rake db:create

If you now visit localhost (or your boot2docker ip) port 5000 in your browser you will see a familar greeting.

Adding Further Services

Now we have our application up and running lets add a Redis container. To do this we just need to edit the fig.yml and add a new service definition:

db:
  image: postgres:9.4.0
  environment:
    POSTGRES_USER: 'docker'
    POSTGRES_PASSWORD: 'mysupersecretpassword'
redis:
  image: redis:2.8.19
app:
  build: .
  volumes:
    - .:/var/www
  ports:
    - "5000:5000"
  links:
    - db
    - redis
  environment:
    RACK_ENV: development

Running Specs/Tests

Now we have the development environment setup its time to start running our tests using the same containers. I like to have Guard watching my code and run specs automatically during development. For this we need another container setup for the test environment. In fig.yml add a service for test

test:
  build: .
  volumes:
    - .:/var/www
  links:
    - db
  environment:
    RACK_ENV: test
  command: bundle exec guard -i -p -l 1

This is almost identical to the app definition however the RACK_ENV has been changed to "test" and the default command has been changed to start Guard.

In another terminal tab build then start the test container

fig build test
fig up test

This container will poll the file system for changes and run the appropriate spec for any modified code.

Cleaning Up

When you swap between projects you'll want to stop the current set of containers from running. You can stop any services start by Fig that are running in the foreground with Ctrl-C. For any other service run fig stop <service name>

To remove any containers any you no longer require run fig rm <service name>