Puppet loves Docker


One of the great things released this year at PuppetConf was Puppet’s image build The thing that I really love about this project is, it make your journey into Docker super easy if you are already experienced with Puppet. This holds a special spot in my heart to learn Docker this way, as it is the way I learnt Docker. Puppet’s image build allows you to take your existing puppet code and create containers from it, so any work you have already done in Puppet will port right across to Docker. You might be asking the question “Why would I do that there is already Dockerfiles ?” I don’t think this project is meant to replace the Dockerfile, in fact it uses a Dockerfile in its back end. This project is meant compliment the Dockerfile. The other use case for this project is all the people out there that have repos full of Puppet code and are looking to start using Docker. This makes the uptake super easy as you have already done all the hard work. So let’s see how it works. I am going to take a module that I have written and open sourced to the Puppet forge. The module I will use is my Golang module

Before we can start let’s install Puppet’s image build. Now we are going to assume that you have Puppet installed. If you don’t please follow the offical docs To install the module run the following command in your terminal puppet module install puppetlabs/image_build. Now to check our install went well, we can issue the following command puppet docker build --help and will get the following:

USAGE: puppet docker <action> [--from STRING]
[--maintainer STRING]
[--os STRING]
[--os-version STRING]
[--puppet-agent-version STRING]
[--r10k-version STRING]
[--expose STRING]
[--cmd STRING]
[--entrypoint STRING]
[--labels KEY=VALUE]
[--hiera-config STRING]
[--hiera-data STRING]
[--puppetfile STRING]
[--image-name STRING]
[--config-file STRING]
[--config-directory STRING]
[--master STRING]

Build Docker images and Dockerfiles using Puppet code

  --render-as FORMAT             - The rendering format to use.
  --verbose                      - Whether to log verbosely.
  --debug                        - Whether to log debug information.
  --cmd STRING                   - The default command to be executed by the
                                   resulting image
  --config-directory STRING      - A folder where metadata can be loaded from
  --config-file STRING           - A configuration file with all the metadata
  --entrypoint STRING            - The default entrypoint for the resulting
  --expose STRING                - A list of ports to be exposed by the
                                   resulting image
  --from STRING                  - The base docker image to use for the
                                   resulting image
  --hiera-config STRING          - Hiera config file to use
  --hiera-data STRING            - Hieradata directory to use
  --image-name STRING            - The name of the resulting image
  --[no-]inventory               - Enable or disable the generation of an
                                   inventory file at /inventory.json
  --labels KEY=VALUE             - A set of labels to be applied to the
                                   resulting image
  --maintainer STRING            - Name and email address for the maintainer of
                                   the resulting image
  --master STRING                - A Puppet Master to use for building images
  --os STRING                    - The operating system used by the image if not
  --os-version STRING            - The version of the operating system used by
                                   the image if not autodetected
  --puppet-agent-version STRING  - Version of the Puppet Agent package to
  --puppetfile STRING            - Enable use of Puppetfile to install
                                   dependencies during build
  --r10k-version STRING          - Version of R10k to use for installing modules
                                   from Puppetfile
  --rocker                       - Use Rocker as the build tool

  build         Build a Docker image from Puppet code
  dockerfile    Generate a Dockerfile which will run the specified Puppet code

See 'puppet man docker' or 'man puppet-docker' for full help.

Now lets create the plumbing for our Docker image. Firstly we will create the base directory called golang inside that folder we will create three things. Firstly we will create a file called Puppetfile, then metadata.yaml and lastly a manifests directory. In the Puppetfile we will add the modules that we need to build the Docker image, in this case it will be the following.

forge 'https://forgeapi.puppetlabs.com'

mod 'scottyc/golang'
mod 'puppetlabs/stdlib'
mod 'maestrodev/wget'

In the metadata.yaml we will define the metadata about the image. This is one thing that I really love, it allows you to set things that you usually would pass via the command line. The beauty of this is that you can add this file to source control. Giving you you an audit trail of the images metadata through its lifecycle. In this example we are just going to set two values. The fist one the image name (in this case we will use the puppet namespace for demo purposes) and the second an env setting to add the Golang binary to the path. The code looks like

image_name: puppet/golang
env: PATH=/usr/local/go/bin:$PATH

Now in the manifests folder we will create a file called init.pp and we will add a small bit of Puppet code that will just declare some values from the Golang module

class {'golang':
  base_dir     => '/usr/local/go',
  from_repo    => true,
  repo_version => 'go1.7',
  goroot       => '$GOPATH/bin:/usr/local/go/bin:$PATH',
  workdir      => '/usr/local/',

Now this could be even more simple if you wanted to take the module defaults. You could just have include golang in init.pp file. Thats it all the coding is done.
Let’s build our image with puppet docker build from the root of the golang folder that we created earlier.

Once the image is built we can issue a docker images and get

REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
puppet/golang        latest              55345520d00b        3 hours ago         672 MB

Now lets take the image for a test drive and run a container with docker run -it --name golang puppet/golang bash and we get the containers shell. From there just type go to make sure the install is good.

root@4714a89e18df:/# go
Go is a tool for managing Go source code.


        go command [arguments]

The commands are:

        build       compile packages and dependencies
        clean       remove object files
        doc         show documentation for package or symbol
        env         print Go environment information
        fix         run go tool fix on packages
        fmt         run gofmt on package sources
        generate    generate Go files by processing source
        get         download and install packages and dependencies
        install     compile and install packages and dependencies
        list        list packages
        run         compile and run Go program
        test        test packages
        tool        run specified go tool
        version     print Go version
        vet         run go tool vet on packages

Use "go help [command]" for more information about a command.

Additional help topics:

        c           calling between Go and C
        buildmode   description of build modes
        filetype    file types
        gopath      GOPATH environment variable
        environment environment variables
        importpath  import path syntax
        packages    description of package lists
        testflag    description of testing flags
        testfunc    description of testing functions

Use "go help [topic]" for more information about that topic.

There we go (pun intended). So there are some downsides to this method as you can see the image size is big, and there is already a container in the Docker Hub library for Go. But imagine other applications that are already in Puppet code that have not yet been ported to containers, this could be some that your business uses internally or any number of custom applications. I think that is where the benefit kicks in, the development hours you have already clocked up can be ported in minutes and you have a containerised image of your Puppetised application making your journey to Docker a quick and painless task.


Linux geek, Docker Captain and Retro Gamer

Leave a Reply

Your email address will not be published. Required fields are marked *

15 + two =