Using Docker as a Development Environment
JAMES BILLOT | 19TH JULY 2013 | HOW-TO
*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.
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).
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:
- Using a Dockerfile
- 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
# 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
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
# 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
..so in my case:
$ docker run -v /home/jamesbillot/dev/coolgarif-site/output/dev/:/vagrant/ -p 8080 -i -t jamesbillot/nginx /bin/bash
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
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.
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 chat.freenode.net. Particular thanks to keeb for helping me with a networking issue (follow those instructions if docker doesn't want to communicate with the interwebs).