Puppet - Entry Point
Puppet is a set of utilities written in Ruby that let you manage a large number of servers from one place. It lets you describe the desired state for every server in your infrastructure. Creating new servers no longer means clicking through buttons, using templates/boilerplate, and so on.
The developers put together a neat little VM you can use to get familiar with puppet’s features. I just finished the quests myself and want to sum up the key points.
If you’re looking for an entry point into puppet, I’d recommend going through the materials in <a href="https://puppetlabs.com/download-learning-vm" target="_blank">Learning VM</a> first, then coming back here.
A bit of theory
Manifest - a text file with a .pp extension. Manifests describe classes.
Class - a named block of code. Usually a class defines a resource type tied to some function or component of the system. Classes are described using resources.
Resources - the basic units for building configuration, each describing a specific part of the system. Resources are described in Puppet code using a specific Ruby-based language - DSL (‘Domain Specific Language’). The developers and marketing people didn’t overthink the name.
A manifest consists of classes. Classes are described using resources. In manifests and classes you can use variables, conditional operators, and pass arguments to them.
You can get a list of all resources with the following command:
puppet resource -types
View info about a resource (the user resource with the value root):
puppet resource user root
Get a description of a resource:
puppet describe user less
Module - this is what lets you organize Puppet code into separate configuration units for later use. Most modules live in the directory listed as modulepath in the /etc/puppetlabs/puppet/puppet.conf file
You can find out modulepath by running the following command:
puppet agent -configprint modulepath
Puppet looks for modules first in the /etc/puppetlabs/puppet/environments/production/modules folder, then in the /etc/puppetlabs/puppet/modules folder, and finally in the /opt/puppet/share/puppet/modules folder
You can install a module with the following command:
puppet module install puppetlabs-apache
The gist of working with puppet is creating modules with manifests.
Simple manifest examples are covered in the Learning VM quests.
To check out a module’s structure run:
tree -L 2 -d /etc/puppetlabs/puppet/environments/production/modules/apache
You can dig through the contents in more detail.
Classes are described in the following file (this is already a manifest):
manifests/init.pp
You can validate a manifest with the following command:
puppet apply -noop manifests/init.pp
Without the --noop flag the manifest gets applied to the Puppet configuration.
After that the class becomes available in Puppet’s web UI, where you can define which server a given class applies to. Based on that, a given config will (or won’t) get applied to the server.
The most important manifest lives here:
/etc/puppetlabs/puppet/environments/production/manifests/site.pp
This file describes the classes and variables that apply to every server in your fleet. For example, for ssh access to all servers you use the opsadmin user with a key (id_rsa). It’d make sense to include the class that creates this user into the main manifest, so you don’t have to add this class by hand in puppet’s web UI for every new server.
The same goes for ssh settings. They’re most likely going to be the same across all servers.
Example of creating a manifest
I’ll copy the ssh example from Learning VM.
Create the module’s folders:
mkdir -p /etc/puppetlabs/puppet/environments/production/modules/sshd/{manifests,files}
Create the manifest:
vim /etc/puppetlabs/puppet/environments/production/modules/sshd/manifests/init.pp
class sshd {
package {'openssh-server':
ensure => present,
before => File["/etc/ssh/sshd_config"],
}
file { '/etc/ssh/sshd_config':
ensure => file,
mode => 600,
source => 'puppet:///modules/sshd/files/sshd_config',
}
service {'sshd':
ensure => running,
enable => true,
subscribe => File['/etc/ssh/sshd_config'],
}
}
Create the config that’ll be used on all your servers:
vim /etc/puppetlabs/puppet/environments/production/modules/sshd/files/sshd_config
I won’t include the actual content. Everyone uses settings that match their company’s policy.
Open the main manifest:
vim /etc/puppetlabs/puppet/environments/production/manifests/site.pp
Find the section:
node default {
...
}
Add to it:
include sshd
Verify:
puppet apply -noop /etc/puppetlabs/puppet/environments/production/manifests/site.pp
If everything’s OK - repeat the previous command without the --noop flag.
As for pushing changes out to existing servers, that happens on a schedule every 30 minutes. To speed that up you can kick the puppet agent:
puppet agent -t
Conditional operator:
Puppet manifests allow using the following conditional operators:
- if
- case
- switch
You need conditionals if only because your fleet might have both Ubuntu and Centos servers. The VM (Learning VM) covers an example of adding users to different groups depending on the operating system.
So what do you use in conditional operators? - Variable values. To get a list of variable values you can work off of, use the facter utility
Run:
facter -p less
Take a closer look at the output. There’s a lot of useful stuff there.
In this example we care about the operatingsystem or osfamily values.
In manifest code you can reference them like this:
$::operatingsystem == 'centos'
$::osfamily = 'RedHat'
Example #2
Let’s create a manifest that creates the opsadmin user with a public key for ssh access. The user will be added to the wheel group on CentOS servers, and to the admin group on Ubuntu servers
Create the module’s folders:
mkdir -p /etc/puppetlabs/puppet/environments/production/modules/accounts/{manifests,files}
Create the manifest:
vim /etc/puppetlabs/puppet/environments/production/modules/accounts/manifests/init.pp
class accounts ($name) {
if $::operatingsystem == 'centos' {
$groups = 'wheel'
}
elsif $::operatingsystem == 'debian' {
$groups = 'admin'
}
user { $name:
ensure => 'present',
home => "/home/${name}",
groups => $groups,
}
file { '/home/${name}/.ssh/authotized_keys':
ensure => file,
mode => 600,
source => 'puppet:///modules/accounts/${name}-authotized_keys',
}
}
Create the file containing the opsadmin user’s public key
/etc/puppetlabs/puppet/environments/production/modules/accounts/files/opsadmin-authotized_keys
Open the main manifest:
vim /etc/puppetlabs/puppet/environments/production/manifests/site.pp
Find the section:
node default {
...
}
Add to it:
class {'accounts':
name => 'opsadmin',
}
Verify:
puppet apply -noop /etc/puppetlabs/puppet/environments/production/manifests/site.pp
If everything’s OK - repeat the previous command without the --noop flag.
Overall the manifest turned out pretty universal since it lets you add many users with sudo rights. By the same logic you can add users to other groups. For example add the dev team to the developers group. User keys need to go into the following folder:
/etc/puppetlabs/puppet/environments/production/modules/accounts/files/
File names should start with the username.
Conclusion:
From these examples it’s not hard to see the benefits of using Puppet. Imagine you have a dev team of 20 people working on projects across 40 servers. When a new person joins you need to add them to all the servers. With Puppet you can do that in a matter of minutes. It’ll take the same amount of time to add a person to 400 servers.
You don’t need to manually apply an ssh config change on every server if the encryption policy or something else changes.
I’ll keep playing around with manifests as I find free time, and post them in separate articles.
