Published on 29 October 2015 by

EDITOR'S NOTE: This post is an update of Adrien's original and much-read post on dynamic environments, Git Workflow and Puppet Environments.

When you're writing Puppet manifests and modules, you're writing the source code for your infrastructure. And like any other source code, your Puppet code needs to be tracked and versioned.

Managing Code

When it comes to tracking and versioning code, Git has become the version control system (VCS) tool of choice. This especially applies to Puppet code, given that there are many thousands of Git repositories for Puppet modules on GitHub.

Since Git was originally created to version-control the Linux kernel, it was built to handle complex development processes with many people working on different features at once. That makes it especially useful for Puppet code. Because Puppet is declarative and human readable, it lends itself to being shared by multiple teams outside of Ops — development, test, network administration, DBA and more. Using Git to version Puppet code lets everyone see what's happening in infrastructure for every phase, and lets you all collaborate.

Code Isolation

One of the hallmark features of Git is the heavy use of branches. With Git, each branch represents a single version of some code and the history of that code. Git makes branching and merging very lightweight, so when using Git it's common to create a separate Git branch for each new feature or change. This allows any number of people to work on the same code and be constantly developing without stepping on each other's toes — changes are only merged into the master branch when they're ready, and are isolated until then.

Puppet has a similar feature that provides isolation, called environments. A Puppet environment is an isolated set of Puppet manifests, modules, and data. When a Puppet agent checks into a master and requests a catalog, it requests that catalog from a specific environment.

Environments allow you to easily run different versions of Puppet code, so you can test changes to that code without affecting all of your systems. Git branches and Puppet environments both isolate changes at different times — so why not use them together?

Mapping Branches to Environments

Recent versions of Puppet include something called directory environments. Introduced in Puppet 3.5 and 3.6, directory environments work by looking at a configured path for directories and creating an environment for each directory found in that path. In earlier versions of Puppet, adding a new environment meant either manually updating puppet.conf or relying on some quirks in how puppet.conf was evaluated. In contrast, creating a new environment with directory environments is simply a matter of creating a directory in the right path, and then it will be loaded by Puppet.

Directory environments make it very easy to create Puppet environments based on Git branches. Each branch in a Git repository is cloned into the environment path, and voila — your Puppet environments now match your Git branches.

Without tooling, it would be a labor-intensive process to create, update and delete Puppet environments. Thankfully tooling does exist — in the form of r10k.

Managing Git-Based Environments with r10k

r10k is a tool that maps Git branches to Puppet environments. Once r10k is configured to know where your Puppet code lives and where to create environments, it'll sort out the rest.

# /etc/puppetlabs/r10k/r10k.yaml
    remote: 'git://'
    basedir: '/etc/puppetlabs/code/environments'

Once r10k is configured, we can run r10k to have it update all environments.

# r10k deploy environment -pv
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/ad_hoc_repo_box
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/canary_job
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/check_job_status
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/chronos_improvements
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/dns_dr
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/echo_vpn_test
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/gpg_agent_flexibility
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/graphite_metrics_logstash
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/improve_locking
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/nginx_allow_updatephp
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/openstack
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/production
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/python_forced_commands
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/rsync_monitoring
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/slice_dev
INFO     -> Deploying environment /etc/puppetlabs/puppet/environments/sysdig

See how all the environments got deployed? Now you know what you need to do to change quickly between environments (Git branches) for a terrific workflow — for example, from development to production to patch, and back again.

Adrien Thebo is a software engineer at Puppet Labs and the creator of r10k.

Learn More

Share via:
Posted in:

How do you reuse code between branches? What do you do when you discover a common bug in one of the branches? How do you sync the fix between all the branches?

How do you avoid having to merge every change N times to N branches (branch = environment) for each change?  What if your branches begin to diverge, because you're taking too long to integrate the fix?  How does that gel with the notions of continuous integration (aka everything on "master"), or other software development focused methodologies?  This seems like it would quickly become unmanageable if you had several custom modules, profiles, roles, whatever, and more than like 3 environments.

The content of this field is kept private and will not be shown publicly.

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.