I am using puppet since the end of 2012 and I finally found a use case for Hiera. This is my way of implementing it:
What is Hiera
Hiera is split into two parts: The backend is a key/value store that supports hierarchies. The data can be stored in flat files like yaml or in complex RDBMS like postgresql. The other part is the frontend, this are are tools that you use to access the data, for example a shell client or a Hiera lookup function in puppet.
Justification
Why should anybody use Hiera? What are the advantages? There are two reasons for using Hiera. You want to separate your data from code and from business logic. This allows you a higher level of abstraction. Also Hiera supports a hierarchy. this makes it easy for your to automatically assign the correct values to your busines logic. An example: Every of your nodes should report to the same Zabbix master. Except for every node with two two interfaces, they should use their internal interface and IP. And axcept for every node in the management LAN, this should also use the internal IP range but public interface and so on and so on. There is always a big base of nodes that shares the same value but several edge cases that need exceptions. Normally you would use huge case and if statements to determine which node gets which value, Hieras hierarchie makes this obsolet. Let us take a look at the implementation:
Implementation
I’m running r10k on my masters to deploy dynamic environments, I want to bundle Hiera into my controlrepo. hiera.yaml
is a per-environment configuration file which defines the path to the data and the hierarchy. My hiera.yaml
:
--- :backends: - yaml :hierarchy: - clientcert/%{clientcert} - puppet_role/%{puppet_role} - type/%{type} - global :yaml: :datadir: /etc/puppet/environments/%{environment}/hiera
Hiera can work with variables (facts in this case, but unlike to puppet, without :: in front of them), this allows us to point to a Hiera directory in our environment. This directory has a global file(global.yaml
), for the highest lookup, than a type lookup (I’ve got two types of systems, hypervisors and infrastructure nodes, both types highly differ in their data), than a role based directory and a node based one for the deepest lookup (this layout highly depends on your infrastructure, maybe you also need a layer for the location or something else).
$ tree
.
├── documentation
├── hiera
│ ├── clientcert
│ ├── global.yaml
│ ├── puppet_role
│ └── type
├── hypervisors.yaml
│ └── infrastructure.yaml
├── hiera.yaml
├── manifests
│ └── site.pp
└── Puppetfile
I created the infrastructure.yaml as an example file:
--- zabbix_server_ip: '192.168.1.15'
You can now commit everything and redeploy your environment with r10k, after that, you can test Hiera via CLI:
root@puppet-master ~ # hiera zabbix_server_ip environment=hieratest type=infrastructure --config /etc/puppet/environments/hieratest/hiera.yaml --debug
DEBUG: 2015-11-26 16:34:38 +0100: Hiera YAML backend starting
DEBUG: 2015-11-26 16:34:38 +0100: Looking up zabbix_server in YAML backend
DEBUG: 2015-11-26 16:34:38 +0100: Looking for data source type/infrastructure
DEBUG: 2015-11-26 16:34:38 +0100: Found zabbix_server in type/infrastructure
192.168.0.8
root@puppet-master ~ #
To use Hiera in Puppet, I had to symlink the Hiera config:
ln -sf /etc/puppet/environments/hieratest/hiera.yaml /etc/puppet/hiera.yaml ln -sf /etc/puppet/environments/hieratest/hiera.yaml /etc/hiera.yaml
Hiera takes a look at /etc/hiera.yaml
as the default config path, Puppet accepts one global Hiera config which isn’t environment specific, so it has to be at /etc/puppet/hiera.yaml
. You can also enforce the links via Puppet (maybe in your puppetmaster profile):
file{['/etc/hiera.yaml', '/etc/puppet/hiera.yaml']: ensure => 'link', source => '/etc/puppet/environments/production/hiera.yaml', }
(this should create two symlinks, but creates two copies of the original file, not sure if I use the file resource wrong or if it is a bug)
You can also change the path to Puppets Hiera config, than you only need one symlink.
You need to restart your master to accept the new Hiera config. Than you finally can use Hiera in your Puppet code:
class { '::zabbix::agent': server => hiera('zabbix_server_ip'), zabbix_package_state => 'latest', zabbix_version => '2.4', manage_resources => false, serveractive => '10.111.2.50', hostmetadata => $::sach_type, hostmetadataitem => 'system.uname', listenip => $ipaddr, }
Hiera also support “automatic param lookup”, where you specify a class param like this:
class profiles::zabbixagent ( $zabbix_server_ip, ) { class { '::zabbix::agent': server => $zabbix_server_ip, zabbix_package_state => 'latest', zabbix_version => '2.4', manage_resources => false, serveractive => '10.111.2.50', hostmetadata => $::sach_type, hostmetadataitem => 'system.uname', listenip => $ipaddr, } }
And you include that profile in your role:
class roles::awesomethingything{ include ::profiles::beautifulcode include ::profiles::omgthisissonice include ::profiles::zabbixagent }
in your infrastructure.yaml
, you change the explicit key to a class param key:
--- profiles::zabbixagent::zabbix_server_ip: '192.168.1.15'
Summary
Puppet will notice that you included a class via include, without passing any variables. This follows a lookup of the class param in Hiera. The Hiera syntax just needs to be class:subclass::paramoftheclass
. If you use Hiera like this, you don’t need explicit hiera()
calls, but it will be hard to determine where values are coming from.