puppet is made by Reductive Labs and released under the GNU Public License. It is a system to manage configuration files and services. It is written in ruby.
There are several products out there that do the same job as puppet, the main competitor is Cfengine. There is also the open source version of Red Hat satellite server spacewalk. All these systems have their various benefits and pitfalls. I find puppet to be simpler to configure and very easy to extend. One possible pitfall is that it is written in Ruby, and I have almost no experience in ruby. That said, it's a very simple language to read to quite easy to follow (unlike PERL).
To configure puppet, you install the puppet-server rpm or build from source. The rpms are available for centos, RHEL (via EPEL) and Fedora. In the previous section we downloaded the puppet rpms from EPEL and installed them in our local repository.
The steps to configure puppet are
[root@server0 ~]# chkconfig puppetmaster on [root@server0 ~]# cd /etc/puppet [root@server0 puppet]# ls fileserver.conf manifests puppet.conf [root@server0 puppet]# ls manifests [root@server0 puppet]#Both the server and client versions use the /etc/puppet directory. Both are configured from puppet.conf. The default configuration of puppet.conf will be sufficient at this point, but the manifests directory is completely empty. This is possibly the biggest complain with puppet, you start with a blank slate. It is up to you to populate the manifest directory. The only file you will need is site.pp in that directory. The site.pp file names all the other files you wish to include in your manifest.
The puppetmaster listens on port 8140, so we'll need to add that port to our firewall
[root@server0 ~]# iptables -I RH-Firewall-1-INPUT -p tcp -m state --state NEW,ESTABLISHED,RELATED --dport 8140 -j ACCEPT [root@server0 ~]# iptables-save >/etc/sysconfig/iptablesWe cannot start the puppetmaster yet, we have an empty manifests directory, we'll start building the contents of that directory now.
There are many types predefined, for a complete list of all types and their usage, see the documentation. Common types used are:
To create a new class, use the class keyword. As a simple example, we'll create a class which creates a file in root's home directory called hello, with the word "world" in it.
class test {
file {"/root/hello":
content => "world",
mode => 644,
owner => root,
group => root
}
}
To have this class applied to a node, we need to assign the class to the node with the node and include keywords.
node client15 {
include test
}
Put these definitions in site.pp and start puppetmaster.
[root@server0 manifests]# cat site.pp
class test {
file {"/root/hello":
content => "world",
mode => 644,
owner => root,
group => root
}
}
node client15 {
include test
}
[root@server0 manifests]# service puppetmaster start
Starting puppetmaster: [ OK ]
Now that we have a minimal site.pp installed, we can configure our client and test the configuration. We'll configure clients from kickstart later, but to test the install at this point, login to client15 and execute puppet manually.
[root@client15 ~]# puppetd --no-daemonize --server server0.example.com --test --no-splay info: Creating a new certificate request for client15.example.com info: Creating a new SSL key at /var/lib/puppet/ssl/private_keys/client15.example.com.pem warning: peer certificate won't be verified in this SSL session notice: Did not receive certificate notice: Set to run 'one time'; exiting with no certificateOur client successfully connected to the puppetmaster but it failed to retrieve a catalog (what puppet calls the collection of actions to perform on the client). This is because our ssl key was not signed by the puppetmaster. Back on the puppetmaster, we'll sign the key for client15 and then try our test again.
[root@server0 ssl]# puppetca --list client15.example.com [root@server0 ssl]# puppetca --sign client15.example.com Signed client15.example.comBack on client 15
[root@client15 ~]# puppetd --no-daemonize --server server0.example.com --test --no-splay
warning: peer certificate won't be verified in this SSL session
notice: Got signed certificate
info: Caching catalog at /var/lib/puppet/localconfig.yaml
notice: Starting catalog run
notice: //Node[client15]/test/File[/root/hello]/content: defined 'content' as '{md5}7d793037a0760186574b0282f2f435e7'
notice: //Node[client15]/test/File[/root/hello]/owner: defined 'owner' as 'root'
notice: //Node[client15]/test/File[/root/hello]/group: defined 'group' as 'root'
notice: //Node[client15]/test/File[/root/hello]/mode: defined 'mode' as '644'
info: Creating state file /var/lib/puppet/state/state.yaml
notice: Finished catalog run in 0.03 seconds
[root@client15 ~]# cat /root/hello
world[root@client15 ~]#
Now that we have verified that puppet is working, we'll make a proper site.pp file and some useful classes.
# site.pp import "functions.pp" # define nodes import "nodes.pp" # define classes import "base.pp"
define remotefile($owner = root, $group= root, $mode, $server = "server0.example.com", $cls="base", $backup = false, $recurse = false) {
file {
$name:
mode => $mode,
owner => $owner,
group => $group,
backup => $backup,
source => "puppet://$server/$cls/$name"
}
}
The name of the function is remotefile, since we imported functions.pp in our site.pp, we can use remotefile in any other file we include from site.pp. The file type in puppet can have a remote location that is given in the source option. Our function defines a default location of puppet://server0.example.com/base/$name where name is the name of the file. We also set the owner and group to root by default. Without using this function, copying an /etc/resolv.conf file from our puppetmaster to a client would look like this:
file {"/etc/resolv.conf":
mode => 644,
owner => root,
group => root,
backup => false
source => "puppet://server0.example.com/base/etc/resolv.conf"
}
Using the function we can slim this down to:
remotefile { "/etc/resolv.conf": mode=> 644 }
Next we will define our nodes and a default class for puppetmaster to apply to the nodes.
node default {
include base
}
With this defined, we'll create another manifest for the class base.
[root@server0 manifests]# su - signer [signer@server0 ~]$ ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/home/signer/.ssh/id_rsa): Created directory '/home/signer/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/signer/.ssh/id_rsa. Your public key has been saved in /home/signer/.ssh/id_rsa.pub. The key fingerprint is: 35:b7:84:6d:34:8c:76:9a:8d:7c:3e:4a:e8:c1:1e:fd signer@server0.example.com [signer@server0 ~]$ cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAr9rnu0jbSPipuZI2umz/v73jeRTjxlX9D7cHSFIaJUUShFSelFUfojkjl4ri4m4qc40icArMa4NMGZ9d3y+ZqMqeIPZVtJKEqkn2E9GJS36N13H75DwVPv4KE2oLR9Zk4T8HovLr50tWbJr5/G6VfwwybR3q6HdJSO7liAKmrJwFokev1fsmiZQX+rADL8XB+gZ/9FsFIi4F4YKsLGQz78CSf/jZ71qNC5Y4HniVQDv6RmZp+koHT6hOPKTuUD/VOWXHxoLc9c6ypkeSMaINvNHDvmUsbp+rNppiZPKnsDFoh3fL4h5pFKJ1DAYjOdnhLKJgwLzmBq7qfYpd/PEw2Q== signer@server0.example.com [signer@server0 ~]$ exit [root@server0 manifests]# cat base.pp class base { remotefile { "/etc/sysconfig/puppet": mode => 644 } service { puppet: ensure => true, enable => true, hasrestart => true } ssh_authorized_key { "signer": ensure => present, type => "ssh-rsa", key => "AAAAB3NzaC1yc2EAAAABIwAAAQEAr9rnu0jbSPipuZI2umz/v73jeRTjxlX9D7cHSFIaJUUShFSelFUfojkjl4ri4m4qc40icArMa4NMGZ9d3y+ZqMqeIPZVtJKEqkn2E9GJS36N13H75DwVPv4KE2oLR9Zk4T8HovLr50tWbJr5/G6VfwwybR3q6HdJSO7liAKmrJwFokev1fsmiZQX+rADL8XB+gZ/9FsFIi4F4YKsLGQz78CSf/jZ71qNC5Y4HniVQDv6RmZp+koHT6hOPKTuUD/VOWXHxoLc9c6ypkeSMaINvNHDvmUsbp+rNppiZPKnsDFoh3fL4h5pFKJ1DAYjOdnhLKJgwLzmBq7qfYpd/PEw2Q==", name => "signer@example.com", target => "/root/.ssh/authorized_keys" } } [root@server0 manifests]#Now we have to create /etc/sysconfig/puppet that we referenced in our call to remotefile.
[root@server0 puppet]# pushd /var/lib/puppet/files /var/lib/puppet/files /etc/puppet [root@server0 puppet]# mkdir -p files/base/etc/sysconfig [root@server0 puppet]# mkdir facts [root@server0 puppet]# cd files/base/etc/sysconfig [root@server0 sysconfig]# cat <Next, we'll configure the puppet fileserver to serve out files that are stored in /var/lib/puppet/files/basepuppet > PUPPET_SERVER=server0.example.com > PUPPET_EXTRA_OPTS=--factsync > EOF [root@server0 files]# popd /etc/puppet
[root@server0 puppet]# cat fileserver.conf [base] path /var/lib/puppet/files/base allow *.example.com allow 192.168.0.0/24 [facts] path /var/lib/puppet/facts allow *.example.com allow 192.168.0.0/24Now we have defined the base and facts shares. The facts share is used by facter, something we'll talk about later, but we might as well configure it's file share while we are here. At this point we can restart the puppetmaster, but when new clients connect, they have to wait for us to manually sign their keys. We'll configure puppet to automatically sign keys using autosign.conf
[root@server0 puppet]# cat autosign.conf *.example.com 192.168.0.0/24
At this point new clients can connect and (provided they are in example.com or our subnet) they will have their keys automatically signed. They will all be placed in the base class by default. Our next step is to go back to our kickstart and have puppet configured with kickstart.
%post chvt 3 echo "executing post install" echo hostname for puppet is $HOSTNAME puppetd --fqdn=$HOSTNAME --test --no-splay --server=server0.example.com --onetime --verbose --factsync echo "type enter to continue" read enter_key chvt 1After installing your machine should change to virtual terminal 3 (chvt 3) and echo "executing post install" followed by the output from puppet
info: Retrieving facts
info: Caching catalog at /var/lib/puppet/localconfig.yaml
notice: Starting catalog run
notice: //Node[default]/base/Ssh_authorized_key[signer]/ensure: created
1,11c1,2
< # The puppetmaster server
< #PUPPET_SERVER=puppet
<
< # If you wish to specify the port to connect to do so here
< #PUPPET_PORT=8140
<
< # Where to log to. Specify syslog to send log messages to the system log.
< #PUPPET_LOG=/var/log/puppet/puppet.log
<
< # You may specify other parameters to the puppet client here
< #PUPPET_EXTRA_OPTS=--waitforcert=500
---
> PUPPET_SERVER=server0.example.com
> PUPPET_EXTRA_OPTS=--factsync
notice: //Node[default]/base/Remotefile[/etc/sysconfig/puppet]/File[/etc/sysconfig/puppet]/source: replacing from source puppet://server0.example.com/base//etc/sysconfig/puppet with contents {md5}87dd2effcdc742da03df3ab010c03436
notice: //Node[default]/base/Service[puppet]/enable: enable changed 'false' to 'true'
notice: Finished catalog run in 0.23 seconds
type enter to continue
%post chvt 3 echo "executing post install" cat >/etc/sysconfig/puppet <<EOF PUPPET_SERVER=server0.example.com PUPPET_EXTRA_OPTS=--factsync EOF chkconfig puppet on echo hostname for puppet is $HOSTNAME puppetd --fqdn=$HOSTNAME --test --no-splay --server=server0.example.com --onetime --verbose --factsync echo "type enter to continue" read enter_key chvt 1Using this method, if the puppetmaster is down when this client goes to execute puppetd, it will fail but puppet will still be configured and will work when the machine is rebooted. I prefer to use Method 2 just in case.
At this point your clients are installing from scratch and getting configured by puppet automatically from kickstart. The bulk of the work is done, what you now need to do is go through your machine configurations and translate all the changes into puppet. This may take a while, but in the end it is well worth it. There may be some changes that are difficult to translate into puppet, for those hard to make changes that require some advanced sed/awk work, augeas may be the answer. We'll look at that in the next section.
For making changes on the fly (not waiting for puppet clients to check-in) you will need to configure func (or use cluster ssh or something similar). Now, on to augeas.