Have you used Docker yet? Docker is awesome! Docker is a tool for managing isolated system environments. I have started using Docker for everything. This is not the first time I’ve written about this delightful tool.
Though the ideas and implementation behind Docker are really cool, the command line syntax is extremely cumbersome. There used to be this great third-party tool to address this problem called fig. But, as of yesterday, it looks like Docker is maintaining fig
and have renamed it to docker-compose
. The impact of this change is minimal on developers - rename fig.yml
files to docker-compose.yml
, use docker-compose up
instead of fig up
. Some great features have been added that I’ve been sorely missing, including the ability to use environment files and only grabbing the latest tagged images from the hub.
Let’s learn you some docker-compose
.
Installation
Two ways: pip install docker-compose
(if you’re into that kind of thing) or through a questionable bash script:
1 2 |
|
You probably want bash completion while you’re at it:
1
|
|
Compose, World
Let’s say I have a simple Go application which only prints the obligatory “Hello, World”:
1 2 3 4 5 6 7 |
|
I want to run this with docker-compose
, so I create the following file:
1 2 3 4 5 6 |
|
This file specifies using the golang:1.4
image as the base image. It maps the our code directory to a directory in the $GOPATH
in the container. It then specifies running the command go run main.go
from the working directory of /go/src/simple-golang-app
. So let’s run it:
1 2 3 4 5 6 |
|
Composing a Database Connection
Now let’s say I want to connect to a database and find some information. The following example connects to a Mongo database, inserts a record, and spits out all the existing records:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
This application has an external dependency on mgo
, which means I’ll need to have that dependency installed somehow. I’ll make a Dockerfile
to live alongside this example:
1 2 3 |
|
Now I’ll construct a yaml file for docker-compose
to consume. This time, we need to build
the main app from our Dockerfile
, add in a second container for the database, add a link
between our application and the database, and specify an environment
variable.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Since mongod
has a tendency to spit up a big boilerplate on initialization, I redirect its output to /dev/null
for this example. Let’s see what happens when we compose:
1 2 3 4 5 6 7 8 |
|
If you squint, you can see the output next to the advanced_1
tag. We printed out one item from our database - the one we just added. What if we run it again?
1 2 3 4 |
|
This output proves that we’re fetching from the database each time in addition to adding new records. Since the Mongo Dockerfile
we used to build the image creates a volume
, we get to see the data persisted.
To achieve the same goal with the Docker CLI, I would need to do this:
1 2 3 4 5 |
|
Keeping that much information at your fingertips all at once absolutely hurts. Making that line reproducible means copy/pasting it into/out of a README somewhere, or running some “single-line script” which only the one guy on the team is allowed to touch. docker-compose
fixes these issues and makes docker
a joy to use.
You can find all of the source code for this blog post on Github: https://github.com/larryprice/docker-compose-example.