For my job I do a lot of Puppet and I thought it was about time to write some tips and tricks down.
First part of this post is about my environment setup. In my test setup
I use a lot of environments. They are not at all useful, but that’s not
the point. It’s my lab environment so things need to break once in a
while. But with multiple environments Puppetlabs says that you should
switch to directory environments
(PuppetDoc)
but some way or another I cannot get that to work in a good way with my
PE version (3.4.3 (Puppet Enterprise 3.2.3)
). So I started
implementing dynamic environments, which is a simple way of specifying
the directories for your environments.
Part of my puppet.conf
looks like
[master] environment = production manifest = $confdir/environments/$environment/manifests/site.pp manifestdir = $confdir/environments/$environment/manifests modulepath = $confdir/environments/$environment/modules:/usr/share/puppet/modules templatedir = $confdir/environments/$environment/templates
So, my default environment is production
and a client can specify
another environment to be in. The command
puppet agent --environment=test
would place this node in the test
environment. A simple module places
a new puppet.conf
file on the client stating this new environment.
Couldn’t be more simple.
Well, that’s what you think. But what if you need to deploy 10.000+
hosts of which there are about a third in environment test
and about a
1000 in environment development
? It would take a lot of time to ssh
into all these servers and run Puppet with the correct environment.
There has to be a way around that. And, of course, there is. In Puppet
version 3 and up Hiera is integrated into Puppet and we already use that
a lot. Why not integrate the environment in Hiera? Well, our
hiera.yaml
is now:
---
:hierarchy:
- "%{environment}/hiera/%{::fqdn}"
- "%{environment}/hiera/%{::hostname}"
- "%{environment}/hiera/%{::domainname}"
- "%{environment}/hiera/%{::systemtype}"
- "%{environment}/hiera/%{::osfamily}"
- "%{environment}/hiera/common"
:backends:
- yaml
:yaml:
:datadir:
/etc/puppetlabs/puppet/environments
This challenges me with a chicken and egg problem. To get the environment I need to know the environment. But what if I make Hiera into an ENC and let this one deliver the environment? Can this be done? Yes, it can.
This is how I did it:
First create a part of the Hiera structure that’s not in the current environment, for example like this:
---
:hierarchy:
- "hiera/%{::fqdn}"
- "hiera/default"
- "%{environment}/hiera/%{::fqdn}"
- "%{environment}/hiera/%{::hostname}"
- "%{environment}/hiera/%{::domainname}"
- "%{environment}/hiera/%{::systemtype}"
- "%{environment}/hiera/%{::osfamily}"
- "%{environment}/hiera/common"
:backends:
- yaml
:yaml:
:datadir:
/etc/puppetlabs/puppet/environments
And in the directory /etc/puppetlabs/puppet/environments/hiera
I place
a very small file, called default.yaml
, which contains:
---
environment: 'production'
This makes sure that any node without a specific file, will get the
production
environment. This is the default for Puppet as well, so
nothing changes for that.
To test this, run:
hiera environment ::fqdn=$(hostname -f)
This will give you something like environment: production
. For every
host in another environment as the production
one, create a small file
named the FQDN of the host with the contents stating the wanted
environment.
(Watch for the ::
in front of the fqdn
. This means that the fqdn
variable is a top scope variable, as all facter variables are.
Now integrate this into Puppet. First create a little script that executes the command above and returns the wanted output.
My script is called getenv
and placed in /etc/puppetlabs/puppet/bin
#!/bin/bash
penv="$(/opt/puppet/bin/hiera \
-c /etc/puppetlabs/puppet/hiera.yaml \
environment ::fqdn="${1}")"
echo "environment: ${penv}"
This returns a string like environment: production
.
And last, but not least, place this settings in the [master]
of your
puppet.conf
node_terminus = exec external_nodes: /etc/puppetlabs/puppet/bin/getenv
It took some work to get things started, but a small shell thingy read the file with all 10.000+ hosts and required environments, that created all the Hiera files for all nodes that are not in the production environment.
Just one thing to do: When I have a lot of host-files in a single directory, this could become slow. I could place all definitions in a simple database, but things would get complicated again, and that’s not what I want. I also could split things up per letter, but I’m not sure yet if I really want that.
When I have resolved this, this entry will be continued.