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.
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.
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 --- sources: puppet: remote: 'git://github.com/tessier-ashpool/puppet-modules' 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.
- If you've set up one repo for all your Puppet code, you'll want to refactor your monolithic code repo to deploy with r10k.
- Here's a webinar from professional services engineer Gary Larizza, Git Workflow Best Practices & Deploying with r10k.