Puppet is an industrial-strength cross-platform configuration management engine. Though you’ll find lots of existing Puppetmaster images on the Docker registry, this one will serve as the baseline for other expanded uses of Puppet – such as using it with Munki and SSL client certificates.
This is a walkthrough for building a Puppetmaster Docker container.
Building Puppetmaster into a Docker container:
Let’s start with our Dockerfile:
FROM centos:centos6 MAINTAINER firstname.lastname@example.org ENV PUPPET_VERSION 3.7.3 RUN rpm --import https://yum.puppetlabs.com/RPM-GPG-KEY-puppetlabs && rpm -ivh http://yum.puppetlabs.com/puppetlabs-release-el-6.noarch.rpm RUN yum install -y yum-utils && yum-config-manager --enable centosplus >& /dev/null RUN yum install -y puppet-$PUPPET_VERSION RUN yum install -y puppet-server-$PUPPET_VERSION RUN yum clean all ADD puppet.conf /etc/puppet/puppet.conf VOLUME ["/opt/puppet"] RUN cp -rf /etc/puppet/* /opt/puppet/ VOLUME ["/opt/varpuppet/lib/puppet"] RUN cp -rf /var/lib/puppet/* /opt/varpuppet/lib/puppet/ EXPOSE 8140 ENTRYPOINT [ "/usr/bin/puppet", "master", "--no-daemonize", "--verbose" ]
This is based on a CentOS 6 container, for no other reason than that I’m already familiar with CentOS 6. I could just as easily have used Ubuntu or Debian or any of the other base image variants. I chose CentOS 6 over 7 due to major changes in 7 (such as the inclusion of systemd) that I’m not quite familiar with.
ENV PUPPET_VERSION 3.7.3
This makes it easy for me to enforce specific versioning of Puppet in my Dockerfile. As of writing time, Open-source Puppet is on 3.7.3, so that’s what we’ll install. It also means that future builds of this Dockerfile won’t suddenly change or update Puppet without specific admin intervention. It also means that we can easily update the Puppet version by only making one change in the Dockerfile.
RUN rpm --import https://yum.puppetlabs.com/RPM-GPG-KEY-puppetlabs && rpm -ivh http://yum.puppetlabs.com/puppetlabs-release-el-6.noarch.rpm
As per Puppet’s installation instructions, we need to add the GPG key for Puppetlab’s yum repo, and then install the yum repo to get the updated Puppet from.
RUN yum install -y yum-utils && yum-config-manager --enable centosplus >& /dev/null RUN yum install -y puppet-$PUPPET_VERSION RUN yum install -y puppet-server-$PUPPET_VERSION RUN yum clean all
Install the yum utils, install puppet, install the puppet server, clean up after ourselves.
ADD puppet.conf /etc/puppet/puppet.conf
Add in the Puppet configuration file. That will be explored later.
RUN cp -rf /etc/puppet/* /opt/puppet/
RUN cp -rf /var/lib/puppet/* /opt/varpuppet/lib/puppet/
Here, we expose two volumes for sharing –
/opt/varpuppet/lib/puppet/. These are important, as we’ll configure Puppet to use these for configuration and dynamic data in puppet.conf. That way, we can share this out to data-only containers so we don’t lose anything if we ever remove the Puppetmaster container.
In addition to creating those two directories, we’re also copying the contents from the default directories for Puppet (
/var/lib/puppet/) into our new alternate directories, so they’ll be pre-populated.
Puppet runs on port 8140, so we need that port available to the outside world.
ENTRYPOINT [ "/usr/bin/puppet", "master", "--no-daemonize", "--verbose" ]
The entrypoint starts Puppet in master mode, with verbose logging, as the primary process in the container. This is what allows us to run the container in detached mode, and easily check the logs.
The Puppet Configuration file:
[agent] certname = puppetmaster pluginsync = true [master] certname = puppet confdir = /opt/puppet vardir = /opt/varpuppet/lib/puppet basemodulepath = $confdir/site-modules:$confdir/modules:/usr/share/puppet/modules factpath = $confdir/facts:/var/lib/puppet/lib/facter:/var/lib/puppet/facts autosign = true hiera_config = $confdir/hiera.yaml rest_authconfig = $confdir/auth.conf ssldir = $vardir/ssl csr_attributes = $confdir/csr_attributes.yaml
The important things about the Puppet.conf to notice:
vardirare set to custom directories located in /opt/. We shared these volumes earlier with the VOLUME directives in the Dockerfile, so that means this data will exist in a shareable form that can be linked to other Docker containers.
autosignis true. That means all client certificates will be automatically signed on request. This makes a fine default for testing, but for production use, we’ll want to change this.
csr_attributesis set to a file called “csr_attributes.yaml” which exists in in
/opt/puppet/. This isn’t necessary for this particular demo, but it’ll play a part in the next iteration of our Puppet docker container.
You can build this container yourself (if you git clone the project) using:
docker build -t name/puppetmaster .
or you can pull the automated build from the Docker registry:
docker pull macadmins/puppetmaster
To use this container:
As always, we want a data-only container to keep all of Puppet’s configuration and dynamic data in. This is especially important as we need to preserve the Puppet certificates so that they’re not lost if the Puppetmaster container is removed:
docker run -d --name puppet-data --entrypoint /bin/echo macadmins/puppetmaster Data-only container for puppetmaster
Now run the container:
docker run -d --name puppetmaster -h puppet -p 8140:8140 --volumes-from puppet-data macadmins/puppetmaster
Here, we’ve set the hostname to “puppet.” The puppet agent will always try to reach the puppet master via DNS at “puppet”, so we’ll need to make that happen in DNS. As explained earlier, we’re mapping port 8140 to the localhost at 8140.
There’s a critical step that needs to happen next:
docker exec puppetmaster cp -Rf /etc/puppet /opt/
We need to populate the Puppet configuration directory with all the of the content in /etc/puppet/. Because of the order in which puppet.conf is read, /etc/puppet is populated with the default Puppet setup before it discovers that there’s a new confdir directive. Thus, /opt/puppet, while being used as the confdir for all Puppet configurations, does not start out with content in it. We need to fix that manually.
Puppet has started, let’s see a list of certs:
docker exec puppetmaster puppet cert list -all
Only one certificate exists so far – the puppetmaster itself.
Puppetizing a client:
I did all this on an OS X 10.10.1 VM, but this will work on any client.
- Install Puppet, Hiera, and Facter onto a client.
Add the IP of your Docker host to /etc/hosts (or configure DNS so that your Docker host is reachable at “puppet”). For example, if your Docker host IP is 10.0.0.1:
Test puppet on client running as root:
# puppet agent --test
You should see the certificate request being generated and autosigned.
Verify cert signing on puppetmaster docker container:
docker exec puppetmaster puppet cert list -all
You should see the certificate for the client’s hostname in the list.
On the client, run:
# puppet agent --testagain to verify that cert exists and was confirmed.
We’ve got a working Puppet master in a Docker container! This will make a baseline Docker image we can expand upon.