Building dynamic Docker images with JSON and Tiller 0.1.4

Updated:

Note : This page may contain outdated information and/or broken links; some of the formatting may be mangled due to the many different code-bases this site has been through in over 20 years; my opinions may have changed etc. etc.

Tiller 0.1.4 has just been released, and brings a few new improvements. Firstly, you can now use -b,-l and -e command-line flags to set the tiller_base, tiller_lib, and enviroment values respectively. This makes things a little neater when debugging or testing new configurations on the command line.

I also added a environment_json data source, based on idea by Florent Valdelievre (thanks for the feedback, Florent!)

This means you can now pass in arbitrary JSON data through the tiller_json environment variable, and use the resulting structure in your templates. As it merges values with the rest of the global values from other data sources, you can also use it to over-ride a default setting in your environment files; this may be particularly useful if you build Docker containers that are provided to end-users who wish to customise their settings.

To illustrate this, here are a few quick examples, also showing the new command-line flags when developing your templates.

Note: Please note that although this feature was added in Tiller 0.1.4, I’m using 0.6.5 in these examples as it includes the defaults module, newline suppression for ERb templates (the -%> syntax you’ll see below) which makes templates with loop constructs much neater, and lots of other assorted bug fixes.

Note 2: 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. However, this article still refers to the old “one file per environment” approach as it’s still supported and won’t be removed. Check out this blog post to see an updated example in the new format, using this post as a reference.

Note 3: Tiller v0.7.6 and later allow a slightly modified form of the JSON structure, which lets you specify global AND local values, which may be useful when over-riding template defaults. See http://www.markround.com/blog/2016/04/25/tiller-0-dot-7-6-and-environment-json-v2/

Firstly, install the Tiller gem, create a work directory with the usual Tiller structure and change into it :

$ gem install tiller
$ mkdir json_example
$ cd json_example
$ mkdir templates environments

Then, create your common.yaml which enables the relevant data & template sources :

data_sources:
  - defaults
  - file
  - environment_json
template_sources:
  - file

Create your environment file (environments/development.yaml) :

json_template.erb:
  target: parsed_template.txt

And a defaults file containing a global value (defaults.yaml) :

global:
  default_value: 'This is a default global value that may be replaced'

And your template (templates/json_template.erb) :

Default value : <%= default_value %>
List of servers follows...
<% servers.each do |server| -%>
	Server : <%= server %>
<% end -%>

Now, run Tiller and pass in your JSON as an environment variable (you can add the -v flag to tiller to make the output more verbose) :

$ tiller_json='{ "servers" : [ "server1" , "server2" , "server3" ] }' tiller -b $PWD -n
tiller v0.6.5 (https://github.com/markround/tiller) <[email protected]>
Template generation completed

$ cat parsed_template.txt
Default value : This is a default global value that may be replaced
List of servers follows...

  server1
  server2
  server3

As mentioned above, you can also use this to over-ride a default. Notice that Tiller will warn you of this :

$ export tiller_json='{ "default_value" : "Hello, World!" , "servers" : [ "server1" ] }' 
$ tiller -b $PWD -n
tiller v0.6.5 (https://github.com/markround/tiller) <[email protected]>
Warning, merging duplicate global values.
default_value => 'This is a default global value that may be replaced' being replaced by : 'Hello, World!' from EnvironmentJsonDataSource
Template generation completed

$ cat parsed_template.txt
Default value : Hello, World!
List of servers follows...

  server1

More complicated structures can easily be built up. However, these can be quite error prone to pass “on the fly”, so instead create a file called config.json with the following contents :

{
  "servers" : [
      {
        "hostname" : "server1",
        "port" : "80"
      },
      {
        "hostname" : "server2",
        "port" : "8080"
      }
    ],
  "username" : "mark",
  "password" : "tiller"
}

This is much easier to read and check for syntax errors! This example contains a list of server configuration hashes, and a couple of simple key:value pairs.

Edit your template templates/json_template.erb as follows :

Username : <%= username %>
Password : <%= password %>
List of servers follows...
<% servers.each do |server|  -%>
  http://<%= server['hostname'] %>:<%= server['port'] %> 
<% end -%>

Now you get the following produced :

$ tiller_json="$(cat config.json)" tiller -n -b $PWD
$ cat parsed_template.txt

Username : mark
Password : tiller
List of servers follows...

  http://server1:80
  http://server2:8080

Assuming you use Tiller as your Docker CMDor ENTRYPOINT command and set up a suitable exec in your common.yaml, you can now include complex configuration in your Docker containers at runtime simply by doing something like :

$ docker run -e tiller_json="$(cat config.json)" \
  -d -t -i ...rest of arguments...

Hope that’s of some help! If you have any feedback, just use the comments feature on this blog and I’ll reply ASAP. You may also want to check out some of my other articles on Tiller, particularly the walkthrough with a sample Dockerfile, and how to use the API to query a running Docker container.