Using Docker as a Development Environment

*Updated for Docker v0.5.0*

I currently use Vagrant in development to provide me with an environment that closely mimics production - as well as all the good things that fall-out of sandboxing/starting from a known-point/minimalism, etc.

Docker is a 'Linux Container Engine' - think: Virtual Machine, but.. smaller, faster, practically zero startup/shutdown time. That, plus the fact that it is new and shiny, convinced me to try it out as a Vagrant replacement in my development workflow.

Docker logo

The goals are:

  • Docker image closely matching production setup

  • Mounting of development dir on my local machine into docker image - so that any changes I make there are instantly reflected inside docker

  • Minimum of additional 'faff' when working

Here is a quick guide to getting a toy Nginx stack up-and-running for use when developing a simple static site (more complicated set-ups are easily possible).

Install Docker

I run Ubuntu 13.04, so was able to install docker fairly easily:

$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:dotcloud/lxc-docker
$ sudo apt-get update
$ sudo apt-get install lxc-docker

If you're on a Mac or Windows, there's more detailed instructions for your situation available on the docker site.

Create An Image

Pull a Base Image

If it's your first time running docker then you need to pull a base image to use as the base for all of your future images. This might take a little while depending upon your internet connection, but, is the longest you'll ever be waiting when using docker:

$ docker pull base

Once it's finished downloading, you can check that everything's working by running a quick shell:

$ docker run -i -t base /bin/bash

Build Your Stack

Now you're ready to start putting together your own build containing everything that you need for your dev environment. You can do this in a couple of ways:

  1. Using a Dockerfile

  2. Installing in an interactive shell and taking 'snapshots'

#1 is the more repeatable method, but, for simplicity - and a love of noddling around - I'm going to show you #2. This will be just like manually installing things via the terminal on a new box, or your local machine.

Fire up a base image with interactive shell:

$ docker run -i -t base /bin/bash

Now inside the docker image shell, we'll install a plain Nginx version via apt-get:

# necessary to add the ppa
$ sudo apt-get install python-software-properties
$ sudo apt-get install software-properties-common

# add the ppa
$ sudo add-apt-repository ppa:nginx/stable

# update the repos
$ sudo apt-get update

# install nginx
$ sudo apt-get install nginx

When apt-get has finished running, you can check that things are successful by running:

$ service nginx status

Nginx shouldn't be running, but, you should see instructions for use that suggests that it is at least present.

At this point we want to take a snapshot:

Take a Snapshot

Open a new terminal on your local machine and run:

$ docker ps

You should see something like the following - we want to grab the hex number listed under ID:

# output of docker ps
>> ID            IMAGE  COMMAND     CREATED         STATUS
>> 8b44da996d5d  base   /bin/bash   44 minutes ago  Up 44 minutes

This is the ID of your currently running container and what we need to pass to docker in order to create our snapshot. Making a snapshot takes the form:

$ docker commit [ID] [image name]

So.. for example:

$ docker commit 8b44da996d5d jamesbillot/nginx

Convention(?) calls for you to name your image in the form '[user]/[image name]'. In order to see your new image, run:

$ docker images

..and there, nestled in the list, should be your newly-named image.

Having this image means that you can fire-up a docker container instance at any time, using the named image, and have things be exactly as they are when you took the snapshot, ie. with a nice, fresh install of Nginx and that's it.

Kill the currently running docker container instance by running exit at the shell-prompt (running docker ps again should confirm that the container that was running is no longer listed).

Now you can use this image in your development with your dev directory mounted within.

Getting Things Running

These next steps assume that you have a development directory containing some form of static site that you're currently working on, plus an nginx.conf file for serving this site.

*Updated for v0.5.0* The command to get docker running with your dev directory mounted takes the form of:

$ docker run -v [/path/to/dir/to/mount/on/local/machine/]:[/desired/path/in/docker/] -p [port to forward] -i -t [name of image] /bin/bash in my case:

$ docker run -v /home/jamesbillot/dev/coolgarif-site/output/dev/:/vagrant/ -p 8080 -i -t jamesbillot/nginx /bin/bash

Next Steps

Update Nginx.conf

My dev docker image has the default nginx.conf, so I need to swap it out for whichever nginx.conf is relevant to the project that I'm working on. I do this manually, but, I'm sure this would be quite straight-forward to automate.

# change dir to the folder containing the current nginx.conf file
$ cd /path/to/current/nginx/conf/

# rename the current nginx.conf file
$ mv nginx.conf nginx.conf.old

# symlink your new nginx.conf file into the dir
$ ln -s /path/to/new/nginx.conf nginx.conf

Start Nginx

Docker runs a single primary process - in this case it's running /bin/bash (the terminal you're interacting with). So Nginx won't be running.

# start nginx
$ service nginx start

Check the Site in Your Browser

To find out what port on your machine is forwarding to the Nginx port (in my case 8080) in docker, run the following in a new terminal on your local machine:

$ docker ps
>>ID           IMAGE   COMMAND   CREATED            STATUS            PORTS
>>c207e232d8b9 foo/bar /bin/bash About an hour ago  Up About an hour  49153->8080

Looking at the last item in the list, we can see that port 49153 is forwarding to port 8080 in docker. Using this info, point your browser at: http://localhost:49153 and you should see your site being served.

Make a Change, Marvel at the Results

Edit one of the .html files on your local machine, hit save, refresh your browser - BOOM!Cooking on gas.

Other Tips

Docker is still in its formative stages and things are likely to change often. I found that the docs were good, but, missing in parts. I found that going through the GitHub Issues for docker and the Docker Club Google Group helped clarify elements that are undocumented/changing fast. Stackoverflow for docker is OK, but, needs more content - so if you've got a question, ask away on there.

I also found that people were very helpful on IRC: #docker on Particular thanks to keeb for helping me with a networking issue (follow those instructions if docker doesn't want to communicate with the interwebs).