This is the last part of my ongoing blog series on how to use Docker to create a local Puppet development stack. If you've followed all the previous steps you have hopefully learned a lot by now and are running a fully operational dockerized Puppet stack consisting of a Puppetmaster, The Foreman and PuppetDB.

In this blog post we will take a look at how to configure and use these components to provision a new docker container.

Configuring Foreman

First we need to configure Foreman so that it may fulfill its role as ENC and management tool for Puppet.

1. Open your browser and go to: https://foreman.localdomain

2. You are greeted with a login screen after you've accepted the self-signed certificate. The default username is 'admin'. You have to generate a new password to be able to login. In my repository there is a script that resets the Foreman password. It runs the following command: docker exec -t foreman foreman-rake permissions:reset. The generated password will be visible in your terminal. Copy it to the clipboard and use it to login to The Foreman.

3. If yo are logged in you need to configure the idle timeout first. This is a loose end that I need to fix but haven't been able to figure out yet. Foreman is unfortunately configured with an idle timeout of zero seconds by default. This means that you will be logged out on every page refresh. We have to increase the timeout to prevent this from happening (you will have to login three times before the setting is adjusted permanently, sorry!).

Go to the settings:

Foreman Settings

Click on the 'General' Tab and enter '60' at the idle_timeout:
General Settings

Save your settings!

4. Now we have to configure a smart-proxy for The Foreman so that way can manage the Puppetmaster.

Go to Infrastructure - Smart Proxies:

Create a new smart-proxy and enter the following details:

Click submit to save the settings.

Configuring Puppet

Before we can import Puppet classes and environments into Foreman we need to add some environment directories and modules. There is an environments directory in the repository that is used by the Puppetmaster to look for environments and modules. It installs a production environment by default.

1. Create common and development directories. Common will contain community modules that are used by all other environments. Development is for modules that are in development and not yet stable enough for production.

cd environments  
mkdir -p common/modules  
mkdir -p development/modules  

2. Install some modules (I'm using Git but if you've installed Puppet you can use the puppet module command which is more convenient)

cd environments/common/modules  
git clone --branch 4.4.0 stdlib  
git clone --branch 1.2.0 apache  
git clone --branch 3.0.0 mysql  
git clone --branch 1.1.2 concat  

3. Import the modules into The Foreman.

Go to Configure - Puppet - Environments:

Click 'Import from Puppetmaster'. This is your smart-proxy on the Puppetmaster.

The first time you might get an error:

ERF12-2749 [ProxyAPI::ProxyException]: Unable to get environments from Puppet ([RestClient::NotAcceptable]: 406 Not Acceptable) for proxy https://puppetmaster.localdomain:8443/puppet

Just reload the page and it will work. Hopefully someone can tell me why this is happening. After some time you can select which environments and classes to import:

Check all environments and click 'update'.

Running Puppet

We are now at the stage where we can reap the benefits of all our hard work. Let's provision a new container with our imported Puppet classes.

1. Create and run a new container

docker run --dns -h dummy.localdomain -it centos:centos6 bash  

With this command we are now in a bash shell inside the container. The hostname will be 'dummy.localdomain' and we've set the DNS resolver to our Docker bridge on which Docker-spy is listening. I've used a clean CentOS 6 base image. If you start from a base image with Puppet pre-installed you can skip te next step.

2. Install Puppet and Connect to the Puppetmaster

rpm --import  
rpm --import  
rpm -ivh  
rpm -ivh

yum -y install puppet-3.6.2-1.el6

puppet agent --test --server puppetmaster.localdomain  

After running the Puppet command you should see the following lines:

Info: Creating a new SSL key for dummy.localdomain  
Info: Caching certificate for ca  
Info: csr_attributes file loading from /etc/puppet/csr_attributes.yaml  
Info: Creating a new SSL certificate request for dummy.localdomain  
Info: Certificate Request fingerprint (SHA256): 04:BA:A0:44:D3:B2:3D:7A:3C:DA:5C:2D:8C:90:16:7C:D1:C2:C9:51:4A:29:CE:DA:FE:7D:F4:F1:AC:4B:E6:78  
Info: Caching certificate for ca  
Exiting; no certificate found and waitforcert is disabled  

This means that the certificate needs to be signed on the Puppetmaster before the agent can connect. Let's do that in The Foreman:

3. Go to: infrastructure - Smart Proxies. Click on the certificates button. You should see the dummy.localdomain certificate request that needs to be signed:
Click on the sign button to authorize the new node. If you want to make Puppetmaster sign requests automatically you can click on the Autosign Entries and add a wildcard for agents that may connect without manual approval.

4. In the dummy container rerun the Puppet agent command to register the node with The Foreman.

puppet agent --test --server puppetmaster.localdomain  

You should see a lot of output and a message that the run was successful. This node is now registered in Foreman and can be configured with the Puppet classes. Let's install Apache, MySQL and our dummy module on this node.

5. Go to: Hosts - All Hosts You should see our new dummy node listed:
Click on the edit button to configure our new node:
Select the development environment for this node and switch to the Puppet Class tab:
From the available classes select apache and mysql::server to be included on this node. Hit the submit button to save.

5. In the dummy container rerun the Puppet agent to provision the container.

puppet agent --test --server puppetmaster.localdomain  

You should see a lot of output from the Puppet agent while it installs Apache and MySQL on the container. When the run ends you should be able to run the following commands:

curl localhost  

This should retrieve the default Apache page and connect to the MySQL server. You can now add more classes and docker containers to rapidly test and iterate with your Puppet infrastructure. Success!


It's been a long journey and we've touched upon a lot of cool new technologies. I hope you have enjoyed this blog series. Please leave a comment if you have any remarks or suggestions. Issues with the tools should be filed against their respective Github repositories.

Enjoy your new dockerized Puppet development stack!