Puppet and Git, 201: r10k Setup – Installation

I know you’re probably anxious to get started with managing your infrastructure, but we’re going to stay distracted by Git for a little longer. In the 100 series, we saw some examples of how to migrate your manifests and modules into Git and how to make changes to your manifests through branches. The setup is a little primitive, but acceptable for a lab – everything is is either done by root or involves pushing changes as a user and pulling them as root, and changes are tested in production. I’d like to introduce you to a tool called r10k that will help us create dynamic branches for testing and decouple our workflow from direct access to the puppet master. In this 201 class, we’ll work on the first half by migrating our existing repo structure into r10k.

Review and Setup

If we review the puppet-tutorial repo’s master branch, we have a standard directory layout that you should be somewhat familiar with now:

|-- auth.conf
|-- fileserver.conf
|-- manifests
  |-- site.pp
|-- modules
|-- puppet.conf
|-- README.md

r10k enables dynamic environments. It does this by managing the modules directory itself. It uses a file called Puppetfile, described by librarian-puppet, to manage these modules. The directory structure of an r10k repo is much simpler:

|-- manifests
  |-- site.pp
|-- Puppetfile
|-- dist
|-- site
|-- .gitignore

The puppet configuration files are removed. The site manifest stays where it is, the modules directory is replaced by the Puppetfile, and we have two new directories, dist for internally developed generic modules (where roles/profiles may reside) and site for modules deploying custom services.

In addition, r10k has a configuration file located at /etc/r10k.yaml. We won’t track this directly – we’re using Puppet, so we’ll have Puppet configure the r10k configuration.

Since writing this article in 2014, the location of the configuration file has moved. See the official quickstart guide for the current location. As of May, 2016, that location is /etc/puppetlabs/r10k/r10k.yaml. Don’t worry, since Puppet is doing the configuration, you don’t actually have to know this!

Now let’s look at what it takes to get from where we are to where r10k wants us to be. The first step is to look at our own modules.

Dist/Site versus Repos

As mentioned above, there are dist/site directories for your custom modules. You can use those, or you can create additional repos for your modules and have r10k track them. While this will make the final workflow slightly more complicated (potentially making changes to two repos for a single effective change), it’s a process fairly well documented on the internet, compared to almost zero documentation on using dist/site. If you have any instructions or tips on using dist/site, please drop me a line in the comments!

Since we need to break our module out into another repo, let’s take a look at what the module is called first, using puppet module list.

[root@puppet ~]# puppet module list
├── puppetlabs-apache (v0.11.0)
├── puppetlabs-concat (v1.0.1)
├── puppetlabs-firewall (v1.0.0)
├── puppetlabs-ntp (v3.0.1)
├── puppetlabs-stdlib (v4.1.0)
├── rnelson0-base (v0.1.0)
├── saz-ssh (v1.4.0)
├── yguenane-augeas (v0.1.1)
└── yguenane-ygrpms (v0.1.0)
/usr/share/puppet/modules (no modules installed)

Go to your github page and create a new repo named after your module, in my case rnelson0-base. Next, clone the repo. You can clone this on the puppet master in a different directory, as I have done for ease of capturing output, or do it on another machine – remember that r10k decouples our workflow from the master. Create a directory, clone the repo, and copy the module files from the puppet repo or the puppet master itself.

[root@puppet ~]# mkdir git
[root@puppet ~]# cd git
[root@puppet git]# git clone https://rnelson0@github.com/rnelson0/rnelson0-base.git
Initialized empty Git repository in /root/git/rnelson0-base/.git/
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
[root@puppet git]# cp -r /etc/puppet/modules/base/* rnelson0-base/
[root@puppet git]# cd rnelson0-base/
[root@puppet rnelson0-base]# ls -l
total 32
drwxr-xr-x. 2 root root 4096 Mar 26 14:48 manifests
-rw-r--r--. 1 root root  902 Mar 26 14:48 metadata.json
-rw-r--r--. 1 root root  384 Mar 26 14:48 Modulefile
drwxr-xr-x. 3 root root 4096 Mar 26 14:48 pkg
-rw-r--r--. 1 root root  167 Mar 26 14:48 README
-rw-r--r--. 1 root root   57 Mar 26 14:48 README.md
drwxr-xr-x. 2 root root 4096 Mar 26 14:48 spec
drwxr-xr-x. 2 root root 4096 Mar 26 14:48 tests

Verify everything is copacetic, commit your change and push it upstream.

[root@puppet rnelson0-base]# git add .
[root@puppet rnelson0-base]# git commit -m 'Initial commit'
[master af364be] Initial commit
 17 files changed, 342 insertions(+), 0 deletions(-)
 create mode 100644 Modulefile
 create mode 100644 README
 create mode 100644 manifests/init.pp
 create mode 100644 manifests/user.pp
 create mode 100644 metadata.json
 create mode 100644 pkg/rnelson0-base-0.1.0.tar.gz
 create mode 100644 pkg/rnelson0-base-0.1.0/Modulefile
 create mode 100644 pkg/rnelson0-base-0.1.0/README
 create mode 100644 pkg/rnelson0-base-0.1.0/manifests/.init.pp.swp
 create mode 100644 pkg/rnelson0-base-0.1.0/manifests/.users.pp.swp
 create mode 100644 pkg/rnelson0-base-0.1.0/manifests/init.pp
 create mode 100644 pkg/rnelson0-base-0.1.0/manifests/users.pp
 create mode 100644 pkg/rnelson0-base-0.1.0/metadata.json
 create mode 100644 pkg/rnelson0-base-0.1.0/spec/spec_helper.rb
 create mode 100644 pkg/rnelson0-base-0.1.0/tests/init.pp
 create mode 100644 spec/spec_helper.rb
 create mode 100644 tests/init.pp
[root@puppet rnelson0-base]# git push origin
Counting objects: 21, done.
Compressing objects: 100% (18/18), done.
Writing objects: 100% (20/20), 8.47 KiB, done.
Total 20 (delta 4), reused 0 (delta 0)
To https://rnelson0@github.com/rnelson0/rnelson0-base.git
 6506cdd..af364be master -> master

Refresh the github page and ensure you see your changes before proceeding. Repeat this process for any other custom modules you may have developed. When you’re done, we can install r10k itself.

Install r10k

In the section below, I have used zack/r10k as it was current in 2014. The module has been since replaced with puppet/r10k. You should use this module instead and it may require adjustments. The article however has been left as-is for posterity.

If you read the r10k page above, you saw that r10k can be installed by running gem install r10k, plus modifying some other files. That sounds like a lot of work! Well, not that much, but there’s a few steps and every one is a chance to mess something up. Instead, let’s use an installer for r10k, zack/r10k (not to be confused with puppetlabs/r10k!). On the puppet master, run puppet module install zack/r10k:

[root@puppet ~]# puppet module install zack/r10k
Notice: Preparing to install into /etc/puppet/modules ...
Notice: Downloading from https://forge.puppetlabs.com ...
Notice: Installing -- do not interrupt ...
└─┬ zack-r10k (v1.0.2)
  ├── gentoo-portage (v2.1.0)
  ├── mhuffnagle-make (v0.0.2)
  ├── puppetlabs-gcc (v0.1.0)
  ├── puppetlabs-git (v0.0.3)
  ├── puppetlabs-inifile (v1.0.3)
  ├── puppetlabs-pe_gem (v0.0.1)
  ├── puppetlabs-ruby (v0.1.0)
  └── puppetlabs-vcsrepo (v0.2.0)

We also need to create a manifest to manage puppetlabs’s r10k. Here’s an example:

class { 'r10k':
  version => '1.2.0',
  sources => {
    'puppet' => {
      'remote'  => 'https://github.com/rnelson0/puppet-tutorial.git',
      'basedir' => "${::settings::confdir}/environments",
      'prefix'  => false,
  manage_modulepath => true,
  modulepath        => "${::settings::confdir}/environments/\$environment/modules:/opt/puppet/share/puppet/modules",

ini_setting { 'manifestdir':
 ensure => present,
 path => '/etc/puppet/puppet.conf',
 section => 'main',
 setting => 'manifestdir',
 value => '/etc/puppet/environments/$environment/manifests',

This should be fairly explanatory. First we declare an instance of the zack/r10k class. The version is the version of r10k to install. In the sources hash, the remote setting points to our puppet repo and the basedir and modulepath keys indicate where modules are, the first specifying the modules directory managed by r10k and the second the modulepath in /etc/puppet.conf. Prefix determines whether to use r10k’s source-prefix feature, which we will not. The manage_modulepath key indicates that r10k has control of the modulepath, so if you throw any files in there don’t expect them to persist past the next r10k run.

Below the class definition, we invoke an ini_setting definition. This comes from puppetlabs/inifile, which happens to be a dependency for zack/r10k so it’s guaranteed to be installed. Currently, zack/r10k only allow us to declare the modulepath, not the manifestdir, so if we want manifests to be specific per environment, we need to have manifestdir point to our dynamic directory. If you skip this step, only the manifests in /etc/puppet/manifests will be used. That’s a valid option, just not one we are pursuing.

We also need to create the basedir. Do that, adjust the file to match your setup, save it to a .pp file, and apply it:

[root@puppet ~]# mkdir -p /etc/puppet/environments
[root@puppet ~]# cat > r10k_installation.pp
class { 'r10k':
  version => '1.2.0',
  sources => {
    'puppet' => {
      'remote'  => 'https://github.com/rnelson0/puppet-tutorial.git',
      'basedir' => "${::settings::confdir}/environments",
      'prefix'  => false,
  manage_modulepath => true,
  modulepath        => "${::settings::confdir}/environments/\$environment/modules:/opt/puppet/share/puppet/modules",

ini_setting { 'manifestdir':
 ensure => present,
 path => '/etc/puppet/puppet.conf',
 section => 'main',
 setting => 'manifestdir',
 value => '/etc/puppet/environments/$environment/manifests',
[root@puppet ~]# puppet apply r10k_installation.pp
Warning: Config file /etc/puppet/hiera.yaml not found, using Hiera defaults
Notice: Compiled catalog for puppet.nelson.va in environment production in 4.12 seconds
Notice: /Stage[main]/R10k::Config/File[r10k.yaml]/ensure: defined content as '{md5}878ce519e7049c54ea4f12a8535db5fa'
Notice: /Stage[main]/R10k::Config/Ini_setting[R10k Modulepath]/ensure: created
Notice: /Stage[main]/R10k::Install/Package[r10k]/ensure: created
Notice: /Stage[main]/Ruby/Package[rubygems-update]/ensure: created
Notice: /Stage[main]/Ruby/Exec[ruby::update_rubygems]: Triggered 'refresh' from 1 events
Notice: /Stage[main]/Ruby::Dev/Package[ruby-devel]/ensure: created
Notice: Finished catalog run in 35.74 seconds

Currently (this article was written on 4/28/2014), zack/r10k does a bad, bad thing by updating rubygems on RHEL/CentOS. This overwrites the version of rubygems installed by the OS package, which would leave us up a creek without a paddle. It’s easy to fix, though, just reinstall rubygems:

[root@puppet ~]# yum reinstall rubygems -y

Check the contents of /etc/puppet/puppet.conf and you’ll see the modified modulepath:

[root@puppet ~]# cat /etc/puppet/puppet.conf
    # The Puppet log directory.
    # The default value is '$vardir/log'.
    logdir = /var/log/puppet

    # Where Puppet PID files are kept.
    # The default value is '$vardir/run'.
    rundir = /var/run/puppet

    # Where SSL certificates are kept.
    # The default value is '$confdir/ssl'.
    ssldir = $vardir/ssl

    server = puppet.nelson.va
    modulepath = /etc/puppet/environments/$environment/modules:/opt/puppet/share/puppet/modules

    # The file in which puppetd stores a list of the classes
    # associated with the retrieved configuratiion.  Can be loaded in
    # the separate ``puppet`` executable using the ``--loadclasses``
    # option.
    # The default value is '$confdir/classes.txt'.
    classfile = $vardir/classes.txt

    # Where puppetd caches the local configuration.  An
    # extension indicating the cache format is added automatically.
    # The default value is '$confdir/localconfig'.
    localconfig = $vardir/localconfig

Make sure r10k works. If it doesn’t, you may need to reinstall rubygems as mentioned.

[root@puppet ~]# r10k
Faraday: you may want to install system_timer for reliable timeouts
    r10k - Killer robot powered Puppet environment deployment

    r10k  [options]

    r10k is a suite of commands to help deploy and manage puppet code for
    complex environments.

    deploy         Puppet dynamic environment deployment
    puppetfile     Perform operations on a Puppetfile
    version        Print the version of r10k
    (3 hidden commands omitted; show them with --verbose)

    -c --config     Specify a global configuration file (deprecated, use `r10k deploy -c`)
    -h --help       Show help for this command
    -t --trace      Display stack traces on application crash
    -v --verbose    Set verbosity level

With r10k installed and running, we can convert our repo to the new format.

Take a break

We’re not done yet here, by a long shot! However, the 200-series classes cover a lot of ground and these articles can get too long and complicated if I let them. I know how difficult they are to proofread at least! So we’re going to break them up into bite-size chunks as much as we can. Next week we’ll finish up the r10k setup by converting our existing repo and using r10k for an actual deployment of our new, dynamic environments.

3 thoughts on “Puppet and Git, 201: r10k Setup – Installation

  1. Hey rnelson0,

    thanks for this great tutorial. I’m currently running Puppet 3.7.1 and now it is deprecated to set manifestdir and modulepath:
    Warning: Setting manifestdir is deprecated. See http://links.puppetlabs.com/env-settings-deprecations
    (at /usr/share/ruby/vendor_ruby/puppet/settings.rb:1117:in `issue_deprecation_warning’)
    Warning: Setting modulepath is deprecated in puppet.conf. See http://links.puppetlabs.com/env-settings-deprecations
    (at /usr/share/ruby/vendor_ruby/puppet/settings.rb:1119:in `issue_deprecation_warning’)
    It seems to be enough to set “environmentpath = $confdir/environments” in the puppet.conf

  2. Pingback: Improved r10k deployment patterns | rnelson0

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s