Roles and profiles example
This example demonstrates a complete roles and profiles workflow. Use it to understand the roles and profiles method as a whole. Additional examples show how to design advanced configurations by refactoring this example code to a higher level of complexity.
Configure Jenkins controller servers with roles and profiles
Jenkins is a continuous integration (CI) application that runs on the JVM. The Jenkins controller server provides a web front-end, and also runs CI tasks at scheduled times or in reaction to events.
In this example, we manage the configuration of Jenkins controller servers.
Set up your prerequisites
If you're new to using roles and profiles, do some additional setup before writing any new code.
Choose component modules
For our example, we want to manage Jenkins itself using the
puppet/jenkins
module.
Jenkins requires Java, and the puppet/jenkins
module can manage it
automatically. But we want finer control over Java, so we're going to disable that. So, we
need a Java module, and puppetlabs/java
is a good choice.
That's enough to start with. We can refactor and expand when we have those working.
To learn more about these modules, see puppet/jenkins and puppetlabs/java.
Write a profile
From a Puppet perspective, a
profile is just a normal class stored in the profile
module.
profile::jenkins::controller
, located at
.../profile/manifests/jenkins/controller.pp
, and fill it with Puppet code.
# /etc/puppetlabs/code/environments/production/site/profile/manifests/jenkins/controller.pp
class profile::jenkins::controller (
String $jenkins_port = '9091',
String $java_dist = 'jdk',
String $java_version = 'latest',
) {
class { 'jenkins':
configure_firewall => true,
install_java => false,
port => $jenkins_port,
config_hash => {
'HTTP_PORT' => { 'value' => $jenkins_port },
'JENKINS_PORT' => { 'value' => $jenkins_port },
},
}
class { 'java':
distribution => $java_dist,
version => $java_version,
before => Class['jenkins'],
}
}
This is pretty simple, but is already benefiting us: our interface for
configuring Jenkins has gone from 30 or so parameters on the Jenkins class (and many
more on the Java class) down to three. Notice that we’ve hardcoded the
configure_firewall
and install_java
parameters, and
have reused the value of $jenkins_port
in three places.Set data for the profile
Let’s assume the following:
- We use some custom facts:
-
group
: The group this node belongs to. (This is usually either a department of our business, or a large-scale function shared by many nodes.) -
stage
: The deployment stage of this node (dev, test, or prod).
-
- We have a five-layer hierarchy:
- console_data for data defined in the console.
-
nodes/%{trusted.certname}
for per-node overrides. -
groups/%{facts.group}/%{facts.stage}
for setting stage-specific data within a group. -
groups/%{facts.group}
for setting group-specific data. -
common
for global fallback data.
- We have a few one-off Jenkins controllers, but most of them belong to the
ci
group. - Our quality engineering department wants controllers in the
ci
group to use the Oracle JDK, but one-off machines can just use the platform’s default Java. - QE also wants their prod controllers to listen on port 80.
# /etc/puppetlabs/code/environments/production/data/nodes/ci-controller01.example.com.yaml
# --Nothing. We don't need any per-node values right now.
# /etc/puppetlabs/code/environments/production/data/groups/ci/prod.yaml
profile::jenkins::controller::jenkins_port: '80'
# /etc/puppetlabs/code/environments/production/data/groups/ci.yaml
profile::jenkins::controller::java_dist: 'oracle-jdk8'
profile::jenkins::controller::java_version: '8u92'
# /etc/puppetlabs/code/environments/production/data/common.yaml
# --Nothing. Just use the default parameter values.
Write a role
To write roles, we consider the machines we’ll be managing and decide what else they need in addition to that Jenkins profile.
-
profile::base
must be assigned to every machine, including workstations. It manages basic policies, and uses some conditional logic to include OS-specific profiles as needed. -
profile::server
must be assigned to every machine that provides a service over the network. It makes sure ops can log into the machine, and configures things like timekeeping, firewalls, logging, and monitoring.
So a role to manage one of our Jenkins controllers should include those classes as well.
class role::jenkins::controller {
include profile::base
include profile::server
include profile::jenkins::controller
}
Assign the role to nodes
Finally, we assign role::jenkins::controller
to every node that acts as a Jenkins controller.
Puppet has several ways to assign classes to nodes, so use whichever tool you feel best fits your team. Your main choices are:
- The console node classifier, which lets you group nodes based on their facts and assign classes to those groups.
- The main manifest which can use node statements or conditional logic to assign classes.
-
Hiera or Puppet lookup — Use
the
lookup
function to do a unique array merge on a specialclasses
key, and pass the resulting array to theinclude
function.# /etc/puppetlabs/code/environments/production/manifests/site.pp lookup('classes', {merge => unique}).include
To learn more about how to assign custom facts to individual nodes, visit https://puppet.com/docs/puppet/8/fact_overview.html.