Bootstrapping puppet from heat

This tutorial assumes the following:

  • You have created a basic network setup in the Catalyst Cloud.

  • You have access to a server that is acting as the Puppet Master.

  • You have installed the OpenStack command line tools and sourced an openrc file, as explained at Installation on Linux and Mac.

  • You have a basic understanding of Heat templates as shown at Command line methods.

Introduction

In this tutorial, you will see how to add a new server to an existing Catalyst Cloud network and configure it with Puppet and have it check in to the Puppet Master.

To achieve this, you will create a heat template that will handle the creation of the instance and then run a nested cloud-config script via cloud-init that will handle the provisioning of Puppet on the new server.

Setup

You will make use of Heat template to deploy a single instance into an existing network hosted in the Catalyst Cloud. In order to make this work, you need to retrieve the relevant network IDs and add them into the template.

The two networks you will be connecting to are front-end and public-net. To find these values, you need to run the following OpenStack commands.

$ openstack network list -c ID -c Name
+--------------------------------------+------------+
| ID                                   | Name       |
+--------------------------------------+------------+
| 74be55e6-b303-473c-ac1a-xxxxxxxxxxxx | mgmt-net   |
| 849ab1e9-7ac5-4618-8801-xxxxxxxxxxxx | public-net |
| e7adca02-5b8b-4c2e-9946-xxxxxxxxxxxx | front-end  |
+--------------------------------------+------------+

You also need to know the ID of the front-end network subnet, in order to create a port and assign a floating IP to the server.

$ openstack subnet list -c ID -c Name
+--------------------------------------+-------------+
| ID                                   | Name        |
+--------------------------------------+-------------+
| 279a71ca-6772-4235-bbb4-xxxxxxxxxxxx | front-end   |
| 450cb9f7-b297-40fe-a855-xxxxxxxxxxxx | mgmt-subnet |
+--------------------------------------+-------------+

Implementation

This snippet ( included in the template below ) is responsible for passing the cloud-config script puppet_bootstrap.yaml to cloud-init

user_data_format: RAW
 user_data:
  get_file: /home/user1/cloud/puppet_bootstrap.yaml

Here is the Heat template that is responsible for creating the new instance. The network ID values found previously have been added to the relevant parameters as defaults. It is also possible to pass these values in as arguments from the command line, as shown here.

heat_template_version: 2013-05-23

description: >
  Heat template to deploy a single server into an existing Neutron tenant
  network, assign a floating IP addresses and ensure it is accessible from
  the public network.

  It also uses a cloud-init script to bootstrap the server with Puppet.

parameters:
  key_name:
    type: string
    description: Name of keypair to assign to servers
    default: mykey
  image:
    type: string
    description: Name of image to use for servers
    default: ubuntu-14.04-x86_64
  flavor:
    type: string
    description: Flavor to use for servers
    default: c1.c1r1
  public_net_id:
    type: string
    description: >
      ID of public network for which floating IP addresses will be allocated
    default: 849ab1e9-7ac5-4618-8801-xxxxxxxxxxxx
  private_net_id:
    type: string
    description: ID of private network into which servers get deployed
    default: e7adca02-5b8b-4c2e-9946-xxxxxxxxxxxx
  private_subnet_id:
    type: string
    description: ID of private sub network into which servers get deployed
    default: 279a71ca-6772-4235-bbb4-xxxxxxxxxxxx

resources:
  server1:
    type: OS::Nova::Server
    properties:
      name: server1
      image: { get_param: image }
      flavor: { get_param: flavor }
      key_name: { get_param: key_name }
      networks:
        - port: { get_resource: server1_port }
      user_data_format: RAW
      user_data:
        get_file: /home/user1/cloud/puppet_bootstrap.yaml

  server1_port:
    type: OS::Neutron::Port
    properties:
      network_id: { get_param: private_net_id }
      fixed_ips:
        - subnet_id: { get_param: private_subnet_id }
      security_groups: [{ get_resource: server_security_group }]

  server1_floating_ip:
    type: OS::Neutron::FloatingIP
    properties:
      floating_network_id: { get_param: public_net_id }
      port_id: { get_resource: server1_port }

  server_security_group:
    type: OS::Neutron::SecurityGroup
    properties:
      description: Add security group rules for server
      name: security-group
      rules:
        - remote_ip_prefix: 0.0.0.0/0
          protocol: tcp
          port_range_min: 22
          port_range_max: 22
        - remote_ip_prefix: 0.0.0.0/0
          protocol: icmp

outputs:
  server1_private_ip:
    description: IP address of server1 in private network
    value: { get_attr: [ server1, first_address ] }
  server1_public_ip:
    description: Floating IP address of server1 in public network
    value: { get_attr: [ server1_floating_ip, floating_ip_address ] }

This is the cloud-init script that is called via the user-data command. It ensures that the Puppet package is installed and sets some basic configuration to ensure that the server can identify itself and locate the Puppet Master.

It performs the following tasks:

  • creates a host entry for the Puppet Master

  • adds environment and Puppet Master server variables to puppet.conf

  • runs Puppet agent with an optional 120 second wait for the certificate request to be signed by the Puppet Master

#cloud-config

# This is an example of how to have Puppet agent installed and run
# when the instance boots for the first time.
# It needs to passed in valid YAML format to user-data when starting
# the instance.

# bootcmd required as it runs very early in the boot process
# add a host entry so server can correctly identify itself
bootcmd:
  - echo 127.0.0.1 server1.example.co.nz server1 >> /etc/hosts

# Install additional packages on first boot
# if packages are specified then apt_update will be set to true and run
# first
packages:
 - puppet

puppet:
 # Every key present in the conf object will be added to puppet.conf:
 # [name]
 # subkey=value
  conf:
    agent:
      server: "puppet.example.co.nz"
      environment: dev

# add Puppet Master host entry and do initial Puppet run
runcmd:
  - echo 10.20.40.12 puppet.example.co.nz puppet >> /etc/hosts
  - puppet agent --test --server puppet.example.co.nz --waitforcert 120

# Capture all subprocess output into a logfile
# Useful for troubleshooting cloud-init issues
output: {all: '| tee -a /var/log/cloud-init-output.log'}

Creating the server

To create the server, run the following Heat command. This will create a new server called server1 in a stack named puppet-slave-stack

openstack stack create -t /home/user1/cloud/puppet_slave.yaml puppet-slave-stack

Here’s how to check the progress of your deployment:

openstack console log show server1

Final note

Unless your Puppet Master is configured to automatically sign agent certificate requests, you will need to sign your new server’s cert before the first Puppet run will complete.