Maintaining environments

Puppet doesn't organize things in modules exclusively. There is a higher-level unit called environment that groups and contains the modules. An environment mainly consists of:

  • One or more site manifest files
  • A modules directory
  • An optional environment.conf configuration file

When the master compiles the manifest for a node, it uses exactly one environment for this task. As described in Chapter 2, The Master and Its Agents, it always starts in manifests/*.pp, which form the environment's site manifest. Before we take a look at how this works in practice, let's see an example environment directory:

/opt/puppetlabs/code/environments
  production
    environment.conf
    manifests
      site.pp
      nodes.pp
    modules
      my_app
      ntp

The environment.conf file can customize the environment. Normally, Puppet uses site.pp and the other files in the manifests directory. To make Puppet read all the pp files in another directory, set the manifest option in environment.conf:

# /opt/puppetlabs/code/environments/production/environment.conf
manifest = puppet_manifests

In most circumstances, the manifest option need not be changed.

The site.pp file will include classes and instantiate defines from the modules. Puppet looks for modules in the modules subdirectory of the active environment. You can define additional subdirectories that hold the modules by setting the modulepath option in environment.conf:

# /opt/puppetlabs/code/environments/production/environment.conf
modulepath = modules:site-modules

The directory structure can be made more distinctive:

/opt/puppetlabs/code/environments/
  production
    manifests
    modules
      ntp
    site-modules
      my_app

Configuring environment locations

Puppet uses the production environment by default. This and the other environments are expected to be located in /opt/puppetlabs/code/environments. You can override this default by setting the environmentpath option in puppet.conf:

[main]
environmentpath = /etc/local/puppet/environments

With Puppet 3, you are required to set this option, as it is not yet the default. It is available from version 3.5. The earlier versions needed to configure the environments right in puppet.conf with their respective manifest and modulepath settings. These work just like the settings from environment.conf:

# in puppet.conf (obsolete since 4.0)
[testing]
manifest = /etc/puppet/environments/testing/manifests
modulepath = /etc/puppet/environments/testing/modules

For the special production environment, these Puppet setups use the manifest and modulepath settings from the [main] or [master] section. The old default configuration had the production environment look for the manifests and the modules right in /etc/puppet:

# Obsolete! Works only with Puppet 3.x!
[main]
manifest = /etc/puppet/site.pp
modulepath = /etc/puppet/modules

The sites that operate like this today should be migrated to the aforementioned directory environments in /opt/puppetlabs/code/environments or similar locations.

Obtaining and installing modules

Downloading existing modules is very common. Puppet Labs hosts a dedicated site for sharing and obtaining the modules - the Puppet Forge. It works just like RubyGems or CPAN and makes it simple for the user to retrieve a given module through a command-line interface. In the Forge, the modules are fully named by prefixing the actual module name with the author's name, such as puppetlabs-stdlib or ffrank-constraints.

The puppet module install command installs a module in the active environment:

root@puppetmaster# puppet module install puppetlabs-stdlib

Tip

The Testing your modules section has information on using different environments.

The current release of the stdlib module (authored by the user puppetlabs) is downloaded from the Forge and installed in the standard modules' location. This is the first location in the current environment's modulepath, which is usually the modules subdirectory. Specifically, the modules will most likely end up in the environments/production/modules directory.

Tip

The stdlib module in particular should be considered mandatory; it adds a large number of useful functions to the Puppet language. Examples include the keys, values, and has_key functions, which are essential for implementing the proper handling of hash structures, to name only a few. The functions are available to your manifests as soon as the module is installed - there is no need to include any class or other explicit loading. If you write your own modules that add functions, these are loaded automatically in the same way.

Modules' best practices

With all the current versions of Puppet, you should make it a habit to put all the manifest code into modules, with only the following few exceptions:

  • The node blocks
  • The include statements for very select classes that should be omnipresent (the most common design pattern does this in the so-called base role, however; see Chapter 8, Separating Data from Code Using Hiera, for the Roles and Profiles pattern)
  • Declarations of helpful variables that should have the same availability as the Facter facts in your manifests

This section provides details on how to organize your manifests accordingly. It also advises some design practices and strategies in order to test the changes to the modules.

Putting everything in modules

You might find some manifests in very old installations that gather lots of manifest files in one or more directories and use the import statements in the site.pp file, such as:

import '/etc/puppet/manifests/custom/*.pp'

All classes and defined types in these files are then available globally.

Note

This whole approach had scalability issues and has long been deprecated. The import keyword is missing from Puppet 4 and the newer versions.

It is far more efficient to give meaningful names to the classes and defined types so that Puppet can look them up in the collection of modules. The scheme has been discussed in an earlier section already, so let's just look at another example where the Puppet compiler encounters a class name, such as:

include ntp::server::component::watchdog

Puppet will go ahead and locate the ntp module in all the configured module locations of the active environment (path names in the modulepath setting). It will then try and read the ntp/manifests/server/component/watchdog.pp file in order to find the class definition. Failing this, it will try ntp/manifests/init.pp.

This makes compilation very efficient. Puppet dynamically identifies the required manifest files and includes only those for parsing. It also aids code inspection and development, as it is abundantly clear where you should look for specific definitions.

Tip

Technically, it is possible to stuff all of a module's manifests into its init.pp file, but you lose the advantages that a structured tree of module manifests offers.

Avoiding generalization

Each module should ideally serve a specific purpose. On a site that relies on Puppet to manage a diverse server infrastructure, there are likely modules for each respective service, such as apache, ssh, nagios, nginx, and so forth. There can also be site-specific modules such as users or shell_settings if the operations require this kind of fine-grained control. Such customized modules are sometimes just named after the group or the company that owns them.

The ideal granularity depends on the individual requirements of your setup. What you generally want to avoid are modules with names such as utilities or helpers, which serve as a melting pot for ideas that don't fit in any of the existing modules. Such a lack of organization can be detrimental to discipline and can lead to chaotic modules that include definitions that should have become their own respective modules instead.

Adding more modules is cheap. A module generally incurs no cost for the Puppet master operation, and your user experience will usually become more efficient with more modules, not less so. Of course, this balance can tip if your site imposes special documentation or other handling prerequisites on each module. Such rulings must then be weighed into the decisions about module organization.

Testing your modules

Depending on the size of your agent network, some or many of your modules can be used by a large variety of nodes. Despite these commonalities, these nodes can be quite different from one another. A change to a central module, such as ssh or ntp, which are likely used by a large number of agents, can have quite extensive consequences.

The first and the most important tool for testing your work is the --noop option for Puppet. It works for puppet agent, as well as puppet apply. If it is given on the command line, Puppet will not perform any necessary sync actions, and merely present the respective line of output to you instead. There is an example of this in Chapter 1, Writing Your First Manifests.

When using a master instead of working locally with puppet apply, a new problem arises, though. The master is queried by all your agents. Unless all the agents are disabled while you are testing a new manifest, it is very likely that one will check in and accidentally run the untested code.

Tip

In fact, even your test agent can trigger its regular run while you are logged in, transparently in the background.

It is very important to guard against such uncontrolled manifest applications. A small mistake can damage a number of agent machines in a short time period. The best way to go about this is to define multiple environments on the master.

Safe testing with environments

Besides the production environment, you should create at least one testing environment. You can call it testing or whatever you like. When using the directory environments, just create its directory in environmentpath.

Such an additional environment is very useful for testing changes. The test environment or environments should be copies of the production data. Prepare all the manifest changes in testing first. You can make your agents test this change before you copy it to production:

root@agent# puppet agent --test --noop --env testing

You can even omit the noop flag on some or all of your agents so that the change is actually deployed. Some subtle mistakes in the manifests cannot be detected from an inspection of the noop output, so it is usually a good idea to run the code at least once before releasing it.

Tip

Environments are even more effective when used in conjunction with source control, especially distributed systems such as git or mercurial. Versioning your Puppet code is a good idea independently of environments and testing—this is one of the greatest advantages that Puppet has to offer you through its Infrastructure as Code paradigm.

Using environments and the noop mode form a pragmatic approach to testing that can serve in most scenarios. The safety against erroneous Puppet behavior is limited, of course. There are more formal ways of testing the modules:

Explaining these tools in detail is beyond the scope of this book.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset