James Attard
May 21 · 4 min read
Dockerizing a Puppet infrastructure

Deploying a Puppet infrastructure on Docker has many advantages, including portability.

Unfortunately there are not a lot of articles explaining how this is done, so hopefully this can serve as a good starter. This article was inspired from Puppet Labs Docker project.

Getting Started

To get started, you will need an installation of Docker Compose on the host on which you will run your Puppet Infrastructure. We then continue to create the following docker-compose.yml file:

version: '3'

services:
puppet:
hostname: puppet.${DOMAIN:-internal}
image: puppet/puppetserver
ports:
- 8140:8140
environment:
# necessary to set certname and server in puppet.conf, required by
# puppetserver ca cli application
- PUPPETSERVER_HOSTNAME=puppet.${DOMAIN:-internal}
# DNS_ALT_NAMES must be set before starting the stack the first time,
# and must list all the names under which the puppetserver can be
# reached. 'puppet.${DOMAIN:-internal}' must be one of them, otherwise puppetdb won't be
# able to get a cert. Add other names as a comma-separated list
- DNS_ALT_NAMES=puppet,puppet.${DOMAIN:-internal},${DNS_ALT_NAMES:-}
- PUPPERWARE_ANALYTICS_ENABLED=${PUPPERWARE_ANALYTICS_ENABLED:-true}
- PUPPETDB_SERVER_URLS=https://puppetdb.${DOMAIN:-internal}:8081
volumes:
- ${VOLUME_ROOT:-.}/volumes/code:/etc/puppetlabs/code/
- ${VOLUME_ROOT:-.}/volumes/puppet:/etc/puppetlabs/puppet/
- ${VOLUME_ROOT:-.}/volumes/serverdata:/opt/puppetlabs/server/data/puppetserver/
dns_search: ${DOMAIN:-internal}
networks:
default:
aliases:
- puppet.${DOMAIN:-internal}

postgres:
image: postgres:9.6
environment:
- POSTGRES_PASSWORD=puppetdb
- POSTGRES_USER=puppetdb
- POSTGRES_DB=puppetdb
expose:
- 5432
volumes:
- ${VOLUME_ROOT:-.}/volumes/puppetdb-postgres/data:/var/lib/postgresql/data
- ./postgres-custom:/docker-entrypoint-initdb.d
dns_search: ${DOMAIN:-internal}
networks:
default:
aliases:
- postgres.${DOMAIN:-internal}

puppetdb:
hostname: puppetdb.${DOMAIN:-internal}
image: puppet/puppetdb
environment:
- PUPPERWARE_ANALYTICS_ENABLED=${PUPPERWARE_ANALYTICS_ENABLED:-true}
# This name is an FQDN so the short name puppet doesn't collide outside compose network
- PUPPETSERVER_HOSTNAME=puppet.${DOMAIN:-internal}
- PUPPETDB_POSTGRES_HOSTNAME=postgres.${DOMAIN:-internal}
- PUPPETDB_PASSWORD=puppetdb
- PUPPETDB_USER=puppetdb
ports:
- 8080
- 8081
depends_on:
- postgres
- puppet
volumes:
- ${VOLUME_ROOT:-.}/volumes/puppetdb/ssl:/etc/puppetlabs/puppet/ssl/
dns_search: ${DOMAIN:-internal}
networks:
default:
aliases:
- puppetdb.${DOMAIN:-internal}

This file will create the following Docker containers that are created from the official Puppet Labs Docker images:

  1. puppet: This is the Puppet server. We pass a number of environmental variables, including the URL to the PuppetDB server, which will be hosted on another Docker Container. A number of directories, including the Puppet Code, Puppet configuration and Server Data, will be mounted on separate Docker volumes.
  2. postgres: This is the PostgreSQL database server which will store the data for PuppetDB.
  3. puppetdb: This is the PuppetDB container that will store all the facts and configuration required to run the Puppet infrastructure. The actual data will be stored in the postgres container defined above. We therefore set a couple of dependencies to make sure that the previous two containers are up and running before spawning this one.

Running the Dockerized Puppet

You can start the Dockerized Puppet infrastructure by running the following command:

DNS_ALT_NAMES=host.exam docker-compose up -d

The value of DNS_ALT_NAMES must list all the names, as a comma-separated list, under which the Puppet server in the stack can be reached from agents. It will have puppet and puppet.internal prepended to it as that name is used by PuppetDB to communicate with the Puppet server. The value of DNS_ALT_NAMES only has an effect the first time you start the stack, as it is placed into the server's SSL certificate. If you need to change it after that, you will need to properly revoke the server's certificate and restart the stack with the changed DNS_ALT_NAMES value.

Optionally, you may also provide a desired DOMAIN value, other than default value of internal to further define how the service hosts are named. It is not necessary to change DNS_ALT_NAMES as the default value already takes into account any custom domain.

DOMAIN=foo docker-compose up -d

When you first start the Puppet Infrastructure, the stack will create a volumes/ directory with a number of sub-directories to store the persistent data that should survive the restart of your infrastructure. This directory is created right next to the Docker Compose file and contains the following sub-directories:

  • code/: the Puppet code directory.
  • puppet/: Puppet configuration files, including puppet/ssl/ containing certificates for your infrastructure. This directory is populated with default configuration files if they are not present when the stack starts up. You can make configuration changes to your stack by editing files in this directory and restarting the stack.
  • puppetdb/ssl/: certificates in use by the PuppetDB instance in the stack.
  • puppetdb-postgres/: the data files for the PostgreSQL instance used by PuppetDB
  • serverdata/: persistent data for Puppet Server
  • Note: On OSX, you must add the volumes directory to "File Sharing" under Preferences>File Sharing in order for these directories to be created and volume-mounted automatically. There is no need to add each sub directory.

Adding your first manifest

As explained above, the Puppet code will be mounted on a Docker volume — code/. The hierarchical structure must be therefore created inside this directory as follows:

$ mkdir code/environments/production/manifests

You can create your first main manifest, site.pp, inside the manifests directory, as follows:

node default {
file {'/tmp/example-ip':
ensure => present,
mode => '0644',
content => "Here is my Public IP Address: ${ipaddress_eth0}.\n",
}
}

This example manifest will create a file with the machine’s Public IP Address.

Test your Dockerized Puppet setup

We can test the setup by running the puppet agent inside the Puppet master container:

# docker exec -it <Puppet Container ID> bash
root@puppet:/# puppet agent -t

If everything is working OK, you should see our newly created example file inside the /tmp/ directory.

Next steps

Now you are ready to take this setup a couple of steps further:

  1. Point your other Puppet slave/agent nodes to this Dockerized Puppet server. They could be regular servers or other Docker containers.
  2. Point your browser to the PuppetDB Docker container: http://puppetDB:8080 to view analytics pertaining to your Puppet infrastructure.

About me

DevOps Engineer, James, works for COSTANSIN, a software agency in Malta. He thinks that all Infrastructure can be reduced into a few lines of code thanks to Docker and Puppet, and many corporations can become more efficient with Infrastructure as a Code.

The Startup

Medium's largest active publication, followed by +492K people. Follow to join our community.

James Attard

Written by

DevOps with a passion for Python, JavaScript, AI/ML, Blockchain and anything related with Data. Founder of https://costansin.com.

The Startup

Medium's largest active publication, followed by +492K people. Follow to join our community.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade