Puppet

What is Puppet?

Puppet is a configuration management tool written in Ruby. It works across multiple Unix systems and Linux distributions.

Puppet can be found here. A small summary of features is listed here.

Why do we need Puppet (or any other configuration management software)?

Start here. This is a fairly good introduction to the concepts of why configuration management is useful. Also see for why configuration management is necessary to scale up (and out).

Philosophy of Puppet.

Quoting from the Puppet documentation:
Puppet is a declarative language for expressing system configuration, a client and server for distributing it, and a library for realizing the configuration. Rather than approaching server management by automating current techniques, Puppet reframes the problem by providing a language to express the relationships between servers, the services they provide, and the primitive objects that compose those services. Rather than handling the detail of how to achieve a certain configuration or provide a given service, Puppet users can simply express their desired configuration using the abstractions they're used to handling, like service and node, and Puppet is responsible for either achieving the configuration or providing the user enough information to fix any encountered problems.

Learning more about Puppet

Introduction to Puppet.

Getting started with puppet

Introduction

Puppet is a command-line, client-server application.
Puppet consists of the following executables:
  1. puppet Stand alone Puppet Site Manifest Script evaluator. Parses, evaluates, and implements a Site Manifest locally.
  2. puppetmasterd Puppet Site Manifest Server Daemon. Runs on each host serving/providing configurations to Puppet client hosts.
  3. puppetd Puppet Client Host Configuration Daemon. Runs on each host whose configuration is managed by Puppet. Requests a Host Specific Manifest from a Puppet Manifest Server and implements it locally.
  4. puppetca SSL Certificate Authority Server used for receiving cerification requests from puppet clients. Puppet Client Hosts are required to have their SSL certs signed by their Puppet Manifest Server's Authority before they can be authenticated and authorized to receive configurations.
  5. puppetdoc Command line tool for printing Puppet Default and Local Type Library reference documentation. This is really only used internally

Puppet itself depends on a small utility named facter.
Binary RPMs for Puppet can be found here.

While it is easiest to test Puppet by running both client and server on the same host, in the more general case, we will be running it on multiple hosts, with a central server and multiple clients. The rest of the documentation assumes that you have two systems (even if one is in a virtual machine) available to test and try out puppet.

To start with, we will look at deploying a hosts file on our client node.

Steps for deployment.

Install the puppet and facter packages on both hosts.
Choose one as your central server. From now on, this host will be referred to in the documentation as puppetmaster. The other host will be referred to as puppet-client
If you have installed from the RPM, your default configuration will be in /etc/puppet.
Per host/site configurations are referred to as manifests.
The default manifest for your puppet server is /etc/puppet/manifests/site.pp
Create the default manifest (touch /etc/puppet/manifests/site.pp).
We will put specific manifests in a separate directory within /etc/puppet/manifests. This allows us to simplify configuration details and make the configuration more explicit (mkdir /etc/puppet/manifests/classes).

Include the entire classes directory in your manifest. This is a relative path to site.pp.
At the top of site.pp, add:
import "classes/*"
Create a basic configuration for the client.
Edit a file named /etc/puppet/manifests/classes/hosts.pp (We use descriptive names for the file, which tells us what exists there).
# This class is a standard entry in all hosts.
class host-standard {
        host { "local":
                name    => "localhost",
                alias   => "localhost.localdomain",
                ip      => "127.0.0.1",
                ensure  => present,
        }
}
Create a default manifest for your client in site.pp
node puppet-client {
        include host-standard
}
Start the puppetmasterd process on puppetmaster:
/etc/init.d/puppetmasterd start
If you see any errors, you have to check syslog for the cause and fix it.
Start puppetd on puppet-client:
puppetd --verbose --server puppetmaster

You will have to use a valid DNS name or IP addresses instead of puppetmaster.

Puppet uses SSL for bidirectional authentication and authorization.Ensure that the clocks on both systems are synchronised and correct. On the client, you should see a message about not receiving a certificate.
On the server, we'll list the certificates waiting for signatures:
puppetca --list
This should print puppet-client

Sign the certificate on the server:
puppetca --sign puppet-client
On puppet-client you should see a message about the /etc/hosts file being updated
/usr/sbin/puppetd --verbose --server puppet
notice: Starting Puppet client version 0.22.2
info: Facts have changed; recompiling
info: Caching configuration at /var/lib/puppet/localconfig.yaml
info: /Host[local]: Adding aliases "localhost.localdomain"
notice: Starting configuration run
notice: Finished configuration run in 0.02 seconds
This completes a basic Puppet installation.

Now, we will add puppet-client specific hosts entries to the /etc/hosts file. Edit hosts.pp on puppetmaster and add:

class puppet-client-host {
        host { "puppet-client":
                name    => "puppet-client",
                alias   => "puppet-client.internal.example.com",
                ip      => "192.0.20.1",
                ensure  => present,
        }
        host { "postfix":
                name    => "postfix",
                alias   => "postfix.example.com",
                ip      => "203.199.107.58",
                ensure  => present,
        }
        host { "postfix-us":
                name    => "postfix-us",
                alias   => "postfix-us.example.com",
                ip      => "67.15.238.68",
                ensure  => present,
        }
        host { "postfix-us2":
                name    => "postfix-us2",
                alias   => "postfix-us2.example.com",
                ip      => "67.15.253.249",
                ensure  => present,
        }
}
Edit site.pp on puppetmaster
node puppet-client {
        include host-standard
        include puppet-client-host
}
Run puppetd on puppet-client puppetd --verbose --server puppetmaster

Installing packages

Puppet will manage packages on multiple operating systems and Linux distributions. See the package type for more information.
Puppet will automatically guess the packaging format that you are using based on the platform you are on, but you can override it using the provider parameter; each provider defines what it requires in order to function, and you must meet those requirements to use a given provider.

We setup a class like this on puppetmaster:

class apache-2 {
        package { "httpd":
                ensure => "latest",
       }
}
It is possible to put each class in it's own file, or group them all into one file. A mixture of the two is also feasible.

In site.pp

host puppet-client {
    include apache-2
}

Serving static files

Puppet internally supports a limited number of filetypes. To serve files which Puppet does not natively understand yet, it has the ability to serve files over a XMLRPC interface. This is a slightly slower fileserving technique, but has the advantage of the client not needing to know the organisation of files on the server.

The official documentation is here.

Make a separate directory in /etc/puppet/manifests/ to store static files. This is for the sake of convenience, and not mandatory. Any other suitable location can also be used.
mkdir /etc/puppet/manifests/files/

Edit /etc/puppet/fileserver.conf on puppetmaster and make an entry similar to this one:

[etc]
    path /etc/puppet/manifests/files
    allow 192.168.0.0/24
Puppet's fileserver.conf is modelled after the rsync configuration file. The label etc is how the puppetmasterd identifies the location of the actual file on disk.

Puppet accesses remote files via the puppet:// scheme.
Puppet's URIs work in the following way:
The URI format is puppet://server/label/path/to/file/under/label
This works in a similar fashion to Apache's DocumentRoot directive. So if we were to make a file named syslog.conf in /etc/puppet/manifests/files/, it would be accessible via the URI puppet://puppetmaster/etc/syslog.conf

If you want to make a path dependent on the client hostname, use %h in the fileserver.conf path directive path /etc/puppet/manifests/files/%h In this case, you would have to make the file(s) available under a directory with the same hostname as the client, ie, /etc/puppet/manifests/files/puppet-client/syslog.conf

We setup a class like this:

class standard-syslog {
        file { "/etc/syslog.conf":
                source  => "puppet://puppetmaster/etc/syslog.conf",
                owner   => "root",
                group   => "root",
                mode    => "644",
        }
}
In site.pp
host puppet-client {
    include standard-syslog
}
When puppetd runs on the client, the syslog.conf file will be transferred.

User management

Puppet can be used to manage system users as well. On Linux, Puppet invokes useradd on Linux to create the users. Use a class like this:
class localusers {
        user { "f3ew":
                ensure  => "present",
                comment => "test user",
        }
        user { "dvb":
                ensure  => "present",
                comment => "test user",
                home    => "/home/dvb",
        }
}
See here for options on parameters to the users class. In site.pp:
host puppet-client {
        include localusers
}

Testing

To test the configuration, run puppetd --test --noop on the client,
#puppetd --test --noop
notice: Ignoring --listen on onetime run
notice: Ignoring cache
info: Caching configuration at /var/lib/puppet/localconfig.yaml
info: /Host[local]: Adding aliases "localhost.localdomain"
info: /Host[puppet-client]: Adding aliases "puppet-client.internal.example.com"
info: /Host[postfix]: Adding aliases "postfix.example.com"
info: /Host[postfix-us]: Adding aliases "postfix-us.example.com"
info: /Host[postfix-us2]: Adding aliases "postfix-us2.example.com"
notice: Starting configuration run
notice: //puppet-client/localusers/User[f3ew]/ensure: is absent, should be present (noop)
notice: //puppet-client/apache-2/Package[httpd]/ensure: is 2.0.52-28.ent.centos4, should be 2.0.52-32.3.ent.centos (noop)
notice: Finished configuration run in 11.65 secondsinm

Additional reading

Slightly Debian specific, but still useful:
http://www.debian-administration.org/articles/526
http://www.debian-administration.org/articles/528