In the first scenario, we'll take a deeper look at how to set up a Puppetmaster with the apply provisioner—using Puppet to manage the Puppetmaster itself. We can start this project in a couple of ways:
Bootstrap an entire Puppet environment by installing and configuring the Puppet Labs package repositories and installing Puppet
Using the Vagrant images provided by Puppet Labs and available on the Vagrant Cloud
In this example, we'll start with the Puppet Labs images. These images will have the Puppet agent preinstalled and ready to use. Bootstrapping instances to install Puppet typically involves installation and startup with a shell script that can make a development environment more complicated.
Note
A quick note on using Ubuntu images is that when using Debian-based boxes, it is typically a good idea (if not required) to execute an apt-get update
command prior to executing package installations. As this often needs to be done prior to bootstrapping a Puppet install, it's often best to do this with an inline shell provisioner.
In this section, we'll discuss how we can set up a source-controlled Puppetmaster and also discuss how it can be bootstrapped. You will also learn how to create Puppet nodes. Let's begin.
There are a few ways to start a Puppetmaster project with Vagrant, but I've found that it is often easiest to start with a working (if empty) Puppetmaster configuration and source control from a new Puppetmaster installation. To start a project:
Create a new Vagrant machine. Start by initializing a box from a box provided by Puppet Labs and is available on the Vagrant Cloud. For this example, we'll use:
vagrant init puppetlabs/ubuntu-14.04-64-puppet
To start, we can add a simple shell provisioner to execute an
apt-get update
command (for Debian-based machines only) and execute the installation of a Puppetmaster:# -*- mode: ruby -*- # vi: set ft=ruby : # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "puppetlabs/ubuntu-14.04-64-puppet" config.vm.define "puppetmaster" do |puppetmaster| puppetmaster.vm.hostname = "puppet" puppetmaster.vm.provision "shell", inline: "apt-get update && apt-get install -y puppetmaster" end end
This will start a Vagrant box and install the
puppetmaster
package. We'll use this initial package installation to create an initial configuration directory.Start the box with the
vagrant up
command.Once the box has finished booting, copy the
/etc/puppet
directory to the/vagrant
directory. Access the machine with thevagrant ssh
command and copy the directory with thecp /etc/puppet /vagrant
command.This will copy the contents of the configuration directory outside the virtual machine to the host machine. Verify that the contents of the working directory look something like this:
├── Vagrantfile ├── puppet │ ├── environments │ │ └── example_env │ │ ├── README.environment │ │ ├── manifests │ │ └── modules │ ├── manifests │ ├── modules │ ├── puppet.conf │ └── templates
With our working directory set, destroy the Vagrant machine with the
vagrant destroy
command. This will leave a clean working directory to begin working.Before starting with our Puppetmaster, let's also allow all certificate exchanges to be signed automatically. Create a file in the Puppet root directory (the directory with
puppet.conf
) namedautosign.conf
with a single line:*
This allows all certificate requests to be allowed to our local Puppetmaster. This isn't a good idea in a production environment, but it will make our development processes a bit simpler.
Now that we have a working directory, we can start bootstrapping a Puppetmaster. In some cases, a Puppetmaster can be bootstrapped with a shell script, but it's far more fun (and useful!) to manage a Puppetmaster with the Puppet itself.
Start with our Vagrantfile in the previous step, but remove the step of installing the Puppetmaster itself:
# -*- mode: ruby -*- # vi: set ft=ruby : # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "puppetlabs/ubuntu-14.04-64-puppet" config.vm.define "puppetmaster" do |puppetmaster| puppetmaster.vm.hostname = "puppet" puppetmaster.vm.provision "shell", inline: "apt-get update" end end
This will start our Puppet machine and update the package repositories for use.
Let's start simply by creating a single manifest file to bootstrap our Puppetmaster. In the
manifests/
folder, add a new file namedsite.pp
. This file is the manifest file for our Puppetmaster. Define the content of this file to simply output a notification. This will be the start of our iterative approach to develop puppet manifests:node /^puppet/ { notify{"Install a Puppetmaster": } }
Define a
puppet apply
provisioner in the Vagrantfile. Immediately after the shell provisioner, add the Puppet provisioner block:puppetmaster.vm.provision "puppet" do |puppet| puppet.manifests_path = "puppet/manifests" puppet.manifest_file = "site.pp" puppet.module_path = "puppet/modules" end
This is a basic
puppet apply
provisioner block. It will look to begin catalog compilation with thesite.pp
file and use themodules/
directory to hold reusable Puppet modules.Start the virtual machine with the
vagrant up
command. The final step in booting the machine should be output from the Puppet provisioner:==> puppetmaster: Notice: Compiled catalog for puppet.localdomain in environment production in 0.09 seconds ==> puppetmaster: Notice: Install a Puppetmaster ==> puppetmaster: Notice: /Stage[main]/Main/Node[puppet]/Notify[Install a Puppetmaster]/message: defined 'message' as 'Install a Puppetmaster' ==> puppetmaster: Notice: Finished catalog run in 0.01 seconds
Our output notification here notes that the
notify
resource was successfully called by the Puppet provisioner. The Puppet provisioner can be called subsequently with thevagrant provision
command rather than doing a full restart.With our initial Puppetmaster machine ready to provision, let's create an environment that allows us to modify the configuration of the Puppetmaster (and any Puppet modules) using a local text editor. To do this, we'll link our
puppet/
directory in our host working directory to the/etc/puppet
configuration directory on the guest. (This was our reason to copy files in our first step.) To write this puppet module, we will have to:Install the Puppetmaster package.
Connect to the Vagrant machine with the
vagrant ssh
command.Remove the existing
/etc/puppet
directory installed as part of the Puppetmaster package. We will replace the installed directory with the one we created earlier in our Vagrant working directory.Create a symbolic link in the guest from
/vagrant/puppet
to/etc/puppet
.Restart the Puppetmaster daemon with a
service puppetmaster restart
command. This will read any differences that are present in the symlinked working directory.
If you have done puppet development previously, you might recognize this as a potential use of the package-file-service pattern. We can replace the
notify
command in thesite.pp
file created previously with some Puppet code that reflects the installation of the Puppetmaster. The fullsite.pp
manifest looks like this:node /^puppet/ { package{"puppetmaster": ensure => '3.7.3-1puppetlabs1', } file{"/etc/puppet": ensure => 'link', force => 'true', target => '/vagrant/puppet', require => Package["puppetmaster"], notify => Service["puppetmaster"], } service{"puppetmaster": ensure => running, require => Package['puppetmaster'], } }
This will install the Puppetmaster, link our Puppetmaster code, and start the Puppetmaster.
Execute the manifest by running
vagrant provision puppetmaster
. This will provision and start a Puppetmaster instance in the virtual machine.Verify that the Puppetmaster is running successfully by logging in to the machine (
vagrant ssh puppetmaster
) and running the Puppet agent process. After logging in to the machine, become a super user by executing the following command:sudo puppet agent –t
This will start the Puppet agent. The Puppet agent will attempt to communicate with a Puppetmaster at the default address (Puppet) and retrieve a catalog. The Puppet agent should return quickly, as no changes are registered between the run of the Vagrant provisioner and the agent run:
Info: Retrieving pluginfacts Info: Retrieving plugin Info: Caching catalog for puppet.localdomain Info: Applying configuration version '1416969518' Notice: Finished catalog run in 0.09 seconds
This Puppetmaster setup can be used to continue developing Puppet code in order to deploy using the masterless or agent approach (both should be usable interchangeably). When developing modules, it is often enough to develop using the masterless approach, but it can also be useful to see how nodes interact in a full master/agent environment.