Programming, Technology

Getting started with OpenStack’s Heat

Introduction

OpenStack’s Heat is the project’s infrastructure orchestration component, and can simplify deploying your project on a cloud platform in a way that’s repeatable and easy to understand.  If you’ve come from the Amazon AWS world then it’s analogous to CloudFormation, and indeed it provides compatibility with this service making migration from AWS to OpenStack a little less painful.  However, Heat has its own templating format and that’s what we’ll walk through today.

This post is a quick tutorial on getting started with your first Heat template, and will deploy a pair of webservers together with loadbalancer as an example.

Heat Templates

Let’s jump right in and take a look at the contents of the template that we’ll use to deploy our infrastructure. These template files are typically formatted in YAML and comprise three main sections:

  • Parameters
  • Resources
  • Outputs

 

heat_template_version: 2014-10-16

description: Demo template to deploy a pair of webservers and a loadbalancer

parameters:
key_name:
type: string
description: Name of SSH keypair to be used for compute instance
flavor:
type: string
default: dc1.1x1.20
constraints:
- allowed_values:
- dc1.1x1.20
- dc1.1x2.20
description: Must be a valid Data News Blog Compute Cloud flavour
image:
type: string
default: 6c3047c6-17b1-4aaf-a657-9229bb481e50
description: Image ID
networks:
type: string
description: Network IDs for which the instances should have an interface attached
default: f77c6fdb-72ad-402f-9f1b-6bf974c3ff77
subnet:
type: string
description: ID for the subnet in which we want to create our loadbalancer
default: a8d1edfe-ac8c-49b0-a5c2-c72fa61decd2
user_data:
type: string
default: |
#cloud-config
packages:
- nginx
name:
type: string
description: Name of instances
default: webserver

resources:
webserver0:
type: OS::Nova::Server
properties:
key_name: { get_param: key_name }
flavor: { get_param: flavor }
image: { get_param: image }
networks: [{ network: { get_param: networks } }]
user_data: { get_param: user_data }
user_data_format: RAW

webserver1:
type: OS::Nova::Server
properties:
key_name: { get_param: key_name }
flavor: { get_param: flavor }
image: { get_param: image }
networks: [{ network: { get_param: networks } }]
user_data: { get_param: user_data }
user_data_format: RAW

lb_pool:
type: OS::Neutron::Pool
properties:
protocol: HTTP
subnet_id: { get_param: subnet }
lb_method: ROUND_ROBIN
vip:
protocol_port: 80

lb_members:
type: OS::Neutron::LoadBalancer
properties:
pool_id: { get_resource: lb_pool }
members: [ { get_resource: webserver0 }, { get_resource: webserver1 } ]
protocol_port: 80

outputs:
vip_ip:
description: IP of VIP
value: { get_attr: [ lb_pool, vip, address ] }

 

The first line – heat_template_version: 2014-10-16 – specifies the version of Heat’s templating language we’ll be using, with an expectation that within this template we could be defining resources available up to and including the Juno release.

The first actual section – parameters – let’s us pass in various options as we create our Heat ‘stack’. Most of these are self-explanatory but give our template some flexibility should we need to do some customisation. When we provision our Heat stack there’s a few options we’ll have to specify such as the SSH key name we’ll expect to use with our instances, and various values we can override such as the network we want to attach to, the subnet in which to create our loadbalancer, and so on. Where applicable there’s some sensible defaults in there – in this example the IDs for network and for subnet are taken from my own demonstration project.

The next section – resources – is where most of the actual provisioning magic actually happens. Here we define our two webservers as well as a loadbalancer. Each webserver is of a particular type – OS::Nova::Server – and has various properties passed to it – all of which are retrieved via the get_param intrinsic function. The lb_pool and lb_members resources are similarly created, members in the latter being a list of our webserver resources.

Finally, the outputs section in our example uses another intrinsic function – get_attr – which returns a value from a particular object or resource. In our case this is the IP address of our load-balancer.

Putting it all together

Now that we have our template, we can look at using the heat command-line client to create our stack. Its usage is very straightfoward; Assuming we’ve saved the above template to a file called heatdemo.yaml, to create our stack all we have to do is the following:

$ heat stack-create Webservers --template-file heatdemo.yaml -P key_name=deadline
-P flavor='dc1.1x1.20' -P name=webserver
+--------------------------------------+------------+--------------------+----------------------+
| id | stack_name | stack_status | creation_time |
+--------------------------------------+------------+--------------------+----------------------+
| 433026fc-b543-4104-902f-d335e1ea189d | Webservers | CREATE_IN_PROGRESS | 2015-04-16T15:26:52Z |
+--------------------------------------+------------+--------------------+----------------------+

The stack-create option to the heat command takes various options, such as the template file we’d like to use. We can also inject various parameters using the command-line at this point, and in my example I’m specifying the SSH key name I wish to use as well as the size (flavor) of instance and a name for each machine that’s created. We can check on the stack’s progress as it’s created by looking in Horizon or again using the heat command:

$ heat stack-show Webservers | grep -i status
| stack_status | CREATE_COMPLETE |
| stack_status_reason | Stack CREATE completed successfully |

Looks good so far – let’s take a look in Horizon. Under Project -> Orchestration -> Stacks we see our newly-created ‘Webserver’ stack. Clicking on that gives us a visual representation of its topology:

Clicking on ‘Overview’ summarises the various details for us, and in the ‘Outputs’ section we can see the IP of the VIP that was configured as part of the stack’s creation. Let’s test that everything’s working as it should from another host on the same network:/

$ nova list | grep -i webserv
| efab9c99-ddc1-4cee-abfb-c3756233418e | Webservers-webserver0-ano27iof4iem | ACTIVE | - | Running | private=192.168.2.34 |
| d3eee79d-7ed4-4b27-8512-16cf201f82f3 | Webservers-webserver1-yiaeqoaxrcq5 | ACTIVE | - | Running | private=192.168.2.33 |
$ neutron lb-vip-list
+--------------------------------------+-------------+--------------+----------+----------------+--------+
| id | name | address | protocol | admin_state_up | status |
+--------------------------------------+-------------+--------------+----------+----------------+--------+
| d943c34b-8299-46ad-88e5-6f7d9d26b769 | lb_pool.vip | 192.168.2.32 | HTTP | True | ACTIVE |
+--------------------------------------+-------------+--------------+----------+----------------+--------+
$ ping -c 1 192.168.2.32
PING 192.168.2.32 (192.168.2.32) 56(84) bytes of data.
64 bytes from 192.168.2.32: icmp_seq=1 ttl=63 time=1.34 ms

--- 192.168.2.32 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.348/1.348/1.348/0.000 ms
$ nc -v -w 1 192.168.2.32 80
Connection to 192.168.2.32 80 port [tcp/http] succeeded!
$ curl -s 192.168.2.32:80 | grep -i welcome
<h1>Welcome to nginx!</h1>

Here we’ve verified that there’s two instances launched, that we’ve a loadbalancer and VIP configured, and then we’ve done a couple of basic connectivity tests to make sure the VIP is up and passing traffic to our webservers. The nginx default landing page that we can see from the output of the curl command means everything looks as it should.

Cleaning up

In order to remove our stack and all of its resources, the heat command really couldn’t be any simpler:

$ heat stack-delete Webservers
+--------------------------------------+------------+--------------------+----------------------+
| id | stack_name | stack_status | creation_time |
+--------------------------------------+------------+--------------------+----------------------+
| 433026fc-b543-4104-902f-d335e1ea189d | Webservers | DELETE_IN_PROGRESS | 2015-04-16T15:26:52Z |
+--------------------------------------+------------+--------------------+----------------------+

After a few seconds, another run of heat stack-list will show that the ‘Webservers’ stack no longer exists, nor does any of its resources:

$ nova list | grep -i webserv
zsh: done nova list |
zsh: exit 1 grep -i webserv
$ neutron lb-vip-list

Summary

This example shows how straightforward it can be to orchestrate infrastructure resources on OpenStack using Heat. This is a very basic and limited example – it’s possible to do much, much more with Heat including defining elastic and auto-scaling pieces of infrastructure, but hopefully this provides you with some insight and inspiration into how such a tool can be useful.