Puppet is a configuration management system. In short this means that by setting up a server (the Puppet master) you can manage many other machines (nodes) with this server by specifying which packages should be installed, files that need to be present, their permissions, etc. The nodes poll the server every 30 minutes (by default) to see if they should apply any changes to their configuration. Other packages that implement a similar idea are CfEnfine and Chef.
Note that all these instructions were performed as root.
The puppet master
Gaffel will be puppet master. I’ve added a DNS entry for puppet.karssen.org that points to gaffel. This installs the client and the Puppet master:
$ aptitude install puppet puppetmaster |
The main configuration of server and client can be found in /etc/puppet/puppet.conf. We’ll leave it at the default for now. The file /etc/puppet/manifests/site.pp contains options that apply to the whole site. Let’s make it and add the following contents:
import "nodes" # The filebucket is for backups. Originals of files that Puppet modifies # get stored here. filebucket { main: server => 'puppet.karssen.org' } File { backup => main} # Set the default $PATH$ for executing commands on node systems. Exec { path => "/usr/bin:/usr/sbin:/bin:/sbin:" } |
The file /etc/puppet/manifests/nodes.pp defines the nodes/clients that will be managed by puppet as well as what configuration will be applied to them, so-called roles. For now, let’s make a quick example:
node common { include packages } node lambik inherits common { include ntp::client } |
Both the ‘packages’ and the ‘ntp’ modules still need to be defined. Let’s do that now.
Modules are collections of puppet code (known as manifests) and related files that are used for client configuration. Modules are stored in /etc/puppet/modules/.
Let’s start with the ntp example. First make the necessary directory structure:
$ mkdir -p /etc/puppet/modules/ntp/{manifests,files,templates} |
Every modules needs a file init.pp that declares the class. It can also include other files. The files and templates directories are used to store files that need to be copied to the node or templates to make such files, respectively. We’ll come across some examples of both. This is the init.pp file for the ntp role (/etc/puppet/modules/ntp/manifests/init.pp):
class ntp::client { package { "ntp": ensure => installed, } service { "ntp_client": name => "ntp" ensure => running, # hasstatus => true, hasrestart => true, require => Package["ntp"], } } |
Here we indicate that the NTP service must be running and that it’s init script (in /etc/init.d) accepts the status and restart options. Lastly in the require line we note that before this manifest can be applied we must make sure that the package ntp has been installed. This is necessary, because the order in which the two directives are executed is not necessarily the order in
which they appear in the manifest.
The # in from of the hasstatus attribute is because of a bug inthe puppet version (2.6.4) shipped with Ubuntu 11.04. See http://projects.puppetlabs.com/issues/5610 for the bug report. In version 2.6.7 it is supposedly fixed.
In our nodes.pp file we also mentioned a packages class. In this class we list all the packages that we want to have installed on the node. Let’s make the packages module. First create the necessary directories:
$ mkdir -p /etc/puppet/modules/packages/{manifests,files,templates} |
Add the file /etc/puppet/modules/packages/manifests/init.pp:
class packages { $base_packages = [ "openssh-server", "nfs-common", "etckeeper", "htop", "iotop", "iftop", ] $editor_packages = [ "emacs", "emacs-goodies-el", "elscreen", ] $all_packages = [ $base_packages, $editor_packages, ] package { $all_packages: ensure => installed, } } |
Here I’ve defined three variables (beginning with a $ sign), one for base packages, one for editor-related packages and one called $all_packages that incorporates them both. Finally, I tell puppet to ensure they are all installed.
Setting up a client
As a test client I’m using lambik, one of my MythTV frontends.
$ aptitude install puppet |
To make sure that puppet starts by default on system startup edit the file /etc/default/puppet and set START to yes:
# Defaults for puppet - sourced by /etc/init.d/puppet # Start puppet on boot? START=yes # Startup options DAEMON_OPTS="" |
Now edit /etc/puppet/puppet.conf (on the client) and add the FQDN of the puppet master server to the [main] section:
[main] logdir=/var/log/puppet vardir=/var/lib/puppet ssldir=/var/lib/puppet/ssl rundir=/var/run/puppet factpath=$vardir/lib/facter templatedir=$confdir/templates prerun_command=/etc/puppet/etckeeper-commit-pre postrun_command=/etc/puppet/etckeeper-commit-post server = puppet.karssen.org [master] # These are needed when the puppetmaster is run by passenger # and can safely be removed if webrick is used. ssl_client_header = SSL_CLIENT_S_DN ssl_client_verify_header = SSL_CLIENT_VERIFY |
Setting up secure communication between master and nodes and first test run
Puppet uses SSL certificates to set up a secure connection between master and nodes. Before you can apply any changes to the client, certificates need to be exchanged and signed. First, tell the client to connect to the puppet master:
$ puppetd --test info: Creating a new SSL key for lambik.karssen.org warning: peer certificate won't be verified in this SSL session info: Caching certificate for ca warning: peer certificate won't be verified in this SSL session warning: peer certificate won't be verified in this SSL session info: Creating a new SSL certificate request for lambik.karssen.org info: Certificate Request fingerprint (md5): 1D:A3:3A:4A:A6:DA:D6:C8:96:F4:D4:7E:52:F4:12:1D warning: peer certificate won't be verified in this SSL session warning: peer certificate won't be verified in this SSL session warning: peer certificate won't be verified in this SSL session Exiting; no certificate found and waitforcert is disabled
On the puppet master we can now sign the certificate:
$ puppetca -l lambik.karssen.org $ puppetca -s lambik.karssen.org notice: Signed certificate request for lambik.karssen.org notice: Removing file Puppet::SSL::CertificateRequest lambik.karssen.org at '/var/lib/puppet/ssl/ca/requests/lambik.karssen.org.pem'
On the client we can now rerun puppetd:
root@lambik:~# puppetd --test info: Caching catalog for lambik.karssen.org info: Applying configuration version '1311930908' notice: /Stage[main]/Packages/Package[iotop]/ensure: ensure changed 'purged' to 'present' notice: /Stage[main]/Packages/Package[iftop]/ensure: ensure changed 'purged' to 'present' notice: /Stage[main]/Ntp/Package[ntp]/ensure: ensure changed 'purged' to 'present' notice: /Stage[main]/Packages/Package[emacs-goodies-el]/ensure: ensure changed 'purged' to 'present' notice: /Stage[main]/Packages/Package[htop]/ensure: ensure changed 'purged' to 'present' info: Creating state file /var/lib/puppet/state/state.yaml notice: Finished catalog run in 78.43 seconds
If all went well, we can now start the puppet client daemon to keep our system under puppet control:
$ service puppet start |
Adding (configuration) files to the roles
Since I run my own NTP server (ntp.karssen.org, only accessible from inside my LAN), the NTP configuration file (/etc/ntp.conf) must be changed. Of course, we want Puppet to take care of this. The ntp.conf file I want to distribute to all nodes has the following contents (note that the only change is the name of the server and commenting the restrict lines):
# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help driftfile /var/lib/ntp/ntp.drift # Enable this if you want statistics to be logged. #statsdir /var/log/ntpstats/ statistics loopstats peerstats clockstats filegen loopstats file loopstats type day enable filegen peerstats file peerstats type day enable filegen clockstats file clockstats type day enable # Specify one or more NTP servers. # Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board # on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for # more information. server ntp.karssen.org # Use Ubuntu's ntp server as a fallback. server ntp.ubuntu.com # Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for # details. The web page <http://support.ntp.org/bin/view/Support/AccessRestrict ions> # might also be helpful. # # Note that "restrict" applies to both servers and clients, so a configuration # that might be intended to block requests from certain clients could also end # up blocking replies from your own upstream servers. # By default, exchange time with everybody, but don't allow configuration. #restrict -4 default kod notrap nomodify nopeer noquery #restrict -6 default kod notrap nomodify nopeer noquery # Local users may interrogate the ntp server more closely. restrict 127.0.0.1 restrict ::1 # Clients from this (example!) subnet have unlimited access, but only if # cryptographically authenticated. #restrict 192.168.123.0 mask 255.255.255.0 notrust # If you want to provide time to your local subnet, change the next line. # (Again, the address is an example only.) #broadcast 192.168.123.255 # If you want to listen to time broadcasts on your local subnet, de-comment the # next lines. Please do this only if you trust everybody on the network! #disable auth #broadcastclient |
Save this file in /etc/puppet/modules/ntp/files (on the puppet master). Now edit the manifest for the ntp role (/etc/puppet/modules/ntp/manifest/init.pp) to add the file section and a subscribe command:
class ntp::client { package { "ntp": ensure => installed, } service { "ntp_client": name => "ntp", ensure => running, # hasstatus => true, hasrestart => true, require => Package["ntp"], subscribe => File["ntp_client_config"], } file { "ntp_client_config": path => "/etc/ntp.conf", owner => root, group => root, mode => 644, source => "puppet:///ntp/ntp.conf", require => Package["ntp"], } } |
The URL specified in the source line automatically looks in the right place (as mentioned just above) for the file. Because we don’t want to wait for puppet to automatically pass on this configuration, let’s run it by hand:
root@lambik:~# puppetd --test info: Caching catalog for lambik.karssen.org info: Applying configuration version '1311936811' --- /etc/ntp.conf 2011-06-17 07:59:54.000000000 +0200 +++ /tmp/puppet-file20110729-12128-1h3fupz-0 2011-07-29 12:53:33.279622938 +0200 @@ -16,16 +16,14 @@ # Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board # on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for # more information. -server 0.ubuntu.pool.ntp.org -server 1.ubuntu.pool.ntp.org -server 2.ubuntu.pool.ntp.org -server 3.ubuntu.pool.ntp.org +server ntp.karssen.org # Use Ubuntu's ntp server as a fallback. server ntp.ubuntu.com @@ -33,8 +31,8 @@ # up blocking replies from your own upstream servers. # By default, exchange time with everybody, but don't allow configuration. -restrict -4 default kod notrap nomodify nopeer noquery -restrict -6 default kod notrap nomodify nopeer noquery +#restrict -4 default kod notrap nomodify nopeer noquery +#restrict -6 default kod notrap nomodify nopeer noquery # Local users may interrogate the ntp server more closely. restrict 127.0.0.1 info: FileBucket adding /etc/ntp.conf as {md5}32280703a4ba7aa1148c48895097ed07 info: /Stage[main]/Ntp::Client/File[ntp_client_config]: Filebucketed /etc/ntp.conf to main with sum 32280703a4ba7aa1148c48895097ed07 notice: /Stage[main]/Ntp::Client/File[ntp_client_config]/content: content changed '{md5}32280703a4ba7aa1148c48895097ed07' to '{md5}0d1b81c95bab1f6b08eb27dfaeb18bb5' info: /Stage[main]/Ntp::Client/File[ntp_client_config]: Scheduling refresh of Service[ntp_client] notice: /Stage[main]/Ntp::Client/Service[ntp_client]: Triggered 'refresh' from 1 events notice: Finished catalog run in 3.06 seconds |
Setting NFS mounts in /etc/fstab
On my clients I want to mount several NFS shares. Let’s create the directories for the nfs_mounts module (on the puppet master of course):
$ mkdir -p /etc/puppet/modules/nfs_mounts/{manifests,files,templates} |
Next, let’s edit the manifest (/etc/puppet/modules/nfs_mounts/manifests/init.pp):
class nfs_mounts { # Create the shared folder unless it already exists exec { "/bin/mkdir -p /var/sharedtmp/": unless => "/usr/bin/test -d /var/sharedtmp/", } mount { "/var/sharedtmp/": atboot => true, ensure => mounted, device => "nfs.karssen.org:/var/sharedtmp", fstype => "nfs", options => "vers=3", require => Package["nfs-common"], } } |
This should make the /var/sharedtmp directory and mount it. Note that I mention the nfs_common package in a require line. This package was defined in the packages module (in the $base_packages variable. Now let’s add this module to the nodes.pp file:
node common { include packages } node lambik inherits common { include ntp::client include nfs_mounts } |
Since I’ve got more than a single NFS mount, let’s extend the previous example and use a defined resource. Change the file /etc/puppet/modules/nfs_mounts/manifests/init.pp as follows:
define nfs_mount( $location, $server = "nfs.karssen.org", $options = "vers=3", $fstype = "nfs" ) { file {"$location": ensure => directory, } mount { "$location": atboot => true, ensure => mounted, device => "${server}:${location}", fstype => "$fstype", options => "$options", require => [ Package["nfs-common"], File["$location"] ], } } class nfs_mounts { nfs_mount { "/home": location => "/home", } nfs_mount { "/var/sharedtmp": location => "/var/sharedtmp", } nfs_mount { "/var/video": location => "/var/video", } nfs_mount { "/var/music": location => "/var/music", } } |
Here we first define a resource called nfs_mount, which can accept various parameters, all of which have a default value, except $location. Secondly we ensure that this location is a directory and then we define how it should be mounted. In the subsequent class definition we use this nfs_mount resource several times to mount the various NFS shares.
Note that it would have been easier if the definition of nfs_mount would have started with
define nfs_mount ( $location = $name, |
because then the invocations of nfs_mount in the class would not
need the location => line. Unfortunately this doesn’t work. It’s
a known bug that has been fixed in version 2.6.5
(http://projects.puppetlabs.com/issues/5061).
Links
- Article by Jes Fraser in the July 2011 edition of Linux Journal. A lot of this info was taken from this article.
- A blog
- The Puppet documentation
- The Puppet language guide