Introduction

Tiller is a tool that generates configuration files. It takes a set of templates, fills them in with values from a variety of sources (such as environment variables, Consul, YAML files, JSON from a webservice...), installs them in a specified location and then optionally spawns a child process.

You might find this particularly useful if you're using Docker, as you can ship a set of configuration files for different environments inside one container, and/or easily build "parameterized containers" which users can then configure at runtime.

However, its use is not just limited to Docker; you may also find it useful as a sort of "proxy" that can provide values to application configuration files from a data source that the application does not natively support.

Background and motivation

I had a number of Docker containers that I wanted to run with a slightly different configuration, depending on the environment I was launching them. For example, a web application might connect to a different database in a staging environment, a MongoDB replica set name might be different, or I might want to allocate a different amount of memory to a Java process. This meant my options basically looked like:

  • Maintain multiple containers / Dockerfiles.
  • Maintain the configuration in separate data volumes and use --volumes-from to pull the relevant container in.
  • Bundle the configuration files into one container, and manually specify the CMD or ENTRYPOINT values to pick this up.

None of those really appealed due to duplication, or the complexity of an approach that would necessitate really long docker run commands.

So I knocked up a quick Ruby script (originally called "Runner.rb") that I could use across all my containers, which does the following :

  • Generates configuration files from ERB templates (which can come from a number of sources)
  • Uses values provided from a data source (i.e YAML files) for each environment
  • Copies the generated templates to the correct location and specifies permissions
  • Optionally executes a child process once it's finished (e.g. mongod, nginx, supervisord, etc.)
  • Now provides a pluggable architecture, so you can define additional data or template sources. For example, you can create a DataSource that looks up values from an LDAP store, or a TemplateSource that pulls things from a database.

This way I can keep all my configuration together in the container, and just tell Docker which environment to use when I start it. I can also use it to dynamically alter configuration at runtime ("parameterized containers") by passing in configuration from environment variables, external files, or a datastore such as Consul.

Why "Tiller" ?

Docker-related projects all seem to have shipyard-related names, and this was the first ship-building related term I could find that didn't have an existing gem or project named after it! And a tiller is the thing that steers a boat, so it sounded appropriate for something that generates configuration files.