Tuesday, 5 May 2015

Heat SoftwareConfig resources - primer/overview.

In this post, I'm going to provide an overview of Heat's Software Configuration resources, as a preface to digging in more detail into the structure of TripleO heat templates, which leverage SoftwareConfig functionality to install and configure the deployed OpenStack cloud.

Heat has supported SoftwareConfig and SoftwareDeployment resources since the Icehouse release, in an effort to provide flexible and non-opinionated abstractions which enable integration with existing software configuration tools and scripts.


The key concepts and some examples are described in our user guide, but what follows is more of a primer, to provide necessary context before we get in to decomposing the TripleO heat templates.  If what follows looks a little scary, check out my introductory (very simple) screencast, which was recorded for the Heat Beyond the Basics session at the Paris OpenStack summit.

Heat SoftwareConfig resources


There are three resources necessary in a typical software configuration scenario:

  1. An OS::Heat::SoftwareConfig resource - this encapsulates the config to be applied, e.g a script, puppet manifest, or any other config definition format you care to use.  This is just a wrapper for the config to apply, optionally parameterized with input values, it doesn't actually configure anything.
  2. An OS::Heat::SoftwareDeployment resource - this is the thing which actually applies the config from (1) - when it moves to and IN_PROGRESS state, it makes the config available to the specified server.  By default, the deployment will stay in the IN_PROGRESS state until a signal is received via the Heat API, notifying the service of success (or failure..) applying the config.
  3. An OS::Nova::Server resource - this is the instance (or physical server in the case of TripleO deploying via Nova and Ironic) being configured, it must contain some tools to support SoftwareConfig, as discussed below, and define the user_data_format property to enable SoftwareConfig.

There are also OS::Heat::StructuredConfig and OS::Heat::StructuredDeployment resources, which are basically identical to SoftwareConfig/SoftwareDeployment resources, except the config is defined as a map, rather than a string (which is useful for some tools such as os-apply-config, which consumes a map of config data, rather than a string such as is used e.g applying a script or a puppet manifest).  So I'll refer only to SoftwareConfig/SoftwareDeployment from here on, but all the concepts are applicable to both.

SoftwareDeployment flow




 
The server being configured requires some agents in order to collect and process the configuration made available via the deployment.  Typically, this works as follows:

  1. os-collect-config polls the Heat API for updated resource metadata associated with the OS::Nova::Server resource
  2. When metadata is updated, os-refresh-config runs, and triggers an element called heat-config.
  3. heat-config then uses the "group" property defined in the SoftwareConfig properties to process applying the configuration via a hook script.  Heat provides (via the heat-templates repo) a variety of ready-made hook scripts for some popular tools, but it's simple to write your own if needed, and it involves no changes to Heat, only a script inside the image you deploy.
  4. On completion, the hook uses "heat-config-notify" to send a signal back to Heat, which includes the return code of the thing we ran, along with stdout/stderr from the tool invoked by the hook script.  This information is then made available via "heat deployment-show" via the Heat API.
  5. If the signal notified success, we move the SoftwareDeployment to COMPLETE state, e.g CREATE_COMPLETE.  If it failed, we move it to e.g CREATE_FAILED state.

SoftwareDeployment HOT template definition

We have example templates for many popular tools, but one thing to emphasize is that the template definition is not dependent on the tool doing the configuration - the coupling between the config to apply and the tool applying it only happens inside the instance via the heat-config hook logic.

Looking at the puppet example step-by step:

1. Define the SoftwareConfig resource

  config:
    type: OS::Heat::SoftwareConfig
    properties:
      group: puppet
      inputs:
      - name: foo
        default: aninput
      - name: bar
      outputs:
      - name: result
      config:
        get_file: config-scripts/example-puppet-manifest.pp

Here, we can see the following:
  • We define the SoftwareConfig, which references a puppet manifest via get_file.  
  • We parameterize applying the manifest by providing some input values, which can specify a default value
  • The "group" is specified as "puppet", which will enable heat-config to correctly apply the manifest using the heat-config-puppet hook.
  • We specify an output - this means inside the manifest we can reference the special "heat_outputs_path" input, and write a file containing a result related to this output.
A SoftwareConfig, when created, goes to CREATE_COMPLETE immediately, there is no dependency on applying the config to a server, thus a config once defined may be applied to multiple servers (potentially with different input parameters).

2. Define the Server resource

   server:
    type: OS::Nova::Server
    properties:
      image:animage
      flavor:m1.small

      user_data_format: SOFTWARE_CONFIG

Here, the some things to note:
  • The image provided must contain the tools previously discussed (os-collect-config, os-refresh-config, heat-config and whatever hooks you need for the "group" properties you want to specify)
  • user_data_format must be set to "SOFTWARE_CONFIG"
  • user_data may still be specified - this is useful where you want to combine first-boot configuration (e.g via cloud-init) with subsequent application deployment via SoftwareConfig.  A recent TripleO patch illustrates this approach.
Optionally you can also specify the transport used for polling metadata via software_config_transport - by default heat will poll via the heat-api-cfn API, but you can also poll via the native heat-api, or in Kilo Heat, via Swift.

 

3. Define the SoftwareDeployment resource

  deployment:
    type: OS::Heat::SoftwareDeployment
    properties:
      config:
        get_resource: config
      server:
        get_resource: server
      input_values:
        foo:abc
        bar:xyz
      actions:
      - CREATE


I've put the SoftwareDeployment resource last on purpose - to highlight the normal deployment flow:
  • heat creates the SoftwareConfig, then the Server, then the Deployment
  • The Deployment applies the config, depending on both the SoftwareConfig and Server resources (implicitly via the get_resource references).
  • The Deployment resource status depends on the status of the signal sent back to heat after applying the config (via the heat-config hook).
Note the "actions" property is optional (defaults to applying the config on both CREATE and UPDATE actions, but it can be used to specify that, for example you only want configuration to happen on create and not update (as above), or perhaps that you only want to run some cleanup on DELETE, for example unregistering the server from some external service.

input_values is used to provide the values for the parameters defined in the SoftwareConfig resource - thus it's perfectly fine for multiple Deployment resources to reference the same Config resource (potentially with different input_values and/or actions specified), but each deployment references exactly one Config (e.g a SoftwareDeployment cannot apply multiple SoftwareConfig resources, only one).

Dealing with dependencies

 

A common pattern is doing a series of configuration steps, for example configure a database, then some application that uses the database (and requires it to be installed and configured).

There are a couple of ways to handle this:

  1. SoftwareDeployment resources have a "name" property, which can influence the sort-order so that, for example, heat-config will apply "config1" before "config2".
  2. Template directive "depends_on" can be used to specify an explicit dependency between two (or more) SoftwareDeployment resources (Note: the dependency is between the *deployment* resources, not the SoftwareConfig resources!)
My preference is to use "depends_on" in most cases - it provides the most explicit control of deployment serialization, and makes the ordering very clear from the template level.

Conclusion and further resources

Hopefully that concludes a reasonable overview of heat SoftwareConfiguration capabilities.  For further information, please see the heat documentation, the user guide and template guide and example templates are a good starting point.

If you're feeling brave, you can also dive into the TripleO heat templates, which make extensive use of SoftwareConfig/StructuredConfig and SoftwareDeployment/Structured deployment resources.  More on TripleO specifically in a future post! :)

1 comment: