Data Packed Volume Containers: Distribute Configuration

One of the more common questions people ask when we are talking about Docker is, “How you would deploy the same image to both a development and production environment?” The conversation can head in a few different directions from this point, but all roads lead to separation of concerns and some form of configuration injection.

The volume container pattern is well known among Docker users. It is even mentioned in the official documentation on volumes. For the unfamiliar, volume containers are containers that simply act as a handle to a set of volumes. These are used as sources for other containers started with the volumes-from flag which copies volume handles from one container to another. It is common to see people create volume containers that simply echo some statement before stopping because containers maintain their handle after they’ve stopped.

I think this practice stops a bit short. I enhance this pattern in chapter 4 of my book, Docker in Action (use wm122214cc for 50% off until December 25th). What I call data packed volume containers are volume containers that make some static content available for other containers by copying content from their image to declared volumes at startup. Consider the following Dockerfile:

FROM busybox:latest
VOLUMES [“/config”]
ADD . /shippedConfig
CMD cp /shippedConfig/* /config/

You could use this Dockerfile to make whatever configuration you want available to a volume mounted at /config. You could use it to package your dev environment configuration by building an image from this Dockerfile in a directory with dev versions of your configuration and tag the resulting image something like myApp/config:dev. Then make sure that myApp/config:dev is installed on every machine in your development fleet, and a container has been created from it. Lets walk through a brief example.

# setup some dev and production config
mkdir dev
cp Dockerfile ./dev/
echo "This is dev" > ./dev/config.txt
mkdir prod
cp Dockerfile ./prod/
echo "This is prod" > ./prod/config.txt
# build the images
cd dev
docker build -t test/config:dev .
cd ../prod
docker build -t test/config:prod .
# start the packed volume container for dev
docker run --name config test/config:dev

At this point the host has been primed. There is a container named “config” that we know provides the configuration we need. Consumers will not have specific visibility into which environment it is providing configuration for and that is a good thing. Start up a consumer, just to investigate the config.

# consume the configuration 
docker run -—rm -it -—volumes-from config busybox:latest /bin/sh -c ‘cat /config/config.txt’
This is dev

It looks like this host is configured for dev since the contents of the config file say so. You can change this without changing your application by simply creating the config container from the other image.

docker rm -v config
# start the packed volume container for dev
docker run --name config test/config:prod

Now run the exact same consumer container…

# consume the configuration 
docker run -—rm -it -—volumes-from config busybox:latest /bin/sh -c ‘cat /config/config.txt’
This is prod

Boom. A simple decoupled mechanism for distributing your configuration. The real work in this is standardizing on a set of mount points where your application looks for its configuration and a container name (if you want to standardize your launch commands).

For some there won’t be anything new here, but my hope is that this pattern helps to show the kind of flexible structures you can build with the awesome primitives provided by Docker.

If you’re interested learning more the Docker in Action MEAP and all other Manning Publications are available for 50% off until December 25th at manning.com with promo code wm122214cc.