Building Docker Images With Puppet

At PuppetConf 2016, we shipped the first release of the image_build module. As its name suggests, this module provides a simple way of taking Puppet code and building images, including Docker images. The module is available for everyone today, and the source code is up on GitHub.

Now, you can use Puppet to solve two problems with large Docker workflows:

  1. You have a Puppet code base and you're moving some of your services into containers. By sharing the same code across your infrastructure, regardless of whether or not it uses containers, you can cut down on duplication of effort, and take advantage of work you've already done.
  2. You're building many images, but scaling Dockerfile involves either a complex hierarchy of images or copying and pasting snippets between many individual Dockerfiles. The image_build Puppet module lets you share common functionality using the familiar Puppet modules mechanism, and Puppet itself provides a rich domain-specific language for a declarative composition of images.

I spoke at DockerCon in Seattle this year about the Dockerfile explosion and the need for higher-level tools and this functionality continues that conversation. It’s not about a general-purpose solution in the vein of Dockerfile; it specifically addresses the needs of existing Puppet users, as well as those new to Puppet working in large, messy enterprise organizations.

Installation

Packaged as a Puppet module, image_build is available on the Forge. You can install it with the usual tools, including the puppet module command:

puppet module install puppetlabs/image_build

You also need to install Docker, for which I'd recommend Docker for Mac or Docker for Windows. Or, you can just install Docker from packages if you're on Linux.

After installing the module, you can use some new Puppet commands, including puppet docker, which in turn has two subcommands: one triggers a build of an image, while the other outputs the intermediary Dockerfile. The examples directory contains a set of examples for experimenting with. Let’s look at one of those now.

Hello World

We'll create a Docker image running Nginx and serving a simple text file. This is a realistic but obviously simplistic example; it could be any application, such as a custom Java or Ruby or other application.

First, let’s use a few Puppet modules from the Forge. We'll use the existing Nginx module and specify its dependencies. We'll also use the dummy_service module to ignore service resources in the Nginx module. We do this by creating a standard Puppetfile.

$ cat Puppetfile
forge 'https://forgeapi.puppetlabs.com'

mod 'puppet/nginx'
mod 'puppetlabs/stdlib'
mod 'puppetlabs/concat'
mod 'puppetlabs/apt'
mod 'puppetlabs/dummy_service'

Next, let’s write a simple manifest. Disabling Nginx daemon mode isn't supported by the module just yet (but the folks maintaining the module have just merged this capability), so let’s drop a file in place with an exec. Have a look at manifests/init.pp:

include 'dummy_service'

class { 'nginx': }

nginx::resource::vhost { 'default':
  www_root => '/var/www/html',
}

file { '/var/www/html/index.html':
  ensure  => present,
  content => 'Hello Puppet and Docker',
}

exec { 'Disable Nginx daemon mode':
  path    => '/bin',
  command => 'echo "daemon off;" >> /etc/nginx/nginx.conf',
  unless  => 'grep "daemon off" /etc/nginx/nginx.conf',
}

Let’s also provide some metadata for the image we intend to build. Take a look at metadata.yaml. (it’s worth noting that this is the only new bit so far).

cmd: nginx
expose: 80
image_name: puppet/nginx

Finally, we can build a Docker image with the following command:

puppet docker build

That’s it. You should see the build output and a new image being saved locally. We’ve aimed for a user experience that’s at least as simple as running docker build.

Let’s run the resulting image and confirm it’s serving the content we added. We expose the web server on port 8080 to the local host to make that easier.

$ docker run -d -p 8080:80 puppet/nginx
83d5fbe370e84d424c71c1c038ad1f5892fec579d28b9905cd1e379f9b89e36d
$ curl http://0.0.0.0:8080
Hello Puppet and Docker%

The image could be run via the Puppet Docker module or atop any of the container schedulers.

The development of image_build benefitted greatly from conversations with several of our larger customers, and I wanted to thank everyone who spoke to us and provided feedback. Thanks in particular to Jason Meltzer, director of technology at Fannie Mae, who nicely summarized what we’re aiming to accomplish with this work:

We’re excited about this announcement because it makes it possible to move a service from traditional infrastructure to a containerized environment with the tools we use today to manage everything from databases, to app servers, to services running in containers.

Next Steps

The full README contains more examples, including how to use Hiera with image_build, building multiple images from a single manifest and how to use a Puppet master in place of local Puppet manifests.

This is the first release of image_build, and we have lots more ideas for improvements and more fleshed-out workflows for using this with other tools in the Puppet and container ecosystems. Keep an eye on this blog for more posts, and if you have any ideas or thoughts, please let us know in the comments below.

 

 

 

 

Top