Puppet Installables – PuppetDB and Hiera

Welcome back to our Puppet for vSphere Admins series. We started out deploying the puppet master and a few example manifests, then took a right turn into the land of Git and workflows. I know you’re anxious to get back to developing manifests, but we’ve got a few more things to install on the master before we worry about the manifests. PuppetDB and Hiera, and MCollective are powerful tools that most administrators will find of immense benefit. We can install these later, but we’d have to redo some of our work. Who wants to do that?

As I mentioned last week, I’ll assume you’re using r10k at some level, so I’ll mostly just reference “use r10k” unless there’s a specific gotcha. If you’re not using r10k, follow whatever workflow you’ve decided on to add modules, update manifests, and track all changes in your VCS.

PuppetDB

First, we’ll install PuppetDB. PuppetDB allows us to work with exported resources and the inventory service. All of the agents that will eventually connect to the puppet master will have facts that we care about – facts like hostname, ips, operating system, mac addresses, the kernel version, etc. You can view the facts by typing facter on the master. However, all of these facts are local to the agents. Using PuppetDB, these facts will be stored and made available to others. Exported resources allow us to mark facts that we want other agents to have access to, such as a node’s public SSH key. The inventory service is an API for services to see the fact, such as Puppet Dashboard or your own software that leverages the API.

PuppetDB can be installed from source, rpm, or via Puppet itself. We’ll use the latter. We need the module puppetlabs/puppetdb and will add the class to the master’s node definition. To get started, let’s create a new branch of the puppet repo based on the production branch (installables). If you’re using r10k, add the module (and dependencies, like puppetlabs/postgresql) to Puppetfile.

mod "puppetlabs/puppetdb"
mod "puppetlabs/postgresql"

Otherwise use puppet module install puppetlabs/puppetdb. Add these lines to the master’s node definition:

  include ::puppetdb
  include ::puppetdb::master::config

Deploy your changes and run a noop test. You should see the package installation, service configuration, postgresql installation, and a modification to use the puppetdb in puppet.conf. It will look something like this – with a lot of errors because of the noop, about failed dependencies, but they should all be tied to postgresql or puppetdb:

Notice: /Stage[main]/Postgresql::Server::Config/Postgresql::Server::Config_entry[listen_addresses]/Postgresql_conf[listen_addresses]/ensure: current_value absent, should be present (noop)
Info: /Stage[main]/Postgresql::Server::Config/Postgresql::Server::Config_entry[listen_addresses]/Postgresql_conf[listen_addresses]: Scheduling refresh of Class[Postgresql::Server::Service]
Notice: Postgresql::Server::Config_entry[listen_addresses]: Would have triggered 'refresh' from 1 events
Notice: /Stage[main]/Main/Node[puppet.nelson.va]/Notify[Generated from our notify branch]/message: current_value absent, should be Generated from our notify branch (noop)
Notice: Node[puppet.nelson.va]: Would have triggered 'refresh' from 1 events
Notice: Class[Main]: Would have triggered 'refresh' from 1 events
Notice: /Stage[main]/Postgresql::Server::Config/Concat[/var/lib/pgsql/data/pg_hba.conf]/File[/var/lib/puppet/concat/_var_lib_pgsql_data_pg_hba.conf]/ensure: current_value absent, should be directory (noop)
...

...
Notice: /Stage[main]/Puppetdb::Master::Puppetdb_conf/Ini_setting[puppetdbserver]: Dependency Exec[concat_/var/lib/pgsql/data/pg_hba.conf] has failures: true
Warning: /Stage[main]/Puppetdb::Master::Puppetdb_conf/Ini_setting[puppetdbserver]: Skipping because of failed dependencies
Notice: /Stage[main]/Puppetdb::Master::Config/Service[puppetmaster]: Dependency Exec[concat_/var/lib/pgsql/data/pg_hba.conf] has failures: true
Warning: /Stage[main]/Puppetdb::Master::Config/Service[puppetmaster]: Skipping because of failed dependencies
Notice: Class[Puppetdb::Master::Config]: Would have triggered 'refresh' from 1 events

If everything went well, run it again without the noop. This will be very noisy and take a while, there’s a LOT of moving parts here. You’ll likely see some “errors” about connection refused, but don’t fret, it just takes a few moments for the services to start up. No errors should show up in the lengthy output. Run one more agent checkin (one change expected that I’ll explain later, but we want some logs) and then you can check that everything went well by checking that the packages are installed, processes are running, and that logs are showing connections:

[root@puppet ~]# rpm -qa | grep puppetdb
puppetdb-1.6.3-1.el6.noarch
puppetdb-terminus-1.6.3-1.el6.noarch
[root@puppet ~]# rpm -qa | grep postgresql
postgresql-libs-8.4.20-1.el6_5.x86_64
postgresql-server-8.4.20-1.el6_5.x86_64
postgresql-8.4.20-1.el6_5.x86_64
[root@puppet ~]# ps -ef | grep puppetdb
puppetdb  6370     1  6 18:53 ?        00:00:39 /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/java -XX:OnOutOfMemoryError=kill -9 %p -Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/puppetdb/puppetdb-oom.hprof -jar /usr/share/puppetdb/puppetdb.jar services -c /etc/puppetdb/conf.d
postgres  6442  6115  0 18:53 ?        00:00:00 postgres: puppetdb puppetdb 127.0.0.1(50875) idle
postgres  6443  6115  0 18:53 ?        00:00:00 postgres: puppetdb puppetdb 127.0.0.1(50876) idle
...
[root@puppet ~]# puppet agent --test --environment=installables
Info: Applying configuration version '1398020510'
Notice: /Stage[main]/Ssh::Hostkeys/Sshkey[puppet.nelson.va_dsa]/ensure: created
Notice: /Stage[main]/Ssh::Client::Config/File[/etc/ssh/ssh_known_hosts]/mode: mode changed '0600' to '0644'
Notice: Generated from our notify branch
Notice: /Stage[main]/Main/Node[puppet.nelson.va]/Notify[Generated from our notify branch]/message: defined 'message' as 'Generated from our notify branch'
Notice: /Stage[main]/Ssh::Hostkeys/Sshkey[puppet.nelson.va_rsa]/ensure: created
Notice: Finished catalog run in 4.09 seconds
[root@puppet ~]# tail /var/log/puppetdb/puppetdb.log
2014-04-20 18:53:53,352 INFO  [pool-3-thread-1] [cli.services] Starting database garbage collection
2014-04-20 18:53:53,421 INFO  [pool-3-thread-1] [cli.services] Finished database garbage collection
2014-04-20 18:53:53,451 INFO  [pool-3-thread-1] [cli.services] Starting sweep of stale reports (threshold: 14 days)
2014-04-20 18:53:53,488 INFO  [pool-3-thread-1] [cli.services] Finished sweep of stale reports (threshold: 14 days)
2014-04-20 18:53:53,511 INFO  [clojure-agent-send-off-pool-2] [server.Server] jetty-7.x.y-SNAPSHOT
2014-04-20 18:53:54,073 INFO  [clojure-agent-send-off-pool-2] [server.AbstractConnector] Started SelectChannelConnector@localhost:8080
2014-04-20 18:53:54,434 INFO  [clojure-agent-send-off-pool-2] [ssl.SslContextFactory] Enabled Protocols [SSLv2Hello, SSLv3, TLSv1, TLSv1.1, TLSv1.2] of [SSLv2Hello, SSLv3, TLSv1, TLSv1.1, TLSv1.2]
2014-04-20 18:53:54,466 INFO  [clojure-agent-send-off-pool-2] [server.AbstractConnector] Started SslSelectChannelConnector@puppet.nelson.va:8081
2014-04-20 19:01:50,312 INFO  [command-proc-51] [puppetdb.command] [7a2fdda7-f44a-4e57-be76-f20640775a52] [replace facts] puppet.nelson.va
2014-04-20 19:02:00,903 INFO  [command-proc-51] [puppetdb.command] [6295434a-af80-4267-9e38-9bd48dfe00f7] [replace catalog] puppet.nelson.va

Now with PuppetDB installed, you have access to exported resources. You get one for free: a resource of the type SSH::Hostkeys. We get this via the saz/sudo module we installed at the beginning of this series. You can see the Puppet DSL code used here. We’ll get into how to use the resources at a later date.

Hiera

Hiera is a different install, a little easier in some ways and more difficult in others. The first part is the easy part, installing two packages, hiera and hiera-puppet from the Puppet repos. Just add the resources to the node definition. This gives us:

node 'puppet.nelson.va' {
  include ::base
  notify { "Generated from our notify branch": }

  # PuppetDB
  include ::puppetdb
  include ::puppetdb::master::config

  # Hiera
  package { ['hiera', 'hiera-puppet']:
    ensure => present,
  }
}

Modern puppet includes the hiera functionality by default. However, it’s good to explicitly state our requirements for the puppet master. When you run puppet again, you may not notice any changes since the components are already installed. If you do rebuild the master, this will ensure Hiera is installed.

Depending on your versions, you may receive an error, though:

Error: Execution of '/usr/bin/yum -d 0 -e 0 -y install hiera-puppet' returned 1:

Transaction Check Error:
  file /usr/bin/extlookup2hiera from install of hiera-puppet-1.0.0-1.el6.noarch conflicts with file from package puppet-3.4.3-1.el6.noarch
  file /usr/lib/ruby/site_ruby/1.8/hiera/backend/puppet_backend.rb from install of hiera-puppet-1.0.0-1.el6.noarch conflicts with file from package puppet-3.4.3-1.el6.noarch
  file /usr/lib/ruby/site_ruby/1.8/hiera/scope.rb from install of hiera-puppet-1.0.0-1.el6.noarch conflicts with file from package puppet-3.4.3-1.el6.noarch
  file /usr/lib/ruby/site_ruby/1.8/hiera_puppet.rb from install of hiera-puppet-1.0.0-1.el6.noarch conflicts with file from package puppet-3.4.3-1.el6.noarch
  file /usr/lib/ruby/site_ruby/1.8/puppet/parser/functions/hiera.rb from install of hiera-puppet-1.0.0-1.el6.noarch conflicts with file from package puppet-3.4.3-1.el6.noarch
  file /usr/lib/ruby/site_ruby/1.8/puppet/parser/functions/hiera_array.rb from install of hiera-puppet-1.0.0-1.el6.noarch conflicts with file from package puppet-3.4.3-1.el6.noarch
  file /usr/lib/ruby/site_ruby/1.8/puppet/parser/functions/hiera_hash.rb from install of hiera-puppet-1.0.0-1.el6.noarch conflicts with file from package puppet-3.4.3-1.el6.noarch
  file /usr/lib/ruby/site_ruby/1.8/puppet/parser/functions/hiera_include.rb from install of hiera-puppet-1.0.0-1.el6.noarch conflicts with file from package puppet-3.4.3-1.el6.noarch

Error Summary
-------------

That’s correct, there’s no Error Summary. This error appears to be because the hiera-puppet version has a conflict with puppet 3.4.2. Upgrading to 3.4.3 or higher resolved this issue.

The second, more difficult part, is creating a hiera.yaml file. You can take the easy way out by relying on the default file provided with Hiera with a single edit on the last line and the creation of a directory:

[root@puppet ~]# cat /etc/hiera.yaml
---
:backends:
  - yaml
:hierarchy:
  - defaults
  - "%{clientcert}"
  - "%{environment}"
  - global

:yaml:
# datadir is empty here, so hiera uses its defaults:
# - /var/lib/hiera on *nix
# - %CommonAppData%\PuppetLabs\hiera\var on Windows
# When specifying a datadir, make sure the directory exists.
  :datadir: /etc/puppet/data
[root@puppet puppet-tutorial]# mkdir /etc/puppet/data

In the long run, you’re going to need to do a bit more configuration. There are a lot of options here, you can define the hierarchy any way you want. The first match in the hierarchy is used, so the most specific settings should be found near the top, such as in the clientcert. and the least specific settings are found at the bottom, in global. If nothing matches, then puppet will move from Hiera to manifest files. This gives us the flexibility to move values from manifests to Hiera over time and not lose functionality or node definitions. Here’s an example of a very, very specific hierarchy:

:hierarchy:
  - environments/%{environment}/data/fqdn/%{fqdn}
  - environments/%{environment}/data/osfamily/%{osfamily}/%{lsbdistcodename}
  - environments/%{environment}/data/osfamily/%{osfamily}/%{lsbajdistrelease}
  - environments/%{environment}/data/osfamily/%{osfamily}/%{architecture}
  - environments/%{environment}/data/osfamily/%{osfamily}/common
  - environments/%{environment}/data/modules/%{cname}
  - environments/%{environment}/data/modules/%{caller_module_name}
  - environments/%{environment}/data/modules/%{module_name}
  - environments/%{environment}/data/common

It’s definitely up to you to define the hierarchy of your own setup. You probably don’t have enough information to make that decision now, but we’ll get to that soon enough. Just keep in mind that it’s top to bottom, first match wins, and manifests are a last resort until we come back to this.

What’s in these files? It depends on the backend specified in /etc/hiera.yaml. Since we’re using yaml, each file is YAML, has the suffix of “.yaml”, and is located in :datadir:. Let’s create the global.yaml file first. We’re going to define one key, puppetmaster, and the FQDN of our master will be the value.

[root@puppet ~]# cat > /etc/puppet/data/global.yaml
---
puppetmaster: 'puppet.nelson.va'

You can test that Hiera is working with the hiera command-line utility. Provide it a key and it will produce the matching value if it is defined and ‘nil’ otherwise:

[root@puppet ~]# hiera puppet
nil
[root@puppet ~]# hiera puppetmaster
puppet.nelson.va

There’s one last thing: the hiera command line program looks at /etc/hiera.yaml but puppet looks at $confdir/hiera.yaml. Check what $confdir evaluates to on your system and create a symlink between the two. If you skip this step, everything will look fine at the CLI but any manifests relying on hiera will fail to compile.

[root@puppet ~]# puppet config print confdir
/etc/puppet
[root@puppet ~]# ln -s /etc/hiera.yaml /etc/puppet/hiera.yaml

All we needed to do today is make sure Hiera is installed and working. We will get into the innards another day. There are other backends available – file, JSON, MySQL, and gpg – if you want to start looking ahead.

MCollective

There’s one more installable component that I want to cover, MCollective. However, the process is longer than what we already discussed today, so you’ll have to tune in next time for the completion of Installables.

One thought on “Puppet Installables – PuppetDB and Hiera

  1. Pingback: Hiera-fy your Hiera setup | rnelson0

Leave a comment