Running Docker in development

By now many of us have had a chance to learn about the benefits of Docker, but one piece of the puzzle is missing before we move it to production. While Cloud 66 and other solutions exist to manage Docker in production, the pain point I've seen so far actually lies in getting comfortable with running it in development.

Sure, it's easy to put a Dockerfile in your repository and use BuildGrid to build the images. But what happens if the build process fails - do you tweak the Dockerfile and redeploy? I've been there before and I'll tell you, it's far from ideal to do this. What if you want to troubleshoot something inside the container once it comes up? Or make a quick code change?

The list goes on and on, eventually making it clear to me that development work belongs locally. This doesn't preclude building the images that you'll use in production - BuildGrid will still do that. But we want to avoid using it for our development workflows.

In this post, we'll go through how to setup your Docker development environment from scratch on Mac OS X, and outline best practices around doing so.

Install boot2docker

Since you can't run Docker natively in OS X, we'll use boot2docker to setup a virtual machine with Docker in it. This may sound daunting, but it's really straightforward.

Simply visit the boot2docker install page, download the latest pkg file and install it. You can now run docker commands in your terminal!

Install Docker Compose

Next we'll want to install Compose (formerly known as Fig), which makes it really easy to spin up multi-container applications from a single file. This is great for development, as you can have your application running alongside your database and cache, all in Docker containers and managed by Compose.

To install it, run the following commands:

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

Using Compose is a three step process:
1. Define your application Dockerfile
2. Define your services in a docker-compose.yml file
3. Run docker-compose up to start the entire application

1. Define your application Dockerfile

Your Dockerfile is a textfile that contains all the commands you'd normally execute manually in order to build a Docker image. We're going to use the awesome lets-chat project for illustrative purposes here - pulling it yourself will help you better follow this guide. They use this Dockerfile:

FROM node:0.10-onbuild  
MAINTAINER SD Elements

ENV LCB_DATABASE_URI mongodb://db/letschat  
EXPOSE 5000  

The FROM directive will pull the library/node image from Docker Hub (tagged 0.10) to be used as a base. The onbuild directive ensures that certain commands in that image only run when it's being used as a base image. Next, the MAINTAINER instruction is simply the author of the file, and ENV sets an environment variable for the container. Finally, EXPOSE informs Docker that the container should listen on port 5000 at runtime.

2. Define your services

Now that our Dockerfile is ready, we're going to switch over to our Compose setup, which will orchestrate the link between this application and its MongoDB datastore dependency.

Let's create a docker-compose.yml file that contains the following (they have a fig.yml file there already, so you can also just rename it):

web:  
  build: .
  links:
    - db:db
  ports:
    - 5000:5000
db:  
  image: mongo:latest

3. Run Docker Compose

At this point we're ready to see some action. Run docker-compose up in your terminal, and watch Compose orchestrate your dependencies - if you don't already have MongoDB, this will be fetched, your application image will be built, and everything will be linked.

Once you see the LETS-CHAT text across your screen, your application is up and running. We just need to know the IP address of our Docker host - run boot2docker ip in your terminal, and you can now visit ip:5000 in your browser to start chatting!