OpenStack Heat is the orchestration project of OpenStack, which allows to describe and provision OpenStack resources based on Heat templates, similar to Amazon CloudFormation. For a research project, I recently had the requirement to assign a floating IP address to every instance of a Heat autoscaling group (ASG). Given the fact that such setup is contradictory to the common practice having a load balancer in front of an ASG, this costed me some time to figure out.
Creating a Single Instance Using Heat
Setting up a single instance using a Heat template (HOT) is pretty easy, straightforward and also covered in the Heat templates repository:
# single_server_with_floating_ip.yaml
# (abbreviated example based on hot/servers_in_existing_neutron_net.yaml)
parameters:
# ~~~snip~~~
network:
type: string
description: The network for the VM
constraints:
- {custom_constraint: neutron.network}
public_net:
type: string
description: ID of public network for which floating IP addresses will be allocated
constraints:
- {custom_constraint: neutron.network}
resources:
server1:
type: OS::Nova::Server
properties:
name: Server1
image: { get_param: image }
networks:
- port: { get_resource: server1_port }
server1_port:
type: OS::Neutron::Port
properties:
network_id: { get_param: network }
security_groups: [{ get_resource: server_security_group }]
server1_floating_ip:
type: OS::Neutron::FloatingIP
properties:
floating_network_id: { get_param: public_net }
port_id: { get_resource: server1_port }As we can see, the server1_floating_ip (of type OS::Neutron::FloatingIP) is NAT’ed to the server1_port which in turn is assigned to the server instance (type OS::Nova::Server) that we instantiate.
Creating an Autoscaling Group
In order to create an ASG of multiple instances, each only with a private IP, we also find help in the Heat templates repository:
# abbreviated example based on hot/asg_of_servers.yaml
parameters:
# ~~~snip~~~
image:
type: string
description: Name or ID of the image to use for the instances.
network:
type: string
description: The network for the VM
default: private
resources:
asg:
type: OS::Heat::AutoScalingGroup
properties:
resource:
type: OS::Nova::Server
properties:
key_name: { get_param: key_name }
image: { get_param: image }
flavor: { get_param: flavor }
networks: [{network: {get_param: network} }]
min_size: 1
desired_capacity: 3
max_size: 10We see the AutoScalingGroup defined with the Nova server instance being scaled up and down (between 1 and 10 instances).
Using the scale up and down URLs provided, one can test the functionality of auto scaling.
Recalling our goal, namely to assign a floating IP address to every instance of the ASG (and also to the ones created during scale-out), it looks intuitively right to extend the resource block of the previous template. However, only one resource can be given to the AutoScalingGroup. For our use case, we also need the floating IP resources to be added and destroyed on demand.
Scaling a Complete Stack
The solution to my problem was finally hidden in one of the other examples, the ASG of stacks.
Essentially, Heat can also scale a complete stack (defined by its own template file).
So what we now do is essentially to treat the server with its floating IP as defined in the first listing (single_server_with_floating_ip.yaml) as entity of scale and pass through all parameters:
# asg_of_server_with_floating.yaml
# (abbreviated example based on hot/asg_of_stacks.yaml)
parameters:
# ~~~snip~~~
image:
type: string
description: Name or ID of the image to use for the instances.
network:
type: string
description: The network for the VM
constraints:
- {custom_constraint: neutron.network}
public_net:
type: string
description: ID of public network for which floating IP addresses will be allocated
constraints:
- {custom_constraint: neutron.network}
resources:
asg:
type: OS::Heat::AutoScalingGroup
properties:
resource:
# this refers to the file previously described
type: server_with_floating.yaml
properties:
image: { get_param: image }
network: { get_param: network }
public_net: { get_param: public_net}
min_size: 1
desired_capacity: 3
max_size: 10As the type of of resource, we can also supply a file name. That’s.. let me think.. not obvious?
After long search, was able to find this behavior documented under Template composition.
Mind the public_net parameter defining the public network ID, from which floating IPs are assigned.
Once we trigger a scale-out, we can see antoehr instance including a floating IP being added after some seconds:

I am not aware of a way to launch such stack using the Horizon dashboard, except when the inner template is referenced via an HTTP(S) URL. Instead, one can launch it using the command line client:
$ openstack stack create my-test-stack --template asg_of_server_with_floating.yaml -e environment.yamlAll definition of the required parameters, i.e., image and network IDs, are happening in the environment.yaml file.
While my use case was a little academic (why in the world should something scale inside an OpenStack cloud and be directly reachable from the outside world), I think this (to me still unknown) concept of template composition helps solving many more problems.
The complete code can be found in this gist.
(cover image by sipa on pixabay.com)