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
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
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.
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.
Under data/tiller, you’ll find the usual files that get copied to /etc/tiller :
etc └── 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 ] environments: production: welcome.erb: 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.erbfile under the
- Line 10 : Provide the
targetvalue 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,
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:
^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!