Tiller and Docker environment variables


After a recent query was posted on the Tiller issue tracker, I thought it might be useful to show a complete example of using Tiller and one of it’s plugins to create a dynamic Docker container. I assume you’re at least somewhat familiar with Tiller. If not, take a quick look through my documentation and other related blog posts.

The “TL;DR” pitch for Tiller is that it’s a tool that normally runs as the CMD or EXEC inside a Docker container; it generates dynamic configuration files for your application/services and then runs the application as a replacement process. It has a pluggable architecture, so you can get values for your configuration files from environment variables, a consul cluster, JSON files, and many more.

This example will create a container image that runs a web-server through Tiller, and generates a simple web page populated with environment variables; two from a Docker -e flag, and one from the Dockerfile ENV declaration.

This is obviously a very contrived example as the most common usage of Tiller is to populate configuration files. However, changes to a web page are more easily visualised than twiddling some configuration options! I also thought I’d demonstrate how you can use one of the plugins (EnvironmentDataSource in this case) to fetch the values, instead of the more familiar static values specified in a Tiller environment file.

As an aside : If you’d like to ship a Docker container with some default values, but would like to allow end-users override them with their own configuration, take a look at Building dynamic Docker images with JSON and Tiller and Tiller 0.3.0 and new Defaults data source.

Note : Tiller v0.7.0 and later support a new configuration system, where you can place most configuration blocks in one file, instead of splitting it out over different environment files. To make this example easier to follow, I have also recently updated this walk-through to use the new format.

Dockerfile configuration

So first off, let’s create our Dockerfile that will install NginX, and then setup Tiller to produce a templated web page:

# Dockerfile
FROM ubuntu:latest

ENV color Blue

RUN apt-get -y update && apt-get -y install nginx
RUN apt-get -y install ruby && gem install tiller
RUN echo "daemon off;" >> /etc/nginx/nginx.conf

ADD data/tiller /etc/tiller

CMD ["/usr/local/bin/tiller" , "-v"]

And that’s it. Pretty simple; it just installs NginX, configures it to run under Docker without forking and copies our Tiller configuration in. It also defines an environment variable called “color”. This will be used later. Note that I’m also using the -v argument when calling Tiller; this makes the output more verbose so we can see what’s going on in more detail.

Tiller configuration

Under data/tiller, you’ll find the usual files that get copied to /etc/tiller :

└── tiller
    ├── common.yaml
    └── templates
        └── welcome.erb

Let’s take a quick look at these files.

# common.yaml
exec: [ "/usr/sbin/nginx" ]
data_sources: [ file , environment ]
template_sources: [ file ]



      target: /usr/share/nginx/html/welcome.html

Here, we’re using the “all-in-one-file” configuration format, where we do the following :

  • Line 1 : Pass control over to NginX after we’re done creating files
  • Line 2 : Load the file data source first, and then environment data source to provide values to our templates
  • Line 3 : Load the file template source, so we can load our .erb templates from files
  • Line 5 : Tell Tiller we’re using the “all-in-one-file” configuration format, and our environments are defined below
  • Line 7 : Define a single environment called “production”
  • Line 9 : Define a single template for this environment, the welcome.erb file under the templates/ directory
  • Line 10 : Provide the target value so Tiller knows where to install it

Note that even though we intend to populate the template file only with the values from environment variables, we still also need the file datasource. This is because the environment datasource cannot provide the meta-data such as where to install the templates, or their permissions etc.

So, in summary, this all tells Tiller to process a single template file, and to install it to the default document root for NginX. We’re not providing any values inside a config: block for that template, so all values will come from elsewhere (e.g. environment variables). Here’s the template file :

<!-- templates/welcome.erb -->
<h1>Tiller env_ demonstration</h1>
Hello, <%= env_name %>. <br />
You are running in the <%= env_environment %> environment. <br />
Your favourite color is <%= env_color %>.

You can see we’re using 3 environment variables, and they are all available in lower-case format, and are prefixed by env_. One of them (color) we defined in the Dockerfile, the others will be passed in at runtime.

Running the container

First, build the container and tag it with it’s name (“tiller-docker-example”):

$ docker build -t tiller-docker-example .

And now, let’s run the container, and pass in two variables, environment and name. As well as being referenced in the template, environment is used by Tiller to select which environment to use (although you can omit it, and it will use development by default).

I’ll omit the -d flag, so it keeps running in the foreground so you can see the Tiller output :

$ docker run -e environment=production -e name=Mark \
	-t -i -p 80:80 tiller-docker-example

tiller v0.7.4 (https://github.com/markround/tiller) <[email protected]>
Using common.yaml v2 format configuration file
Using configuration from /etc/tiller
Using plugins from /usr/local/lib/tiller
Using environment production
Template sources loaded [FileTemplateSource]
Data sources loaded [FileDataSource, EnvironmentDataSource]
Available templates : ["welcome.erb"]
Building template welcome.erb
Setting ownership/permissions on /usr/share/nginx/html/welcome.html
Template generation completed
Executing ["/usr/sbin/nginx"]...
Child process forked with PID 7.

And in a web browser, check out the welcome page :

You can stop the container by hitting Ctrl-C:

Caught signal INT, passing to PID 7
Child process finished, Tiller is stopping.

And that’s it! As I stated before, this is a fairly contrived example but you should still be able to see both how the various plugins work, and how you can maintain a single container but still alter it’s runtime configuration through environment variables. See the Github documentation for more examples and details on how to write your own plugins.

If you have any questions or feedback, just use the comments feature on this blog, join the Gitter chatroom or open an issue on the Github project page and I’ll do my best to help!