← Back

Getting Started With Puppet Server and Agents

Note: At the time of writing the latest version of open-source Puppet is 5.5, therefore in this article I am going to talk about this version only.

Here you will find an introduction to basic puppet setup, which would explain all the key points of how the work of the puppet master and slaves is organized. This article is best suitable for reading and doing at the same time. For that you will need two machines to represent the server and the agent based on CentOS 7. If you don't know how to do that, please refer to this article: Virtualization basics and an introduction to KVM and then come back here.

Puppet features master-slave architecture or, as some might call it, server-agent architecture. Indeed, some do call it so – even Puppet's own terminology refers to them as Puppet Server and Puppet agent.

Installation of the Puppet Platform

Both the server and the agent are obtained from the same Puppet Platform repository [1]. To perform the installation execute the following command:

# rpm -Uvh https://yum.puppet.com/puppet5/puppet5-release-el-7.noarch.rpm

Setting up the server

Now that we have the repository installed, let's begin installing Puppet Server [2]:

# yum install puppetserver

Since we are working on a CentOS machine, the package includes a systemd unit file for Puppet Server's service, which should be enabled and started:

# systemctl enable puppetserver
# systemctl start puppetserver

Apart from that, a firewalld service is also included in the distribution so the port on which Puppet Server listens for incoming connections from agents (8140) can be open by adding a puppetmaster service:

# firewall-cmd --add-service --permanent puppetmaster
# firewall-cmd --reload

Setting up the agent and connecting it with the server

After the successful installation of the server, the next step would be to install and setup our agent. Again, first Puppet Platform repository needs to be installed and then:

# yum install puppet-agent

After the required package has been installed, we need to specify where our puppet server is. The best way to do that would be to make your puppet server resolvable as puppet. This can be achieved by having both our machines in one domain and creating a DNS A-record with puppet subdomain for the server. For example, in my setup, the server is puppet.natucci.de (to be clear, it is actually dns.natucci.de, but also has another A-record puppet.natucci.de pointing to the same IP-address) and the agent is ipa.natucci.de. This allows me not to do anything in order to enable the agent to discover the server.

If you are following the instructions from the article I menioned in the beginning, you are using libvirt and so you should be able to use dnsmasq as your DNS – please refer to article under note number [5]. Otherwise, as a last resort, you can opt to specifying the IP-address to hostnames mapping in /etc/hosts and that is also sufficient.

After the server was made discoverable for our agent, we need to start the connection process of the two. For that, simply run puppet agent manually:

# puppet agent -t

Once this is done, the host should be listed among others that await their processing. This can be checked on the server by typing:

# puppet cert list --all

The host is represented by its X509 certificate, validity of which can be verified on our server. Type puppet cert print <agent's hostname> and compare to the actual certificate of the agent – openssl x509 -text -in /etc/puppetlabs/puppet/ssl/certs/<agent's hostname>.pem [4]. If the certificates match, you are safe to sign the certificate request:

# puppet cert sign <agent's hostname>

After signing, you will probably want to do another test run of the agent (puppet agent -t) to make sure the connection process has finished successfully, and enable the service that will do puppet runs automatically in the future:

# systemctl enable puppet

To see the service logs, use journalctl:

# journalctl -u puppet

Writing our first module: quick tips before we start

At this point we have a working puppet setup – let's write our first module. But before we begin, I want to show a few useful commands that can come in handy when preparing and debugging a manifest.

  1. First of all, there is puppet apply. With it you can debug puppet's language statements almost interactively without packaging a module:
# puppet apply --execute 'file { "/tmp/february": ensure => present }'
Notice: Compiled catalog for dns.natucci.de in environment production in 0.01 seconds
Notice: /Stage[main]/Main/File[/tmp/february]/ensure: created
Notice: Applied catalog in 0.09 seconds

Default action for puppet apply is to apply the content of a file, by the way. Let's put the same code in /tmp/manifest.pp:

# puppet apply /tmp/manifest.pp
Notice: Compiled catalog for dns.natucci.de in environment production in 0.01 seconds
Notice: Applied catalog in 0.08 seconds

Nothing happened because the file is still where it was after the last to last run – success.

  1. There is a quick way to describe an existing resource present on the system:
# puppet resource file /root/.vimrc

file { '/root/.vimrc':
  ensure   => 'file',
  content  => '{md5}2d92627a3a68889e8bcea951a6a1a258',
  ctime    => '2018-09-21 16:14:37 +0200',
  group    => 0,
  mode     => '0644',
  mtime    => '2018-09-18 15:01:30 +0200',
  owner    => 0,
  selrange => 's0',
  selrole  => 'object_r',
  seltype  => 'admin_home_t',
  seluser  => 'system_u',
  type     => 'file',
}

# puppet resource service puppetserver

service { 'puppetserver':
  ensure => 'running',
  enable => 'true',
}

That can be useful when you want to see what attribute a given resource has, and what the current values of the attributes are. The command is extra useful because it prints out the state of a resource in puppet language, so that if you want to write a resource of your own, you can base it off an existing one by using puppet resource and then modifying the output accordingly.

  1. puppet config print – outputs the effective configuration, which lets you understand why sometimes something works not the way you had expected it to (maybe using wrong environment?)

Writing our first module: generating, validating and packaging

Now, there is an exceptional step-by-step guide available on access.redhat.com on how to write a puppet module. Please refer to the chapter two in the article available in the footnote [6]. And because it is so great, here I will only emphasize the most important steps that the process of building a module consists of:

  1. First of all, the module needs to be generated by using puppet module generate <module-name>.

  2. As you write your manifests, there will be a need to ensure that the code you have written is syntactically and semantically correct. There are two main ways to check syntax: puppet-lint <file-or-path> and puppet parser validate <file>. The first command is distributed in a form of a ruby gem and in CentOS can be obtained from the default repository as rubygem-puppet-lint package [7]. The latter command is built-in in puppet. The semantics can be validated by writing tests with rspec. [8]

  3. Finished puppet module needs to be built with puppet module build <path>. This command creates a pkg sub-directory containing an archive that is ready to be installed to the puppet server with puppet module install <path-to-module>.

These were some of the things that I discovered and found useful myself. But there is a whole website devoted to the knowledge every puppet master should be at least aware of: Puppet Cookbook [12] – an exceptionally useful website.

Assigning classes to nodes

There are different ways of assigning classes to nodes. One of them is through site.pp. [9]

It can be done in global one: /etc/puppetlabs/puppet/manifests/site.pp or specific to the environment: /etc/puppetlabs/code/environments/production/manifests/site.pp

[root@dns ~]# head /etc/puppetlabs/code/environments/production/manifests/site.pp
node 'dns.natucci.de' {
  class {'cloud::dns':} ->
  class {'cloud::default':} ->
  class {'hdp::default':} ->
  class {'hdp::server':}
}

Resources

  1. https://puppet.com/docs/puppet/5.5/puppet_platform.html

  2. https://puppet.com/docs/puppetserver/5.3/install_from_packages.html

  3. https://puppet.com/docs/puppet/5.5/install_linux.html

  4. http://www.masterzen.fr/2010/11/14/puppet-ssl-explained/

  5. https://liquidat.wordpress.com/2017/03/03/howto-automated-dns-resolution-for-kvmlibvirt-guests-with-a-local-domain/

  6. https://access.redhat.com/documentation/en-us/red_hat_satellite/6.1/html/puppet_guide/chap-red_hat_satellite-puppet_guide-building_puppet_modules_from_scratch

  7. http://puppet-lint.com/

  8. https://puppet.com/blog/verifying-puppet-checking-syntax-and-writing-automated-tests

  9. https://github.com/puppetlabs/docs-archive/blob/master/pe/3.3/puppet_assign_configurations.markdown#assigning-configuration-data-with-the-site-manifest-sitepp

  10. https://puppet.com/docs/puppet/5.5/environments_creating.html

  11. http://www.masterzen.fr/2010/11/14/puppet-ssl-explained/

  12. https://www.puppetcookbook.com/