This is the third part of my blog series on creating a fully functional Puppet stack with Docker. The previous post on building Puppet container images with Packer can be found here.

Puppet infrastructure module

I've created a simple Puppet module that manages a complete Puppet infrastructure using the Forman installer and PuppetDB community module.

The people behind Foreman have really done a wonderful job of making sure that the whole installation and configuration of the Puppetmaster and Foreman is as streamlined as possible. They've created a special Foreman installer package that is capable of installing The Foreman, Puppetmaster or Foreman smart-proxy in any configuration you want.

When you run the installer in interactive mode you are able to configure every setting that is used by the underlying internal Puppet classes. These settings are written to an answerfile which we can then use in my Puppet infra module. Basically my Puppet module then calls the Foreman installer which in turn runs Puppet to configure the applications.

This way you can stay up to date with the latest changes by upgrading the Foreman installer package but at the same time still retain full control on how to configure the whole setup. You can see the two answerfiles that I have included here. They are specific to the way I want to install these applications but you can easily adjust them to better suit your needs.

Let's have a look at how to configure each container with this module.

PuppetDB

The PuppetDB container is simply configured using the Puppetlabs PuppetDB module:

node default {

  include infra::puppetdb

  ...
}

class infra::puppetdb {

  class { '::puppetdb::server':
    database_host  => 'localhost',
    listen_address => '0.0.0.0'
  }

  class { '::puppetdb::database::postgresql':
    listen_addresses => 'localhost',
  }

}

This tells Puppet to install the PuppetDB server and PostgreSQL on the same server and listen on the localhost address.

Puppetmaster

The Puppetmaster is mostly configured with the Foreman installer:

node default {

  include infra::puppetmaster

  ...

    # Fix the common module path
  file_line { 'puppetmaster_common_modules':
    path    => '/etc/puppet/puppet.conf',
    line    => '    basemodulepath   = /etc/puppet/environments/common/modules:/etc/puppet/modules:/usr/share/puppet/modules',
    match   => '    basemodulepath.*',
    require => Exec['foreman-installer']
  }

  # Install and configure Hiera
  class { 'hiera':
    eyaml     => true,
    datadir   => '/etc/puppet/hiera',
    hierarchy => [
      '%{::environment}/nodes/%{::clientcert}',
      '%{::environment}/modules/%{module_name}',
      '%{::environment}/common'
    ]
  }

}

class infra::puppetmaster {

  # Provide the answerfile for the puppet installer
  file { '/etc/foreman/foreman-installer-answers.yaml':
    ensure  => present,
    source  => 'puppet:///modules/infra/puppetmaster.answers',
    owner   => root,
    group   => root,
    mode    => '0600',
    require => Package['foreman-installer'],
  }

  class { 'puppetdb::master::config':
    puppetdb_server   => 'puppetdb.dev.local',
    require           => Exec['foreman-installer'],
    strict_validation => false,
    restart_puppet    => false
  }

  include infra::installer

}

class infra::installer {

  package { 'cronie':
    ensure => present
  } ->

  package { 'foreman-installer':
    ensure => '1.6.2-1.el6',
    notify => Exec['foreman-installer']
  }

  # Execute the foreman installer
  exec { 'foreman-installer':
    command     => '/usr/sbin/foreman-installer',
    timeout     => 0,
    refreshonly => true,
    require     => File['/etc/foreman/foreman-installer-answers.yaml']
  }

}

Several things are happening here. The infra::puppetmaster class is included and Hiera is installed. There is also a small fix to set the common modules basepath to the correct directory according to my project. If we look at the infra::puppetmaster class we can see that the Puppetmaster answerfile is copied to the node and the PuppetDB module is used to configure all the settings for PuppetDB as a backend to the Puppetmaster. Next, the infra::installer class is called. This class installs the foreman-installer package according to a specific version and runs the foreman-installer with the answerfile that we provided. It also installs the cronie package because it is needed by the foreman-installer. That's all there is to it really. All the work is being done by the Foreman installer with our provided answers, neat!

The Foreman

The installation for The Foreman is almost exactly the same as descibed for the Puppetmaster. The only difference is in the provided answerfile which reconfigures the Foreman installer to install different components.

class infra::foreman {

  # Provide the answerfile for the puppet installer
  file { '/etc/foreman/foreman-installer-answers.yaml':
    ensure  => present,
    source  => 'puppet:///modules/infra/foreman.answers',
    owner   => root,
    group   => root,
    mode    => '0600',
    require => Package['foreman-installer'],
  }

  include infra::installer

}

Provisioning script

I have explained how we can use Packer to define and create Docker images. In this post we looked at the most important part of that process, the Puppet provisioning step.

I've created a script that performs all the necessary steps described in this, and the previous blog post, to create fully provisioned Docker images for PuppetDB, Puppetmaster and The Foreman, ready to be pushed to the Docker registry. Please have a look at the Github documentation on how to run the script.


Now that we have fully provisioned Docker images it is time to start lifting containers with Crane in the next part of this blog series!