engineering thoughts

Docker Links and Runtime Environment Variables

As part of my work at Standard Treasury implementing our continuous integration infrastructure, I ran into a bunch of issues creating ephemeral service instances for test executions. Fortunately for me, the Docker project added links to enable services to expose connection information to each other.

Effectively utilizing these variables in a way that doesn’t tightly couple your application to these ephemeral environment variables is tricky, though. I’ll talk about my strategy for adapting Docker environment variables to application-specific variables in this post.

Linking is a recent feature added to Docker in the 0.6.5 release on October 31. Passing the -link argument to docker run commands will enable a new container to access an existing container’s exposed ports via environment variables.

To link containers, start a container with the -name argument and use the name when starting the second container like this:

docker run -name first -p 5432 -d <container hash>
docker run -name second -link first:db <hash>

When used correctly, the “second” container will have additional environment variables exposed that describe the IP address and port of the mapped container, e.g.:

DB_NAME=first
DB_PORT_5432_TCP_PORT=5432
DB_PORT=tcp://172.17.0.33:5432
DB_PORT_5432_TCP=tcp://172.17.0.33:5432
DB_PORT_5432_TCP_ADDR=172.17.0.33
DB_PORT_5432_TCP_PROTO=tcp

The string after the : value is used as the prefix for the set of environment variables added to the second container.

Using these environment variables is non-trivial; the additional variables may not match up to the values your application is anticipating, so some transformation may be necessary. For example, if your application is expecting SQLALCHEMY_DATABASE_URI to contain the database URI, you’ll need to use the now-exposed DB_PORT_XXX_TCP_ADDR to set SQLALCHEMY_DATABASE_URI.

You might be tempted to try the ENV parameter available for Dockerfile, but this won’t work; the environment variables exposed by -link arguments are only available inside a running docker container, not via the Dockerfile.

To solve this, I’ve opted to use a wrapper script called env.sh in each repo to take variables exposed by linked containers and create new ones that the application expects, exec‘ing the remaining arguments. The Dockerfile for each application sets the ENTRYPOINT as env.sh.

ENTRYPOINT ["/path/to/env.sh"]

Here is the env.sh wrapper script that is used at the ENTRYPOINT for each repo’s container.

Environment variable wrapper script - env.sh

Example Usage

DB_NAME="/$JOB_NAME-$BUILD_NUMBER-db"
DB_CONTAINER=$(sudo docker run -d -name $DB_NAME <private repository URL>/database)
sudo docker run -link $DB_NAME:db -t $JOB_NAME/$BUILD_NUMBER nosetests

In my next post, I’ll describe how this wrapper script is used to expose services in a continuous integration flow.

comments powered by Disqus