Configuration Management and Version Control Software! Definitely, a great way to start a new year in this DevOps' era is to write a post about these two hot topics. Indeed, Moving forward on my DevOPS path makes these two unavoidable.
I'm using Puppet for more than a year now, I have written a dozen of useful modules and as I'm introducing the rest of the team to Configuration Management/Puppet, I'm also starting to understand the great benefits of using Version Control Software(VCS) to manage Puppet Code (or any other Source Codes), especially when that code is being edited by many people (it's even more obvious when these other people are SysAdmin).
I'm using Puppet for more than a year now, I have written a dozen of useful modules and as I'm introducing the rest of the team to Configuration Management/Puppet, I'm also starting to understand the great benefits of using Version Control Software(VCS) to manage Puppet Code (or any other Source Codes), especially when that code is being edited by many people (it's even more obvious when these other people are SysAdmin).
The aim of this post is mainly to share my experience about moving from that monolithic environment where I used to edit my Written Puppet Modules directly on my Puppet Master and use git (a bunch of git clone/git remote/git push/git fetch...) for maintaining Puppet Directory Environment (Production/Dev/Test Puppet Environments) to a much cleaner Workflow where I (and any others contributors) can easily contribute. On that cleaner workflow, I'm intending to make use of:
- A smart tool which provides a general purpose toolset for deploying Puppet environments and modules in a seamless way (R10k).
- An Internal Online Repository Hosting System (Gitlab CE/Git) to Publish /Store Puppet Codes in a Central Repo.
- A much modern integrated toolset for developing Puppet modules and manifests (Gepetto) from workstation (not from my Puppet Master).
To make it more readable, I've divided this post in 03 sections:
I. A Basic and Monolithic Git workflow: Description of this Monolithic Environment
This section is about describing in details the Basic Monolithic Git workflow and how that was implemented. I will also highlight some pros and cons of that workflow.
II. Moving to "one repo per Puppet Module" and a Centralized Git
In This section, I'm describing in detail the split of the huge Puppet Repositories in many smaller repo that are hosted on a Centralized Enterprise Git Server (GitLab-CE)
III. Plastering the walls with r10k:
This last section is about r10k Implementation for the synchronization of the Puppet Environments, their associate Modules and Data
I. A Basic and Monolithic Git workflow : Description of the Monolithic Environment
*** One ring to rule them all
This section is about describing in details the Basic Monolithic Git workflow and how that was implemented.
The Setup I'm using for the post is a classic one for a Middle-sized Infrastructure, a PE 2015.2 with Puppet master, PE console, and PuppetDB all installed on one node.
For Puppet Directory Environment (production/Dev/Test...), I'm using the recommended PE 2015.2 environment-based workflow for testing new code in the node classifier. this workflow is well described on Puppet Site, but basically, it's about the creation/configuration of two type of Nodes Groups, a first set of node groups is used exclusively for assigning environments to nodes, and the second set of node groups is used for applying classes to nodes.
Below, a Picture which summarizes that workflow. On the left side, we have a set of node groups which are used for assigning nodes to either Production, Dev or Test environments and on the right side, the structure that is put in place for Classifying Nodes (using roles).
Source: http://docs.puppetlabs.com/pe/2015.2/console_classes_groups_environment_override.html#
Source: http://docs.puppetlabs.com/pe/2015.2/console_classes_groups_environment_override.html#
For the implementation of such design, I've the default production environment and new test and Dev Environments. The test and dev environments were simply created by initializing /etc/puppetlabs/code/environments/production as git repo and cloning that repo to both dev test directories . The Synchronization between test/dev and production repo is (obviously) manual, with the development of new features/modules being done exclusively in the test environment. Once satisfied with the features/modules on tests environment, the test repository is merged in dev environment and later on, to Production environment. Below is a picture that try to summarize this basic git workflow.
I.a: Creation of Git Repo and Directory Environments
Let's see how this is implemented on my Puppet Master (Name: pe-master). Note that I'm using the default (already configured) production directory to initialize the First git repo.
### Creation of production repo [root@pe-master production]# cd /etc/puppetlabs/code/environments/production [root@pe-master production]# git init Initialized empty Git repository in /etc/puppetlabs/code/environments/production/.git/
[root@pe-master production (master)]# git add --all :/ [root@pe-master production (master)]# git commit -m "Production Initial Repository with few official modules installed" [......]
#### Cloning of production repo to test and dev Repos [root@pe-master production]# cd .. [root@pe-master environments]# git clone production test Cloning into 'test'... done. [root@pe-master environments]# git clone production dev Cloning into 'dev'... done.
### Adding Remote repositories to test/dev Repositories [root@pe-master environments]# cd test/ [root@pe-master test (master)]# pwd /etc/puppetlabs/code/environments/test [root@pe-master test (master)]# git remote add dev /etc/puppetlabs/code/environments/dev [root@pe-master test (master)]$ git fetch dev From /etc/puppetlabs/code/environments/dev * [new branch] master -> dev/master [root@pe-master test (master)]# git remote -v dev /etc/puppetlabs/code/environments/dev (fetch) dev /etc/puppetlabs/code/environments/dev (push) origin /etc/puppetlabs/code/environments/production (fetch) origin /etc/puppetlabs/code/environments/production (push) [root@pe-master test (master)]# git branch -r dev/master origin/HEAD -> origin/master origin/master [root@pe-master test (master)]# cd ../dev [root@pe-master dev (master)]# pwd /etc/puppetlabs/code/environments/dev [root@pe-master test]# git status # On branch master nothing to commit, working directory clean [root@pe-master dev (master)]# git remote add test /etc/puppetlabs/code/environments/test [root@pe-master dev (master)]$ git fetch test From /etc/puppetlabs/code/environments/test * [new branch] master -> test/master [root@pe-master dev (master)]# git remote -v origin /etc/puppetlabs/code/environments/production (fetch) origin /etc/puppetlabs/code/environments/production (push) test /etc/puppetlabs/code/environments/test (fetch) test /etc/puppetlabs/code/environments/test (push) [root@pe-master dev (master)]# git branch -r origin/HEAD -> origin/master origin/master test/master ### Checking the Production Repositories and adding test/dev as remote [root@pe-master dev (master)]# cd ../production [root@pe-master production (master)]$ git remote add test /etc/puppetlabs/code/environments/test [root@pe-master production (master)]$ git remote add dev /etc/puppetlabs/code/environments/dev [root@pe-master production (master)]$ git fetch test From /etc/puppetlabs/code/environments/test * [new branch] master -> test/master [root@pe-master production (master)]$ git fetch dev From /etc/puppetlabs/code/environments/dev * [new branch] master -> dev/master [root@pe-master production (master)]# git remote -v dev /etc/puppetlabs/code/environments/dev (fetch) dev /etc/puppetlabs/code/environments/dev (push) test /etc/puppetlabs/code/environments/test (fetch) test /etc/puppetlabs/code/environments/test (push)
I.b: Creation of Environments Nodes Groups:
As test and dev Environment Directories are already created, the node classification under these Environments Node Groups (test/dev) is quite easy on Puppet Enterprise Console.
On the enterprise console, under the node ==> classification section, we create a new group named test (dev) and set the Environment to "test".
Once that is done, we go on that group and "Edit node group metadata", then checked "
I.c: Create and use a new Module:
With this implementation, Modules can be written in the test environment and added in a seamless way to the dev environment and later on to production environment by just merging the git repositories.
Let's illustrate this by creating a quick (and dirty) newfeature module ( which simply notify on the environment facts).
### Get to the test modules directory and create newfeature Branch
[root@pe-master test (master)]# cd modules/ [root@pe-master modules (master)]# git status # On branch master nothing to commit, working directory clean [root@pe-master modules (master)]# git checkout -b newfeature Switched to a new branch 'newfeature' [root@pe-master modules (newfeature)]# git status # On branch newfeature nothing to commit, working directory clean [root@pe-master modules (newfeature)]# pwd /etc/puppetlabs/code/environments/test/modules [root@pe-master modules (newfeature)]# git branch master * newfeature
### Edit Puppet Manifest
[root@pe-master modules (newfeature)]# mkdir -p newfeature/{manifests,tests,files} [root@pe-master modules (newfeature)]# cd newfeature/manifests/ [root@pe-master manifests (newfeature)]# ls [root@pe-master manifests (newfeature)]# vim init.pp [root@pe-master manifests (newfeature)]# cat init.pp class newfeature { notify { "This is the environment I am assigned to : ${::environment}": } } [root@pe-master manifests (newfeature)]# puppet parser validate init.pp [root@pe-master manifests (newfeature)]# puppet module list --environment test | grep newfeature ├── newfeature (???)
### Commit this change
[root@pe-master manifests (newfeature)]# cd /etc/puppetlabs/code/environments/test/modules [root@pe-master modules (newfeature)]# git status # On branch newfeature # Untracked files: # (use "git add <file>..." to include in what will be committed) # # newfeature/ nothing added to commit but untracked files present (use "git add" to track) [root@pe-master modules (newfeature)]# git add newfeature [root@pe-master modules (newfeature)]# git commit -m "Added newfeature module" [newfeature f037c7b] Added newfeature module Committer: root <root@pe-master.stiv.local> 1 file changed, 3 insertions(+) create mode 100644 modules/newfeature/manifests/init.pp
Classify a test node (Add my small newfeature to the right profile) and run puppet agent on that test node.
[root@pe-test ~]# puppet agent -t Warning: Local environment: "production" doesn't match server specified node environment "test", switching agent to "test". Info: Retrieving pluginfacts Info: Retrieving plugin Info: Loading facts Info: Caching catalog for pe-test.stiv.local Info: Applying configuration version '1452445799' Notice: This is the environment I am assigned to : test Notice: /Stage[main]/newfeature/Notify[This is the environment I am assigned to : test]/message: defined 'message' as 'This is the environment I am assigned to : test' Notice: Applied catalog in 0.73 seconds
The test seems to be well working, so we can merge it back to our test master branch and delete the newfeature branch.
[root@pe-master modules (newfeature)]# git branch master * newfeature [root@pe-master modules (newfeature)]# git checkout master Switched to branch 'master' [root@pe-master modules (master)]# git log master..newfeature commit e8e6c5a5481b2e211a71f912a7185990bcdacf10 Author: root <root@pe-master.stiv.local> Date: Sun Jan 10 18:21:28 2016 +0100 Added newfeature module
[root@pe-master modules (master)]# git merge newfeature Updating 755df81..e8e6c5a Fast-forward modules/newfeature/manifests/init.pp | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 modules/newfeature/manifests/init.pp [root@pe-master modules (master)]# git branch -d newfeature Deleted branch newfeature (was e8e6c5a).
Next step is the Merging of this "test master branch" to the "dev master branch".
[root@pe-master modules (master)]# cd ../../dev/ [root@pe-master dev (master)]# ls environment.conf hieradata manifests modules [root@pe-master dev (master)]# git branch -r origin/HEAD -> origin/master origin/master test/master [root@pe-master dev (master)]# git log master..test/master [root@pe-master dev (master)]# git fetch test remote: Counting objects: 8, done. remote: Compressing objects: 100% (4/4), done. remote: Total 6 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (6/6), done. From /etc/puppetlabs/code/environments/test 755df81..e8e6c5a master -> test/master [root@pe-master dev (master)]# git log master..test/master commit e8e6c5a5481b2e211a71f912a7185990bcdacf10 Author: root <root@pe-master.stiv.local> Date: Sun Jan 10 18:21:28 2016 +0100 Added newfeature module
[root@pe-master dev (master)]# git branch * master [root@pe-master dev (master)]# git merge test/master Updating 755df81..e8e6c5a Fast-forward modules/newfeature/manifests/init.pp | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 modules/newfeature/manifests/init.pp [root@pe-master dev (master)]# git log master..test/master
We can now test that feature on Development System, and later on merge the dev and production repository to have the change live on the production systems.
[root@pe-dev ~]# puppet agent -t Warning: Local environment: "production" doesn't match server specified node environment "dev", switching agent to "dev". Info: Retrieving pluginfacts Info: Retrieving plugin Info: Loading facts Info: Caching catalog for pe-dev.stiv.local Info: Applying configuration version '1452446957' Notice: This is the environment I am assigned to : dev Notice: /Stage[main]/newfeature/Notify[This is the environment I am assigned to : dev]/message: defined 'message' as 'This is the environment I am assigned to : dev' Notice: Applied catalog in 0.65 seconds
Just to summarize this section, once both production,dev and test Directory Environments repo are initially synchronized, the workflow (with git commands associated) is implemented as followed for any module modification/addition:
### From the test repository, Create New Branch for new feature/module git checkout -b newmodule ### Develop Module in the normal way
### (with any number of commit in that newmodule branch) ### and once satisfied with the new module/feature, still in the test repository, ### merge our newmodule with our master branch git checkout master git merge newmodule ### Get to the dev repo, fetch the test repo in order to refresh
### our remote branch listing git fetch test ### Check what the remote branch has that isn't in the current master branch git log master..test/master ### Merge the remote test branch with the production master branch git merge test/master ### once satisfied with that Module on the Development Systems, ### we can move forward to our most critical systems (production), and merge dev git fetch test git fetch dev git merge dev/master ### Get to the dev branch, fetch the origin repo in order to refresh its branch listing git fetch origin ### Get to the test branch, fetch the origin ### and dev repo in order to refresh their branch listing git fetch origin git fetch dev
I.d: Some advantages and disadvantages of that Workflow:
1. How do we enable the tons of external awesome developers in the world to help enhance the module we're writing (in my case, how can I push some of my Puppet Module Code to Public github/gitlab account...)? We obviously can't push our whole Test repo to github and expects people to collaborate on that, we need a way to start a new (single) project/module, and push only that module to Public CVS.
2. How are we pulling Public Written Module which aren't on Forge (e.g: github,gitlab...) to our environment? If we do that, we'll be pulling another repository inside our repo (nested git repo) and that's a bit tough to deal with.
3. Eight (08) manual steps to have a Module modification on Production System! That seems a lot, and it's probably prone to mistake!
II.Moving to one repo per Puppet Module and a Centralized Git:
*** The Fellowship of the Ring
Having now these huge Test, dev and production repos which contain all our downloaded and written modules , I'm aiming to describe in this section how I'm moving from that to smaller repo which are more manageable and easily shareable. As described above, I have 03 similar environments; test,dev and production. Test is where I'm making most of the modification and dev and production are replicas of that repository (Mainly used for node classification.). For the split,I'm mainly working on that Test environment.
But before moving forward with the Test environment's split, there's a set of requirements that must be met:
- The first requirement is to have another System that will be used as Centralized git System. It is possible to use a Public Central Repo for this, but In this case, there are some modules that should be kept Private (the one that are specific to the running environment) and some that will be shared publicly, so I found that having an Internal central Git System will be nice. As you will see below, I'm making use of GitLab for that Internal Git System.
- The second is to make sure that we have no other branch than the master one, so we've to merge all our modification to the master branch of our Test repo.
- The last requirement is to have a Backup of all our environments :-) . For that a simple tar will do the trick, something like :
cd /etc/puppetlabs/code && tar zcvf /tmp/environments_before_split.tar.gz environments
Once the little requirements described above are met, we can apply the recommended strategy for such split which is to make a full clone of the main repo (test) to another local directory named with each module's name. Then, for each of this module, use git filter-branch --subdirectory-filter command to select commits that apply only to that module and we're done.
If that seems a bit tough to understand, then let's see how it is done practically. First we need have a look on the number of modules we've to migrate by using the tree command as seen below.
[root@pe-master test (master)]# tree -d -L 2 modules modules ├── apache │ ├── files │ ├── lib │ ├── manifests │ ├── spec │ ├── templates │ └── tests ├── banner │ ├── manifests │ ├── templates │ └── tests ├── chrony │ ├── manifests │ ├── spec │ ├── templates │ └── tests ├── newfeature │ ├── files │ ├── manifests │ └── tests ├── ntp │ ├── lib │ ├── manifests │ ├── spec │ ├── templates │ └── tests ├── profiles │ ├── files │ ├── manifests │ ├── templates │ └── tests ├── roles │ ├── files │ ├── manifests │ ├── templates │ └── tests ├── stdlib │ ├── examples │ ├── lib │ ├── manifests │ └── spec
So,we have a bunch of Puppet Modules Installed from Puppet Forge (puppetlabs-apache, puppetlabs-ntp, puppetlabs-stdlib and ringingliberty-chrony ), we also have the roles and profiles module and two other Internal written Modules (banner and newfeature).
Let us first try to split a single module (newfeature) by following the described strategy which is (after that we will automate that process):
- Create a new (temporary) working directory and get in that directory
- Clone the test directory environment to a repo named after the module we are willing to split (
- and remove origin after the cloning)
- Keep only the history relevant to that module
- Check that our module only contains the relevant subdirectory
###1.### Create the new working directory and get in that directory [root@pe-master tmp]# cd /tmp/ [root@pe-master tmp]# mkdir modules [root@pe-master tmp]# cd modules/ ###2.### Clone the test directory environment, better remove origin after the cloning [root@pe-master modules]# git clone /etc/puppetlabs/code/environments/test newfeature Cloning into 'newfeature'... done. [root@pe-master modules]# cd newfeature/ [root@pe-master newfeature (master)]# git remote -v origin /etc/puppetlabs/code/environments/test (fetch) origin /etc/puppetlabs/code/environments/test (push) [root@pe-master newfeature (master)]# git remote remove origin ###3.### Keep only the history relevant to that module [root@pe-master newfeature (master)]# git filter-branch --subdirectory-filter modules/newfeature Rewrite e8e6c5a5481b2e211a71f912a7185990bcdacf10 (1/1) Ref 'refs/heads/master' was rewritten ###4.### Check that our module only contains the relevant subdirectory [root@pe-master newfeature (master)]# cat manifests/init.pp class newfeature { notify { "This is the environment I am assigned to : ${::environment}": } } [root@pe-master newfeature (master)]# git status # On branch master nothing to commit, working directory clean [root@pe-master newfeature (master)]# git log --oneline 81ca0ca Added newfeature module
Having many modules to split using that same sequence, I think it's better to write a script for that activity. Here is a Python script that is written for that purpose and below output of that script in this environment (My strong suggestion for running such a script is to switch to an unprivileged system user who has only read right to the Directory Environment folder).
[stiv@pe-master ~]$ ./split_monolithic_puppet_git_repository.py /tmp/modules /etc/puppetlabs/code/environments/test ### Start Split for apache running git clone /etc/puppetlabs/code/environments/test apache ... Cloning into 'apache'... done. running git remote remove origin ... running git filter-branch --subdirectory-filter modules/apache ... Rewrite 755df8167361f1d5fff96cdba1b5b67f63c7e50f (1/1) Ref 'refs/heads/master' was rewritten ### End Split for apache ### Start Split for banner running git clone /etc/puppetlabs/code/environments/test banner ... Cloning into 'banner'... done. running git remote remove origin ... running git filter-branch --subdirectory-filter modules/banner ... Rewrite 8c103db90111a1d9230fdefe5917e6d74c463c39 (2/2) Ref 'refs/heads/master' was rewritten ### End Split for banner ### Start Split for chrony running git clone /etc/puppetlabs/code/environments/test chrony ... Cloning into 'chrony'... done. running git remote remove origin ... running git filter-branch --subdirectory-filter modules/chrony... Rewrite 8c103db90111a1d9230fdefe5917e6d74c463c39 (2/2) Ref 'refs/heads/master' was rewritten ### End Split for banner ### Start Split for newfeature running git clone /etc/puppetlabs/code/environments/test newfeature ... Cloning into 'newfeature'... done. running git remote remove origin ... running git filter-branch --subdirectory-filter modules/newfeature ... Rewrite e8e6c5a5481b2e211a71f912a7185990bcdacf10 (1/1) Ref 'refs/heads/master' was rewritten ### End Split for newfeature ### Start Split for ntp running git clone /etc/puppetlabs/code/environments/test ntp ... Cloning into 'ntp'... done. running git remote remove origin ... running git filter-branch --subdirectory-filter modules/ntp ... Rewrite 755df8167361f1d5fff96cdba1b5b67f63c7e50f (1/1) Ref 'refs/heads/master' was rewritten ### End Split for ntp ### Start Split for profiles running git clone /etc/puppetlabs/code/environments/test profiles ... Cloning into 'profiles'... done. running git remote remove origin ... running git filter-branch --subdirectory-filter modules/profiles ... Rewrite 0572ead6fa6a23f4eca967482eae247e438f9472 (14/14) Ref 'refs/heads/master' was rewritten ### End Split for profiles ### Start Split for roles running git clone /etc/puppetlabs/code/environments/test roles ... Cloning into 'roles'... done. running git remote remove origin ... running git filter-branch --subdirectory-filter modules/roles ... Rewrite ea7b531ad9caabf7fb93bc792edb2fd7dbbee3f4 (11/11) Ref 'refs/heads/master' was rewritten ### End Split for roles ### Start Split for stdlib running git clone /etc/puppetlabs/code/environments/test stdlib ... Cloning into 'stdlib'... done. running git remote remove origin ... running git filter-branch --subdirectory-filter modules/stdlib ... Rewrite 755df8167361f1d5fff96cdba1b5b67f63c7e50f (1/1) Ref 'refs/heads/master' was rewritten ### End Split for stdlib
II.b: Create new remote repositories (our Central git repo):
The next step is to push these small Modules repositories to our Centralized git Repo. To complete that, we obviously need to have that Centralized git repositories Installed and configured.
If you are using a known (public) centralized git repository (such as Bitbucket, Gitlab or GitHub) then you can simply start creating the repo that will host the module and then move forward II.c section (Pushing to the Central git Repo).
For this post, I've installed Gitlab-CE using the vhsn/gitlab Puppet Forge Module. In its simplest form, the following is enough to have it installed on the target node,
class { 'gitlab': external_url => 'http://git.stiv.local', }
I'm strongly advising to update GItLab CE to the latest edition by running yum update right after its installation. On that Centralized git repository, I'm creating one project for each module. As example, I created a repo for the newfeature module developed above on my Gitlab.
Having many repos to create, it's obviously better to automate such operation. It is also a great idea (not mandatory) at this stage to create a Gitlab group that will be used exclusively by Puppet Administrators and Users. For this post, I created a "puppet" group. Related to the automation of the new repo creation, I slightly modified the script described here as following (basically the script is using the right Gitlab API to create the repo. The script (and API) needs as input parameter my Gitlab User token (Available on Gitlab under profile ==> account) and it also needs the namespace id for the GitLab group we are using (puppet). This namespace group's id can be easily retrieved with:
$ curl --header "PRIVATE-TOKEN: MY_TOKEN_ID" "http://git.stiv.local/api/v3/namespaces/"
The script below (located under /home/stiv/git-init-gitlab.sh),
#!/bin/bash
# Location: /home/stiv/git-init-gitlab.sh
set -x # Set RepoName repo_name=$1 # Private Gitlab Token token=XXXXXXXXXXXXXXXX # Namespace of the group this project belongs to group_id=10 # Test that repo name is set test -z $repo_name && echo "Repo name required." 1>&2 && exit 1 # Create the Project in the group curl -H "Content-Type:application/json" http://git.stiv.local/api/v3/projects?private_token=$token -d "{ \"name\": \"$repo_name\",\"namespace_id\":$group_id}"
With that little script, I am able to easily create all my centralized git repo. The only small challenges before running the script over My new modules' repo is to figure out what to do with Modules that were downloaded from Puppet Forge. In my opinion, the simplest to do is to exclude these Forge Modules and create Central repo only for our written Modules, but this is only applicable if you haven't modified a Forge downloaded module. As seen below, we can leverage on "puppet module changes" command to verify if a Forge Module was modified after it has been downloaded.
[stiv@pe-master modules]$ cd /etc/puppetlabs/code/environments/test/modules [stiv@pe-master modules]$ for module in $(ls) > do > echo "##Module Name: $module" > puppet module changes $module > echo " " > done ##Module Name: apache Notice: No modified files ##Module Name: banner Error: No file containing checksums found. Error: Try 'puppet help module changes' for usage ##Module Name: chrony Warning: 1 files modified manifests/config.pp ##Module Name: newfeature Error: Could not find a valid module at "newfeature" Error: Try 'puppet help module changes' for usage ##Module Name: ntp Notice: No modified files ##Module Name: profiles Error: Could not find a valid module at "profiles" Error: Try 'puppet help module changes' for usage ##Module Name: roles Error: Could not find a valid module at "roles" Error: Try 'puppet help module changes' for usage ##Module Name: stdlib Notice: No modified files
Based on that output, I can safely assume that over the Module downloaded from Forge, Apache, NTP and stdlib modules haven't been modified, while chrony has been modified and I need to decide what to do with that modified version (in this case, I'll be keeping this modified version in my Central repository). banner,newfeature, profiles and roles are my own Modules and will obviously be cloned to that Central Repository.
Moving forward with the cloning to Gitlab, I simply have now to create my new Gitlab project (empty Gitlab project).
[stiv@pe-master modules]$ for module in `ls | egrep -v "apache|ntp|stdlib"` > do > /home/stiv/git-init-gitlab.sh $module > done
II.c: Push the new set of modules to their Central Repos and create production branch:
With our central repos created, the next step is to simply push our new set of Modules to these repos (I assume here that remote authentication to GitLab/Github is already configured). Let us see how this works for a single Module (in this case, newfeature), then later on, we'll run a loop for all the Modules.
[stiv@pe-master modules]$ cd newfeature/ [stiv@pe-master newfeature]$ git remote [stiv@pe-master newfeature]$ git remote add origin git@git.stiv.local:puppet/newfeature.git [stiv@pe-master newfeature]$ git remote -v origin git@git.stiv.local:puppet/newfeature.git (fetch) origin git@git.stiv.local:puppet/newfeature.git (push) [stiv@pe-master newfeature]$ git push -u origin master Counting objects: 4, done. Delta compression using up to 8 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (4/4), 348 bytes | 0 bytes/s, done. Total 4 (delta 0), reused 0 (delta 0) To git@git.stiv.local:puppet/newfeature.git * [new branch] master -> master Branch master set up to track remote branch production from origin.
Again, doing that manually for all the modules is kind of boring (Culture, Automation...). A simple loop here will do the trick for all the modules.
[stiv@pe-master modules]$ for module in `ls | egrep -v "apache|ntp|stdlib"` > do > cd $module > git remote add origin git@git.stiv.local:puppet/$module.git > git push -u origin master > cd .. > done
Once that is completed, we can move forward with the last step which is about r10k...
III.Plastering the walls with r10k:
*** The return of the king
Though we've successfully split our huge repos, we're still using that main monolithic repos (test,dev and production) on Puppet Master, this is the step where we're going to break these repo by using r10k.
So, what is r10k? It is a very simple tool that does just two things (more details on Puppet site):
So, what is r10k? It is a very simple tool that does just two things (more details on Puppet site):
- Handles Puppet modules Installation using an authoritative, standalone list (Puppetfile) that you create for it.
- Creates and manages directory environments based on the branches in your Git Control Repository.
One of the great feature r10k provides is the on-fly creation of Directory Environment using git branches of our Control Repo. Meaning that creation of a new environment is done just by adding a new branch to a repository. Anyway, let us see below what is that control repository and how to implement it.
III.a: Create R10k Control Repo:
The Control repository in its simplest form is a Git repository which stores a Puppetfile and hiera data (hieradata/). In facts, to use R10k, we must split a Puppet repo into two parts, a control repo that contains some configuration, including an r10k config file that points to second repo, which will contain our actual Puppet code.For the control repo creation, we can either decide to go for an empty directory that we will populate or use this template repo made available by Puppet. For educational purpose, I've settled for creating my own control repo. Below are the steps I went through for that
- Create a project named "control-repo" and add it to our "puppet" group
- Copy the following folders/files from one of the existing Directory Environment (production in this case, but can choose any other..) to a temporary folder:
[stiv@pe-master tmp]$ cd /tmp/ [stiv@pe-master tmp]$ mkdir control-repo [stiv@pe-master tmp]$ cd control-repo/ [stiv@pe-master control-repo]$ cp -pr /etc/puppetlabs/code/environments/production/{environment.conf,hieradata,manifests} . [stiv@pe-master control-repo]$ ls -lrth total 4.0K -rw-r--r-- 1 stiv stiv 879 Jul 21 2015 environment.conf drwxr-xr-x 2 stiv stiv 6 Jul 21 2015 hieradata drwxr-xr-x 2 stiv stiv 20 Aug 31 2015 manifests
- Create Puppetfile in that same folder, this is the main configuration file which lists the modules we'd like for our environment, and also lists where to get these modules (Forge, git -Gitlab, Github, Bitbucket...-). Below, you can see the one I created for this case. It is to be noted that as I'm using GitLab SSH to authenticate, I had to create another GitLab User in the Gitlab puppet Group (Set as id pe-puppet). I also created a public/private SSH keypair (ssh-keygen) for the user that invokes R10k (typically root, but may be site-specific,) and authorize that public key to connect to my Gitlab groups (and to all the modules' Repositories). r10k simply invokes git in order to clone the repositories defined in that Puppetfile.
[stiv@pe-master control-repo]$ cat Puppetfile forge "http://forge.puppet.com" # Modules from the Puppet Forge mod "puppetlabs-apache", '1.5.0' mod "puppetlabs-ntp", '3.3.0' mod "puppetlabs-stdlib", '4.11.0' # Modules from Git mod 'roles', :git => 'git@git.stiv.local:puppet/roles.git' mod 'profiles', :git => 'git@git.stiv.local:puppet/profiles.git' mod 'newfeature', :git => 'git@git.stiv.local:puppet/newfeature.git' mod 'banner', :git => 'git@git.stiv.local:puppet/banner.git' mod 'chrony', :git => 'git@git.stiv.local:puppet/chrony.git'
- Turn that temporary-control directory as a repo and sync it with the control-repo project (Note the git repo renaming to production in order to match with our Puppet Environment Name)
[stiv@pe-master control-repo]$ git init Initialized empty Git repository in /tmp/control-repo/.git/ [stiv@pe-master control-repo]$ git remote add origin git@git.stiv.local:puppet/control-repo.git [stiv@pe-master control-repo]$ git add :/ [stiv@pe-master control-repo]$ git commit -a -m "Initial Control Repo creation" [master (root-commit) 8d3b8bb] Initial Control Repo creation 3 files changed, 131 insertions(+) create mode 100644 Puppetfile create mode 100644 environment.conf create mode 100644 manifests/site.pp [stiv@pe-master control-repo]$ git branch -m master production [stiv@pe-master control-repo]$ git push -u origin production
- On GitLab, create control-repo's branch for dev and test environments
III.b: Test and Deploy r10k:
We may now run r10k on the console and enjoy the output,
We've everything in place now, so it's time to configure our Puppet master to use r10k for Module deployment. This is the most critical step, as it erases the existing Directory Environment. The strong advise here is to first master this on another environments before getting to the Production.
On PE 2015.2, r10k is configured either during the Installation (answer file) or under the PE Console (after deployment). In this case, we'll be doing that on the PE console, the steps on the console are as followed:
- In the PE console, navigate to the Classification page.
- Click the PE Master group.
- In the PE Master group page, click the Classes tab.
- Locate or add the pe_r10k class.
- In the pe_r10k class, set r10k’s parameters . At minimum, set the remote (which is the control-repo git address) and git_settings parameters (see screenshots below).
- Click Add parameter, and then the Commit change button.
We may now run r10k on the console and enjoy the output,
[root@pe-master ~ ]# r10k deploy environment -pv INFO -> Deploying environment /etc/puppetlabs/code/environments/dev INFO -> Deploying module /etc/puppetlabs/code/environments/dev/modules/apache INFO -> Deploying module /etc/puppetlabs/code/environments/dev/modules/apt INFO -> Deploying module /etc/puppetlabs/code/environments/dev/modules/stdlib INFO -> Deploying module /etc/puppetlabs/code/environments/dev/modules/roles INFO -> Deploying module /etc/puppetlabs/code/environments/dev/modules/profiles INFO -> Deploying module /etc/puppetlabs/code/environments/dev/modules/newfeature INFO -> Deploying module /etc/puppetlabs/code/environments/dev/modules/banner INFO -> Deploying module /etc/puppetlabs/code/environments/dev/modules/chrony INFO -> Deploying environment /etc/puppetlabs/code/environments/production INFO -> Deploying module /etc/puppetlabs/code/environments/production/modules/apache INFO -> Deploying module /etc/puppetlabs/code/environments/production/modules/apt INFO -> Deploying module /etc/puppetlabs/code/environments/production/modules/stdlib INFO -> Deploying module /etc/puppetlabs/code/environments/production/modules/roles [.................]
III.c: Benefits of the split and way forward:
So, our monolithic Git workflow has been divided in smaller units, with r10k in place, we can easily collaborate and turn some Modules on Public Version Control System.
But that is just few of the benefits, one of the huge improvement we got with r10k is that we can now easily refer to a Single Text file to know the exact state of each Environments. Also, as we have now one control-repo branch (thus one Puppetfile) for each environment, we can set (while keeping a trace of that) a different version of the same Module for each environment.
As an example, let's say that I want to have a specific "banner" Module version for dev Environment, I can simply get to the dev control-repo branch, modify the Puppetfile to have the following for Banner. The following setting on the dev Puppetfile tells to r10k that on this dev environment, the banner module's branch I'm willing to use is "dev".
As an example, let's say that I want to have a specific "banner" Module version for dev Environment, I can simply get to the dev control-repo branch, modify the Puppetfile to have the following for Banner. The following setting on the dev Puppetfile tells to r10k that on this dev environment, the banner module's branch I'm willing to use is "dev".
mod 'banner', :git => 'git@git.stiv.local:puppet/banner.git', :ref => 'dev'
There's a set of four options available to specify the module “version” (get more details for that on Official Puppet Documentation):
ref
: Determines the Git reference to check out. Can be any of a tag, commit, or branch.tag
: Clones the repo at a certain tag value.commit
: Clones the repo at a certain commit.branch
: Clones a specific branch of the repo.
Our next step will most probably be the optimization of Roles/Profiles Modules and also discussions around IDEs (Gepetto), but for now, let's enjoy r10k (or Code Manager :-))...
Reference:
http://puppetlunch.com/puppet/version-control.html
http://docs.puppetlabs.com/pe/2015.2/console_classes_groups_environment_override.html
https://puppetlabs.com/blog/refactor-your-monolithic-code-repo-to-deploy-with-r10k
http://www.jeffmalnick.com/blog/2014/05/16/r10k-control-repos/
https://techpunch.co.uk/development/how-to-build-a-puppet-repo-using-r10k-with-roles-and-profiles
http://docs.puppetlabs.com/pe/2015.2/console_classes_groups_environment_override.html
https://puppetlabs.com/blog/refactor-your-monolithic-code-repo-to-deploy-with-r10k
http://www.jeffmalnick.com/blog/2014/05/16/r10k-control-repos/
https://techpunch.co.uk/development/how-to-build-a-puppet-repo-using-r10k-with-roles-and-profiles
This comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDelete