Layer 4 load balancing

The following section details different methods that you can use to create a basic layer 4 loadbalancer for your instances.

Requirements

While each of the examples below will have their own set of requirements, there are some things that you need to prepare no matter which method you are wanting to use. These include:

  • Sourcing an openrc file

  • Uploading or creating your own SSH key

  • Whitelisting your IP address so that you can interact with the cloud from your command line.

  • Installing the necessary tools for your chosen example. You can find instructions for this in the starting section of the documents.

Once these requirements are met, you can continue with any of the examples below:

Preparation

This example illustrates how to load balance traffic on port 80 and 443 for two compute instances running a mock Python Flask web application.

If you already have two or more compute instances running a web application listening on ports 80 and 443, you can skip this step. Otherwise, launch two compute instances and follow the instructions below to run a simple Flask web application in each.

The Flask app binds to ports 80 and 443 respectively and will send a simple HTTP response when a request is received on the listening ports.

First we will have to create a copy of the flask_app.py script (shown below) on each server. The easiest way to do this, is to have a program like vim so you can copy paste the code into the new file you create on your instances.

Note

You must also make sure that you have a security instance that allows access to your instances from both the HTTP port (80) and the HTTPS port (443) otherwise the listeners and members (which are explained further on) won’t be able to access your instances, meaning you will not be able to test the results of the load-balancer.

script flask_app.py

import argparse
import socket
import sys

from flask import Flask


def check_arg(args=None):
    parser = argparse.ArgumentParser(
        description='Simple Flask app to test load balancer service')
    parser.add_argument('-p', '--port',
                        required='True',
                        help='port for the web server to bind to')
    parser.add_argument('-u', '--url',
                        default=None,
                        help='url for the server to respond with')
    results = parser.parse_args(args)
    return (results.port, results.url)


host_name = socket.gethostname()
host_ip = socket.gethostbyname(host_name)

app = Flask(__name__)


@app.route("/")
def hello():
    if server_url is None:
        return "Server : {} @ {}".format(host_name, host_ip)
    else:
        return "Welcome to {} @ {}".format(server_url, host_ip)


@app.route("/health")
def health():
    return "healthy!"


if __name__ == '__main__':
    server_port, server_url = check_arg(sys.argv[1:])
    app.run(host='0.0.0.0', port=server_port)

Once you have a copy of the flask_app.py file, ssh to your instances and follow the instructions below to install the required dependencies.

Note

In order to be able to bind to ports 80 & 443 the application needs to run as the root user.

# sudo to the root account
$ sudo -i
# install the required system packages
$ apt install virtualenv python-pip

# create a virtual environment
$ virtualenv venv

# activate the virtual environment
$ source venv/bin/activate

# install Flask into the virtual environment
$ pip install flask

# exit the virtual environment
$ deactivate

In each compute instance, start two instances of the application (each in their own terminal session) ensuring that there is one listening on port 80 and the other on port 443.

# sudo to the root account
$ sudo -i

# activate the virtual environment
$ source venv/bin/activate

# run the flask app - providing the correct port numbers
$ python flask_app.py -p <port_number>

The output for the services running on port 80 will look similar to this:

root@server-1:~# python flask_app.py -p 80
 * Serving Flask app "flask_app" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
10.0.0.9 - - [28/Jun/2018 06:09:43] "GET /health HTTP/1.0" 200 -

Create a load balancer

Now, we create the loadbalancer. It will be called lb_test_1 and it’s virtual IP address (VIP) will be attached to the local subnet private-subnet.

Note

If you wish to run the tests included with this example, you will need to have root access on the test instances. If you do not have that level of access then substitute 8080 and 8443 wherever you see 80 and 443 respectively.

$ source example-openrc.sh
$ export SUBNET=`openstack subnet list --name private-subnet -f value -c ID`
$ openstack loadbalancer create --vip-subnet-id ${SUBNET} --name lb_test_1
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| admin_state_up      | True                                 |
| created_at          | 2017-11-02T21:32:52                  |
| description         |                                      |
| flavor              |                                      |
| id                  | 547deffe-55fc-49be-ac52-e24c7fd22ece |
| listeners           |                                      |
| name                | lb_test_1                            |
| operating_status    | OFFLINE                              |
| pools               |                                      |
| project_id          | a3a9af91b9e547739bfcb02cc2acded0     |
| provider            | octavia                              |
| provisioning_status | PENDING_CREATE                       |
| updated_at          | None                                 |
| vip_address         | 10.0.0.3                             |
| vip_network_id      | 6e743092-a06a-4234-9fce-25b747b14e9e |
| vip_port_id         | 693039f6-1896-4094-8f96-18d0fbcfb99e |
| vip_subnet_id       | 1c221166-3cb3-4534-915a-b75220ec1873 |
+---------------------+--------------------------------------+

Create a listener

Once the operating_status of the load balancer is ACTIVE, we will create two listeners, both will use TCP as their protocol and they will listen on ports 80 and 443 respectively.

$ openstack loadbalancer list
+--------------------------------------+-----------+----------------------------------+-------------+---------------------+----------+
| id                                   | name      | project_id                       | vip_address | provisioning_status | provider |
+--------------------------------------+-----------+----------------------------------+-------------+---------------------+----------+
| 547deffe-55fc-49be-ac52-e24c7fd22ece | lb_test_1 | a3a9af91b9e547739bfcb02cc2acded0 | 10.0.0.16   | ACTIVE              | octavia  |
+--------------------------------------+-----------+----------------------------------+-------------+---------------------+----------+
$ openstack loadbalancer listener create --name 80_listener --protocol TCP --protocol-port 80 lb_test_1
+---------------------------+--------------------------------------+
| Field                     | Value                                |
+---------------------------+--------------------------------------+
| admin_state_up            | True                                 |
| connection_limit          | -1                                   |
| created_at                | 2017-11-08T22:42:28                  |
| default_pool_id           | None                                 |
| default_tls_container_ref | None                                 |
| description               |                                      |
| id                        | de21c777-1c98-4061-aa86-f4b9faa7ea04 |
| insert_headers            | None                                 |
| l7policies                |                                      |
| loadbalancers             | 547deffe-55fc-49be-ac52-e24c7fd22ece |
| name                      | 80_listener                          |
| operating_status          | OFFLINE                              |
| project_id                | a3a9af91b9e547739bfcb02cc2acded0     |
| protocol                  | TCP                                  |
| protocol_port             | 80                                   |
| provisioning_status       | PENDING_CREATE                       |
| sni_container_refs        | []                                   |
| updated_at                | None                                 |
+---------------------------+--------------------------------------+
$ openstack loadbalancer listener create --name 443_listener --protocol TCP --protocol-port 443 lb_test_1
+---------------------------+--------------------------------------+
| Field                     | Value                                |
+---------------------------+--------------------------------------+
| admin_state_up            | True                                 |
| connection_limit          | -1                                   |
| created_at                | 2018-06-25T01:13:06                  |
| default_pool_id           | None                                 |
| default_tls_container_ref | None                                 |
| description               |                                      |
| id                        | 724816cc-2dbd-42c8-9b61-19f49fa48165 |
| insert_headers            | None                                 |
| l7policies                |                                      |
| loadbalancers             | bfc1a299-3ec2-4681-974a-b7c47b52529f |
| name                      | 443_listener                         |
| operating_status          | OFFLINE                              |
| project_id                | eac679e4896146e6827ce29d755fe289     |
| protocol                  | TCP                                  |
| protocol_port             | 443                                  |
| provisioning_status       | PENDING_CREATE                       |
| sni_container_refs        | []                                   |
| timeout_client_data       |                                      |
| timeout_member_connect    |                                      |
| timeout_member_data       |                                      |
| timeout_tcp_inspect       |                                      |
| updated_at                | None                                 |
+---------------------------+--------------------------------------+

To view the newly created listeners.

$ openstack loadbalancer listener list
+--------------------------------------+-----------------+--------------+----------------------------------+----------+---------------+----------------+
| id                                   | default_pool_id | name         | project_id                       | protocol | protocol_port | admin_state_up |
+--------------------------------------+-----------------+--------------+----------------------------------+----------+---------------+----------------+
| 380ea1df-e043-4167-90ca-03f044b620a3 | None            | 80_listener  | eac679e4896146e6827ce29d755fe289 | TCP      |            80 | True           |
| 724816cc-2dbd-42c8-9b61-19f49fa48165 | None            | 443_listener | eac679e4896146e6827ce29d755fe289 | TCP      |           443 | True           |
+--------------------------------------+-----------------+--------------+----------------------------------+----------+---------------+----------------+

Create a pool

Then add a pool to each listener.

$ openstack loadbalancer pool create --name 80_pool --listener 80_listener --protocol TCP --lb-algorithm ROUND_ROBIN
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| admin_state_up      | True                                 |
| created_at          | 2018-06-25T01:30:17                  |
| description         |                                      |
| healthmonitor_id    |                                      |
| id                  | 96dde7c5-77c5-4ffe-9542-226714f5c58d |
| lb_algorithm        | ROUND_ROBIN                          |
| listeners           | 380ea1df-e043-4167-90ca-03f044b620a3 |
| loadbalancers       | bfc1a299-3ec2-4681-974a-b7c47b52529f |
| members             |                                      |
| name                | 80_pool                              |
| operating_status    | OFFLINE                              |
| project_id          | eac679e4896146e6827ce29d755fe289     |
| protocol            | TCP                                  |
| provisioning_status | PENDING_CREATE                       |
| session_persistence | None                                 |
| updated_at          | None                                 |
+---------------------+--------------------------------------+
$ openstack loadbalancer pool create --name 443_pool --listener 443_listener --protocol TCP --lb-algorithm ROUND_ROBIN
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| admin_state_up      | True                                 |
| created_at          | 2018-06-25T01:31:04                  |
| description         |                                      |
| healthmonitor_id    |                                      |
| id                  | da26844d-921d-4045-af24-017f07107934 |
| lb_algorithm        | ROUND_ROBIN                          |
| listeners           | 724816cc-2dbd-42c8-9b61-19f49fa48165 |
| loadbalancers       | bfc1a299-3ec2-4681-974a-b7c47b52529f |
| members             |                                      |
| name                | 443_pool                             |
| operating_status    | OFFLINE                              |
| project_id          | eac679e4896146e6827ce29d755fe289     |
| protocol            | TCP                                  |
| provisioning_status | PENDING_CREATE                       |
| session_persistence | None                                 |
| updated_at          | None                                 |
+---------------------+--------------------------------------+

Add members

Now add the members to the pools… When creating members, make sure that you use the local addresses of your instances. In our example we use 10.0.0.4 and 10.0.0.6 however this may be different for your instances, it almost certainly will be different, so you should double check the following code before putting it in the command line.

$ openstack loadbalancer member create --name 80_member_1 --address 10.0.0.4 --protocol-port 80  80_pool
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| address             | 10.0.0.4                             |
| admin_state_up      | True                                 |
| created_at          | 2018-06-25T01:37:46                  |
| id                  | 5ce83425-9d85-4da4-a057-4023e603ab2e |
| name                | 80_member_1                          |
| operating_status    | NO_MONITOR                           |
| project_id          | eac679e4896146e6827ce29d755fe289     |
| protocol_port       | 80                                   |
| provisioning_status | PENDING_CREATE                       |
| subnet_id           | None                                 |
| updated_at          | None                                 |
| weight              | 1                                    |
| monitor_port        | None                                 |
| monitor_address     | None                                 |
+---------------------+--------------------------------------+
$ openstack loadbalancer member create --name 80_member_2 --address 10.0.0.6 --protocol-port 80  80_pool
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| address             | 10.0.0.6                             |
| admin_state_up      | True                                 |
| created_at          | 2018-06-25T01:38:48                  |
| id                  | 5f973af6-7d59-4f64-a0b8-df5680d1bf78 |
| name                | 80_member_2                          |
| operating_status    | NO_MONITOR                           |
| project_id          | eac679e4896146e6827ce29d755fe289     |
| protocol_port       | 80                                   |
| provisioning_status | PENDING_CREATE                       |
| subnet_id           | None                                 |
| updated_at          | None                                 |
| weight              | 1                                    |
| monitor_port        | None                                 |
| monitor_address     | None                                 |
+---------------------+--------------------------------------+

Check that the members were created

$ openstack loadbalancer member list 80_pool
+--------------------------------------+-------------+----------------------------------+---------------------+----------+---------------+------------------+--------+
| id                                   | name        | project_id                       | provisioning_status | address  | protocol_port | operating_status | weight |
+--------------------------------------+-------------+----------------------------------+---------------------+----------+---------------+------------------+--------+
| 5ce83425-9d85-4da4-a057-4023e603ab2e | 80_member_1 | eac679e4896146e6827ce29d755fe289 | ACTIVE              | 10.0.0.4 |            80 | NO_MONITOR       |      1 |
| 5f973af6-7d59-4f64-a0b8-df5680d1bf78 | 80_member_2 | eac679e4896146e6827ce29d755fe289 | ACTIVE              | 10.0.0.6 |            80 | NO_MONITOR       |      1 |
+--------------------------------------+-------------+----------------------------------+---------------------+----------+---------------+------------------+--------+

Now repeat for the service on port 443

$ openstack loadbalancer member create --name 443_member_1 --address 10.0.0.4 --protocol-port 443  443_pool
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| address             | 10.0.0.4                             |
| admin_state_up      | True                                 |
| created_at          | 2018-06-25T01:43:41                  |
| id                  | ec245cb0-7548-4b25-881f-5a7dcd0c6e89 |
| name                | 443_member_1                         |
| operating_status    | NO_MONITOR                           |
| project_id          | eac679e4896146e6827ce29d755fe289     |
| protocol_port       | 443                                  |
| provisioning_status | PENDING_CREATE                       |
| subnet_id           | None                                 |
| updated_at          | None                                 |
| weight              | 1                                    |
| monitor_port        | None                                 |
| monitor_address     | None                                 |
+---------------------+--------------------------------------+


$ openstack loadbalancer member create --name 443_member_2 --address 10.0.0.6 --protocol-port 443  443_pool
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| address             | 10.0.0.6                             |
| admin_state_up      | True                                 |
| created_at          | 2018-06-25T01:44:19                  |
| id                  | f91e7d8e-a932-43da-8c9f-c37c0d58d864 |
| name                | 443_member_2                         |
| operating_status    | NO_MONITOR                           |
| project_id          | eac679e4896146e6827ce29d755fe289     |
| protocol_port       | 443                                  |
| provisioning_status | PENDING_CREATE                       |
| subnet_id           | None                                 |
| updated_at          | None                                 |
| weight              | 1                                    |
| monitor_port        | None                                 |
| monitor_address     | None                                 |
+---------------------+--------------------------------------+


$ openstack loadbalancer member list 443_pool
+--------------------------------------+--------------+----------------------------------+---------------------+----------+---------------+------------------+--------+
| id                                   | name         | project_id                       | provisioning_status | address  | protocol_port | operating_status | weight |
+--------------------------------------+--------------+----------------------------------+---------------------+----------+---------------+------------------+--------+
| ec245cb0-7548-4b25-881f-5a7dcd0c6e89 | 443_member_1 | eac679e4896146e6827ce29d755fe289 | ACTIVE              | 10.0.0.4 |           443 | NO_MONITOR       |      1 |
| f91e7d8e-a932-43da-8c9f-c37c0d58d864 | 443_member_2 | eac679e4896146e6827ce29d755fe289 | ACTIVE              | 10.0.0.6 |           443 | NO_MONITOR       |      1 |
+--------------------------------------+--------------+----------------------------------+---------------------+----------+---------------+------------------+--------+

Add a health monitor

Create a health monitor to check the state of the members of the pool. This example performs a simple static request at the URL path ‘/health’.

$ openstack loadbalancer healthmonitor create --name 80_healthcheck --delay 60 --timeout 20 --max-retries 2 --url-path /health --type http  80_pool
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| project_id          | eac679e4896146e6827ce29d755fe289     |
| name                | 80_healthcheck                       |
| admin_state_up      | True                                 |
| pools               | 96dde7c5-77c5-4ffe-9542-226714f5c58d |
| created_at          | 2018-06-25T21:22:25                  |
| provisioning_status | PENDING_CREATE                       |
| updated_at          | None                                 |
| delay               | 60                                   |
| expected_codes      | 200                                  |
| max_retries         | 2                                    |
| http_method         | GET                                  |
| timeout             | 20                                   |
| max_retries_down    | 3                                    |
| url_path            | /health                              |
| type                | HTTP                                 |
| id                  | d8c8c074-574a-4e41-8c43-f0633a4e828d |
| operating_status    | OFFLINE                              |
+---------------------+--------------------------------------+

Here is a brief description of some of the parameters used in the health monitor example.

  • url_path : Path part of the URL that should be retrieved from the back-end server. By default this is “/”.

  • delay : Number of seconds to wait between health checks.

  • timeout : Number of seconds to wait for any given health check to complete. Timeout should always be smaller than delay.

  • max-retries : Number of subsequent health checks a given back-end server must fail before it is considered down, or that a failed back-end server must pass to be considered up again.

Assign a VIP

The final step is to assign a floating ip address to the VIP port on the loadbalancer. In order to do this we need to create a floating ip, find the VIP Port ID and then assign it a floating ip address.

export FIP=`openstack floating ip create public-net -f value -c floating_ip_address`
export VIP_PORT_ID=`openstack loadbalancer show lb_test_1 -f value -c vip_port_id`
openstack floating ip set --port $VIP_PORT_ID $FIP

Test the setup

If you need to retrieve the VIP for the loadbalancer

export VIP=$(openstack loadbalancer show lb_test_1 -f value -c vip_address)
openstack floating ip list | grep $VIP | awk '{ print $4}'

Test the following:

  • Connect to the loadbalancer VIP from a browser. The output should alternate between both back-end servers on port 80.

  • Connect to the healtmonitor url on $VIP/health

  • Connect to $VIP:443 to confirm that the second service is also loadbalanced

Creating a load balancer using Heat

Heat is the native Openstack orchestration tool and functions by reading a template file and creating the resources defined within. In the following example, we are going to use a template to create a loadbalancer which will look after two webserver instances on ports 80 and 443 (for both webservers).

Preparation

If you do not have the underlying resources required to run a set of webservers i.e. a network and a router; you can find the instructions for creating them in this section of the documents. Given that there is a heat template in that section, you could even take a snippet of that template and include it in your own here, allowing you to construct your own template for future use. Additionally, if you need to create the simulated webservers themselves, there are instructions in the CLI section of this page on how to set them up correctly.

Gathering information for your heat template

Once you have these resources created, we are going to need to gather some information about them before we can construct our heat template. We will need to find the following variables for our template:

  • The subnet ID of the network your webservers are on. You can find this information using the following:

$ openstack subnet list
+--------------------------------------+---------------------+--------------------------------------+-----------------+
| ID                                   | Name                | Network                              | Subnet          |
+--------------------------------------+---------------------+--------------------------------------+-----------------+
| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | private-subnet      | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 192.168.3.0/24  |
+--------------------------------------+---------------------+--------------------------------------+-----------------+
  • You will need the internal IP addresses of your two webservers:

$ openstack server list
+--------------------------------------+-----------------------+--------+----------------------------------------------+--------------------------+---------+
| ID                                   | Name                  | Status | Networks                                     | Image                    | Flavor  |
+--------------------------------------+-----------------------+--------+----------------------------------------------+--------------------------+---------+
| 52d0d0af-8d0b-4bf8-8264-762822ae0e25 | webserver-heat-test-2 | ACTIVE | lb-docs-tests=192.168.0.43                   | N/A (booted from volume) | c1.c1r1 |
| bfc4e791-717f-4777-a7f3-2add9481deff | webserver-heat-test   | ACTIVE | lb-docs-tests=192.168.0.42                   | N/A (booted from volume) | c1.c1r1 |
+--------------------------------------+-----------------------+--------+----------------------------------------------+--------------------------+---------+

# We are taking the IP addresses: 192.168.0.43 and 192.168.0.42

Now that we have these variables, we can begin constructing out template.

Building and running a Heat template

For this example, we are going to provide a template which you can use to create your load balancer. You will have to change the “parameters” section of this file to include the variables you collected in the previous step:

heat_template_version: 2016-10-14

description: |
  The heat template is used to create a load balancer for a basic webserver
parameters:
  vip_subnet_id:
    description: Should be a subnet of webserver_network_id
    type: string
    default: <INSERT SUBNET OF WEBSERVERS>
  public_network:
    description: Public network name, could get by 'openstack network list --external'
    type: string
    default: public-net
  pool_member_1:
    description: the first webserver that you want the loadbalancer to balance
    type: string
    default: <INTERNAL IP OF FIRST WEBSERVER>
  pool_member_2:
    description: the second webserver that you want to be loadbalanced
    type: string
    default: <INTERNAL IP OF SECOND WEBSERVER>

resources:
  security_group:
    type: OS::Neutron::SecurityGroup
    properties:
      rules: [
        {protocol: "tcp", "port_range_min": 80, "port_range_max": 80},
        {protocol: "tcp", "port_range_min": 443, "port_range_max": 443},
      ]
  loadbalancer:
    type: OS::Octavia::LoadBalancer
    properties:
      vip_subnet: {get_param: vip_subnet_id}
      name: Loadbalancer-heat-test
  loadbalancer_public_ip:
    type: OS::Neutron::FloatingIP
    properties:
      floating_network: {get_param: public_network}
      port_id: {get_attr: [loadbalancer, vip_port_id]}
  listener:
    type: OS::Octavia::Listener
    properties:
      name: webserver_listener
      protocol: HTTP
      protocol_port: 80
      loadbalancer: {get_resource: loadbalancer}
  loadbalancer_pool:
    type: OS::Octavia::Pool
    properties:
      name: webserver-pool
      lb_algorithm: ROUND_ROBIN
      protocol: HTTP
      listener: {get_resource: listener}
  loadbalancer_pool_member_1:
    type: OS::Octavia::PoolMember
    properties:
      address: {get_param: pool_member_1}
      pool: {get_resource: loadbalancer_pool}
      protocol_port: 80
  loadbalancer_pool_member_2:
    type: OS::Octavia::PoolMember
    properties:
      address: {get_param: pool_member_2}
      pool: {get_resource: loadbalancer_pool}
      protocol_port: 80
  loadbalancer_healthmonitor:
    type: OS::Octavia::HealthMonitor
    properties:
      delay: 5
      max_retries: 3
      pool: {get_resource: loadbalancer_pool}
      timeout: 15
      type: HTTP
      http_method: GET
      expected_codes: 200


outputs:
  lb_ip:
    value: {get_attr: [loadbalancer_public_ip, floating_ip_address]}
  lb_vip:
    value: {get_attr: [loadbalancer, vip_address]}

Reading through the template you can see which resources are being created and how they relate to one another. Once you have saved this template and changed the necessary parameters, we can verify the syntax of our template is correct by using the following code:

$ openstack orchestration template validate -t heat-load-balancer.yaml
Environment:
  event_sinks: []
  parameter_defaults: {}
  parameters: {}
  resource_registry:
    resources: {}
Description: 'The heat template is used to create a load balancer for a basic webserver'

Parameters:
  pool_member_1:
    Default: 192.168.0.43
    Description: the first webserver that you want the loadbalancer to balance
    Label: pool_member_1
    NoEcho: 'false'
    Type: String
  pool_member_2:
    Default: 192.168.0.42
    Description: the second webserver that you want to be loadbalanced
    Label: pool_member_2
    NoEcho: 'false'
    Type: String
  public_network:
    Default: public-net
    Description: Public network name, could get by 'openstack network list --external'
    Label: public_network
    NoEcho: 'false'
    Type: String
  vip_subnet_id:
    Default: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    Description: Should be a subnet of webserver_network_id
    Label: vip_subnet_id
    NoEcho: 'false'
    Type: String

Once you have confirmed that your template is formatted correctly and that it is going to create the correct resources, we can run the following command to create our stack:

$ openstack stack create -t heat-load-balancer.yaml heat-lb-stack
+---------------------+---------------------------------------------------------------------------+
| Field               | Value                                                                     |
+---------------------+---------------------------------------------------------------------------+
| id                  | bedbf02b-0094-44c8-a423-ffe223868291                                      |
| stack_name          | heat-lb-stack                                                             |
| description         | The heat template is used to create a load balancer for a basic webserver |
|                     |                                                                           |
| creation_time       | 2021-01-28T21:36:20Z                                                      |
| updated_time        | None                                                                      |
| stack_status        | CREATE_IN_PROGRESS                                                        |
| stack_status_reason | Stack CREATE started                                                      |
+---------------------+---------------------------------------------------------------------------+

Now we should have our stack created and our loadbalancer running on our webserver instances.

Deleting your resources

If you have created resources using Heat then it is also a good idea to remove them using Heat as well. This is so that your stack is not left with missing resources or in a faulty state.

To remove all the resources created with our template; you can run the following command:

$ openstack stack delete <name of your stack>>
Are you sure you want to delete this stack(s) [y/N]? y

Creating a load balancer using Terraform

For this example, we assume that you already have some knowledge of how Terraform functions and that you know how to construct a Terraform template. If you have not used terraform before, or are not sure how to use it on the Catalyst Cloud, you can find a good starting example under the first instance section of the documents. If you are wanting an even more in depth look at Terraform, you can also check the Terraform documentation.

Preparation

As we mentioned earlier, this tutorial assumes that you already have installed the necessary tools to work with a terraform template. If you have not downloaded and installed terraform, you can follow our guide in the getting started section of the docs.

Once you have the correct tools ready, we can start working on our template. The template file that we are going to use for this example, is going to create a load balancer to manage a pair of webservers. You can use the script explained in the CLI example to create your own set of simulated webservers.

A key difference between this example and the CLI example is that we are focusing on having the webservers respond and balanced only on port 80. Which means that we can ignore the instructions from the CLI example referencing port 443.

Once you have a set of webservers that are serving traffic on port 80, we need to gather the necessary information that our template requires. Lets start with getting our subnet ID for the network we are going to have our loadbalancer on:

$ source example-openRC.sh
$ openstack subnet list
+--------------------------------------+---------------------+--------------------------------------+-----------------+
| ID                                   | Name                | Network                              | Subnet          |
+--------------------------------------+---------------------+--------------------------------------+-----------------+
| aaa43782v-auih-d3hak4i7-bfmdb-jmu2r3 | lb-docs-test-subnet | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | 192.168.0.0/24  |
+--------------------------------------+---------------------+--------------------------------------+-----------------+

Once you have your subnet ID, next you will need to get the local IP of your webservers:

$ openstack server list
+--------------------------------------+--------------------+--------+----------------------------+--------------------------+---------+
| ID                                   | Name               | Status | Networks                   | Image                    | Flavor  |
+--------------------------------------+--------------------+--------+----------------------------+--------------------------+---------+
| 28ad54af-e0d2-47c9-8855-4245dbfa3628 | webserver-2        | ACTIVE | private-net=192.168.3.41   | N/A (booted from volume) | c1.c1r1 |
| 6ea96e5e-8b67-4ec3-80a3-460b5b116bad | webserver-1        | ACTIVE | private-net=192.168.3.40   | N/A (booted from volume) | c1.c1r1 |
+--------------------------------------+--------------------+--------+----------------------------+--------------------------+---------+

# From this output, we can find the IP addresses under the "Networks" section:
# Member_1 = 192.168.3.41 , Member_2 = 192.168.3.40

Creating resources

After you have gathered this information, you need to construct a template to create your loadbalancer. You can use the following as a base and insert the information we gathered earlier into the variables provided:

# Configure the OpenStack Provider
# This example relies on OpenStack environment variables
# If you wish to set these credentials manualy please consult
# https://www.terraform.io/docs/providers/openstack/index.html
provider "openstack" {
  use_octavia = true # You must have set the "use_octavia" parameter to true, otherwise you will not be able to create a loadbalancer
}

# Set the public network that our resources are going to use further down in the template.
variable "public_network_id" {
  default = "f10ad6de-a26d-4c29-8c64-2a7418d47f8f"
}

# Depending on which region you are using, the public_network_id will need to be changed:
#nz-por-1	849ab1e9-7ac5-4618-8801-e6176fbbcf30
#nz_wlg_2	e0ba6b88-5360-492c-9c3d-119948356fd3
#nz-hlz-1	f10ad6de-a26d-4c29-8c64-2a7418d47f8f

# Include a valid VIP subnet_id to be used for your load balancer later.
variable "vip_subnet" {
  default = "<INSERT SUBNET ID>"
}

variable "public_network" {
  default = "public-net"
}

# Include the first webserver IP
variable "pool_member_1_address" {
  default = "<LOCAL IP ADDRESS OF WEBSERVER 1>"
}

# Include the second webserver IP
variable "pool_member_2_address" {
  default = "<LOCAL IP ADDRESS OF WEBSERVER 2>"
}

#-----------------------------------------------------------------------------------------------

# Create loadbalancer
resource "openstack_lb_loadbalancer_v2" "terra_load_balancer" {
  name = "terraform-lb"
  vip_subnet_id = var.vip_subnet
  security_group_ids = [openstack_networking_secgroup_v2.secgroup_1.id]
}

# Create a pool for our loadbalancer
resource "openstack_lb_pool_v2" "pool_1" {
  name        = "webserver-pool"
  protocol    = "TCP"
  listener_id = openstack_lb_listener_v2.listener_1.id
  lb_method   = "ROUND_ROBIN"
}

# create a member and specify the pool it belongs to
resource "openstack_lb_member_v2" "member_1" {
  name = "member-1"
  pool_id       = openstack_lb_pool_v2.pool_1.id
  address       = var.pool_member_1_address
  protocol_port = 80
}

# create a member and specify the pool it belongs to
resource "openstack_lb_member_v2" "member_2" {
  name = "member-2"
  pool_id       = openstack_lb_pool_v2.pool_1.id
  address       = var.pool_member_2_address
  protocol_port = 80
}

# Create a listener
resource "openstack_lb_listener_v2" "listener_1" {
  name = "listener-1"
  protocol        = "TCP"
  protocol_port   = 80
  loadbalancer_id = openstack_lb_loadbalancer_v2.terra_load_balancer.id
}

# Request a floating IP
resource "openstack_networking_floatingip_v2" "fip_1" {
  pool = "public-net"
}

# Associate floating IP
resource "openstack_networking_floatingip_associate_v2" "fip_associate" {
  floating_ip = openstack_networking_floatingip_v2.fip_1.address
  port_id     = openstack_lb_loadbalancer_v2.terra_load_balancer.vip_port_id
}

# Create a security group
resource "openstack_networking_secgroup_v2" "secgroup_1" {
  name = "secgroup_1"
}

# Create security group rule
resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_1" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "TCP"
  port_range_min    = 80
  port_range_max    = 80
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.secgroup_1.id
}

# Create health monitor for LB
resource "openstack_lb_monitor_v2" "monitor_1" {
  pool_id        = openstack_lb_pool_v2.pool_1.id
  type           = "HTTP"
  http_method    = "GET"
  expected_codes = 200
  delay          = 5
  timeout        = 15
  max_retries    = 3
}

Once you have your template ready with the correct variables, you can run the terraform plan command to get an output of what resources your template is going to construct for you:

$ terraform plan

  Refreshing Terraform state in-memory prior to plan...
  The refreshed state will be used to calculate this plan, but will not be
  persisted to local or remote state storage.


  ------------------------------------------------------------------------

  An execution plan has been generated and is shown below.
  Resource actions are indicated with the following symbols:
    + create

  Terraform will perform the following actions:

    # openstack_lb_listener_v2.listener_1 will be created
    + resource "openstack_lb_listener_v2" "listener_1" {
        + admin_state_up         = true
        + connection_limit       = (known after apply)
        + default_pool_id        = (known after apply)
        + id                     = (known after apply)
        + loadbalancer_id        = (known after apply)
        + name                   = "listener-1"
        + protocol               = "HTTP"
        + protocol_port          = 80
        + region                 = (known after apply)
        + tenant_id              = (known after apply)
        + timeout_client_data    = (known after apply)
        + timeout_member_connect = (known after apply)
        + timeout_member_data    = (known after apply)
        + timeout_tcp_inspect    = (known after apply)
      }

    # openstack_lb_loadbalancer_v2.terra_load_balancer will be created
    + resource "openstack_lb_loadbalancer_v2" "terra_load_balancer" {
        + admin_state_up        = true
        + id                    = (known after apply)
        + loadbalancer_provider = (known after apply)
        + name                  = "terraform-lb"
        + region                = (known after apply)
        + security_group_ids    = (known after apply)
        + tenant_id             = (known after apply)
        + vip_address           = (known after apply)
        + vip_network_id        = (known after apply)
        + vip_port_id           = (known after apply)
        + vip_subnet_id         = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
      }

    # openstack_lb_member_v2.member_1 will be created
    + resource "openstack_lb_member_v2" "member_1" {
        + address        = "XXXXXXXXXXXXXXX"
        + admin_state_up = true
        + id             = (known after apply)
        + name           = "member-1"
        + pool_id        = (known after apply)
        + protocol_port  = 80
        + region         = (known after apply)
        + tenant_id      = (known after apply)
        + weight         = (known after apply)
      }

    # openstack_lb_member_v2.member_2 will be created
    + resource "openstack_lb_member_v2" "member_2" {
        + address        = "XXXXXXXXXXXXXXX"
        + admin_state_up = true
        + id             = (known after apply)
        + name           = "member-2"
        + pool_id        = (known after apply)
        + protocol_port  = 80
        + region         = (known after apply)
        + tenant_id      = (known after apply)
        + weight         = (known after apply)
      }

    # openstack_lb_monitor_v2.monitor_1 will be created
    + resource "openstack_lb_monitor_v2" "monitor_1" {
        + admin_state_up   = true
        + delay            = 5
        + expected_codes   = "200"
        + http_method      = "GET"
        + id               = (known after apply)
        + max_retries      = 3
        + max_retries_down = (known after apply)
        + pool_id          = (known after apply)
        + region           = (known after apply)
        + tenant_id        = (known after apply)
        + timeout          = 15
        + type             = "HTTP"
        + url_path         = (known after apply)
      }

    # openstack_lb_pool_v2.pool_1 will be created
    + resource "openstack_lb_pool_v2" "pool_1" {
        + admin_state_up = true
        + id             = (known after apply)
        + lb_method      = "ROUND_ROBIN"
        + listener_id    = (known after apply)
        + name           = "webserver-pool"
        + protocol       = "HTTP"
        + region         = (known after apply)
        + tenant_id      = (known after apply)

        + persistence {
            + cookie_name = (known after apply)
            + type        = (known after apply)
          }
      }

    # openstack_networking_floatingip_associate_v2.fip_associate will be created
    + resource "openstack_networking_floatingip_associate_v2" "fip_associate" {
        + fixed_ip    = (known after apply)
        + floating_ip = (known after apply)
        + id          = (known after apply)
        + port_id     = (known after apply)
        + region      = (known after apply)
      }

    # openstack_networking_floatingip_v2.fip_1 will be created
    + resource "openstack_networking_floatingip_v2" "fip_1" {
        + address    = (known after apply)
        + all_tags   = (known after apply)
        + dns_domain = (known after apply)
        + dns_name   = (known after apply)
        + fixed_ip   = (known after apply)
        + id         = (known after apply)
        + pool       = "public-net"
        + port_id    = (known after apply)
        + region     = (known after apply)
        + tenant_id  = (known after apply)
      }

    # openstack_networking_secgroup_rule_v2.secgroup_rule_1 will be created
    + resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_1" {
        + direction         = "ingress"
        + ethertype         = "IPv4"
        + id                = (known after apply)
        + port_range_max    = 80
        + port_range_min    = 80
        + protocol          = "tcp"
        + region            = (known after apply)
        + remote_group_id   = (known after apply)
        + remote_ip_prefix  = "XXXXXXXXXX"
        + security_group_id = (known after apply)
        + tenant_id         = (known after apply)
      }

    # openstack_networking_secgroup_v2.secgroup_1 will be created
    + resource "openstack_networking_secgroup_v2" "secgroup_1" {
        + all_tags    = (known after apply)
        + description = (known after apply)
        + id          = (known after apply)
        + name        = "secgroup_1"
        + region      = (known after apply)
        + tenant_id   = (known after apply)
      }

  Plan: 10 to add, 0 to change, 0 to destroy.

  ------------------------------------------------------------------------

  Note: You didn't specify an "-out" parameter to save this plan, so Terraform
  can't guarantee that exactly these actions will be performed if
  "terraform apply" is subsequently run.

At this point, after you have checked the plan and made sure all of the resources are correct and that the template is has no formatting problems, you can run the terraform apply command and begin creating your resources:

$ terraform apply

... # truncated output for brevity.
Plan: 10 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

openstack_networking_secgroup_v2.secgroup_1: Creating...
openstack_networking_floatingip_v2.fip_1: Creating...
openstack_networking_secgroup_v2.secgroup_1: Creation complete after 2s [id=04a48ceb-44c4-4f71-9c16-ac7805a070d3]
openstack_networking_secgroup_rule_v2.secgroup_rule_1: Creating...
openstack_lb_loadbalancer_v2.terra_load_balancer: Creating...
openstack_networking_secgroup_rule_v2.secgroup_rule_1: Creation complete after 0s [id=46f0734a-0468-4e03-a5fa-16d462c6f089]
openstack_networking_floatingip_v2.fip_1: Creation complete after 7s [id=cd8006d1-1010-47d8-b6af-a69ee7a349ea]
openstack_lb_loadbalancer_v2.terra_load_balancer: Still creating... [10s elapsed]
openstack_lb_loadbalancer_v2.terra_load_balancer: Still creating... [20s elapsed]
openstack_lb_loadbalancer_v2.terra_load_balancer: Still creating... [30s elapsed]
openstack_lb_loadbalancer_v2.terra_load_balancer: Still creating... [40s elapsed]
openstack_lb_loadbalancer_v2.terra_load_balancer: Still creating... [50s elapsed]
openstack_lb_loadbalancer_v2.terra_load_balancer: Still creating... [1m0s elapsed]
openstack_lb_loadbalancer_v2.terra_load_balancer: Still creating... [1m10s elapsed]
openstack_lb_loadbalancer_v2.terra_load_balancer: Creation complete after 1m11s [id=5529a920-c32e-4eda-b533-98ece771fb0b]
openstack_networking_floatingip_associate_v2.fip_associate: Creating...
openstack_lb_listener_v2.listener_1: Creating...
openstack_networking_floatingip_associate_v2.fip_associate: Creation complete after 1s [id=cd8006d1-1010-47d8-b6af-a69ee7a349ea]
openstack_lb_listener_v2.listener_1: Creation complete after 9s [id=56b09389-bcd5-4e02-9eeb-e3d0f72e13e5]
openstack_lb_pool_v2.pool_1: Creating...
openstack_lb_pool_v2.pool_1: Creation complete after 5s [id=be81e2b6-927f-4f7a-affb-95d926355e9d]
openstack_lb_member_v2.member_1: Creating...
openstack_lb_member_v2.member_2: Creating...
openstack_lb_monitor_v2.monitor_1: Creating...
openstack_lb_member_v2.member_1: Creation complete after 8s [id=9190707b-2358-422d-8cdd-90fd2b5a2d98]
openstack_lb_monitor_v2.monitor_1: Creation complete after 9s [id=67c133f7-3857-425c-8f4a-781c3b11a6f1]
openstack_lb_member_v2.member_2: Still creating... [10s elapsed]
openstack_lb_member_v2.member_2: Creation complete after 17s [id=d30ee7dc-63f4-4e41-8f27-bd2484383ab5]

Apply complete! Resources: 10 added, 0 changed, 0 destroyed.

Deleting resources

Should you wish to remove these resources, you can use the command:

$ terraform destroy

Plan: 0 to add, 0 to change, 10 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

openstack_networking_secgroup_rule_v2.secgroup_rule_1: Destroying... [id=46f0734a-0468-4e03-a5fa-16d462c6f089]
openstack_networking_floatingip_associate_v2.fip_associate: Destroying... [id=cd8006d1-1010-47d8-b6af-a69ee7a349ea]
openstack_lb_member_v2.member_2: Destroying... [id=d30ee7dc-63f4-4e41-8f27-bd2484383ab5]
openstack_lb_member_v2.member_1: Destroying... [id=9190707b-2358-422d-8cdd-90fd2b5a2d98]
openstack_lb_monitor_v2.monitor_1: Destroying... [id=67c133f7-3857-425c-8f4a-781c3b11a6f1]
openstack_networking_floatingip_associate_v2.fip_associate: Destruction complete after 1s
openstack_networking_floatingip_v2.fip_1: Destroying... [id=cd8006d1-1010-47d8-b6af-a69ee7a349ea]
openstack_networking_secgroup_rule_v2.secgroup_rule_1: Destruction complete after 6s
openstack_networking_floatingip_v2.fip_1: Destruction complete after 6s
openstack_lb_monitor_v2.monitor_1: Destruction complete after 8s
openstack_lb_member_v2.member_1: Destruction complete after 9s
openstack_lb_member_v2.member_2: Still destroying... [id=d30ee7dc-63f4-4e41-8f27-bd2484383ab5, 10s elapsed]
openstack_lb_member_v2.member_2: Destruction complete after 11s
openstack_lb_pool_v2.pool_1: Destroying... [id=be81e2b6-927f-4f7a-affb-95d926355e9d]
openstack_lb_pool_v2.pool_1: Destruction complete after 3s
openstack_lb_listener_v2.listener_1: Destroying... [id=56b09389-bcd5-4e02-9eeb-e3d0f72e13e5]
openstack_lb_listener_v2.listener_1: Destruction complete after 5s
openstack_lb_loadbalancer_v2.terra_load_balancer: Destroying... [id=5529a920-c32e-4eda-b533-98ece771fb0b]
openstack_lb_loadbalancer_v2.terra_load_balancer: Destruction complete after 7s
openstack_networking_secgroup_v2.secgroup_1: Destroying... [id=04a48ceb-44c4-4f71-9c16-ac7805a070d3]
openstack_networking_secgroup_v2.secgroup_1: Destruction complete after 9s

Destroy complete! Resources: 10 destroyed.

Creating a load balancer using an Ansible playbook

The following example assumes that you already have some understanding of how Ansible playbooks work and how to construct your own. If you have not used Ansible before or are not sure about how to use it on the Catalyst cloud, a good starting point is our example under the first instance section of the documents. You can also find more information about Ansible in general on the Ansible homepage.

Preparation

Unlike the other examples, our ansible playbook will create its own webserver alongside the load balancer. Therefore we have to gather additional information about our resources before we can start using our playbook.

We will need to prepare a number of variables for our playbook:

  • A name for your webserver

  • A name for your load balancer

  • An image to use for your webserver:

$ openstack image list
+--------------------------------------+-----------------------------------------------------------------+--------+
| ID                                   | Name                                                            | Status |
+--------------------------------------+-----------------------------------------------------------------+--------+
| 683f76b0-eec2-43c3-9143-d693829589a1 | atomic-7-x86_64                                                 | active |
| 7f352450-e87f-42b9-8238-c6d1f32ab8d9 | atomic-7-x86_64-20170502                                        | active |
| bc84a4a4-d73c-44c8-a65e-b694e61580a5 | atomic-7-x86_64-20170608                                        | active |
| 0be2db8d-017b-464f-8a46-9805185e6b14 | atomic-7-x86_64-20170714                                        | active |
| eedefeab-34d3-4f73-8421-3a2c3a854c52 | atomic-7-x86_64-20181018                                        | active |
| ... Truncated for brevity            |                                                                 |        |
+--------------------------------------+-----------------------------------------------------------------+--------+
  • A flavor to use for your webserver:

$ openstack flavor list
+--------------------------------------+------------+--------+------+-----------+-------+-----------+
| ID                                   | Name       |    RAM | Disk | Ephemeral | VCPUs | Is Public |
+--------------------------------------+------------+--------+------+-----------+-------+-----------+
| 00e563b6-11e2-4468-ac55-8403f75bcbd2 | c1.c16r24  |  24576 |   10 |         0 |    16 | True      |
| 01ecf2cc-2047-4606-a53b-11eb4b2b3757 | c1.c8r6    |   6144 |   10 |         0 |     8 | True      |
| 02cb8214-2121-4d0d-b7fd-c36bb9b2e960 | c1.c4r24   |  24576 |   10 |         0 |     4 | True      |
| 07585040-f887-4ddb-a0d5-5fac4ff273a7 | c1.c8r16   |  16384 |   10 |         0 |     8 | True      |
| 1c558eba-0d8a-4d09-86dd-fdcf3bb9874e | c1.c32r24  |  24576 |   10 |         0 |    32 | True      |
| ... Truncated for Brevity            |            |        |      |           |       |           |
+--------------------------------------+------------+--------+------+-----------+-------+-----------+
  • The name of an existing network &

  • The name of the public net to connect to:

$ openstack network list
+--------------------------------------+--------------------------+--------------------------------------+
| ID                                   | Name                     | Subnets                              |
+--------------------------------------+--------------------------+--------------------------------------+
| XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | public-net               | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
| XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | existing-private-net     | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
+--------------------------------------+--------------------------+--------------------------------------+
  • The name of an existing keypair:

$ openstack keypair list
+-----------------+-------------------------------------------------+
| Name            | Fingerprint                                     |
+-----------------+-------------------------------------------------+
| your-keypair    | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
+-----------------+-------------------------------------------------+
  • A list of any security groups you want to attach to your load balancer

$ openstack security group list
+--------------------------------------+-------------------+--------------------------------------------+---------+------+
| ID                                   | Name              | Description                                | Project | Tags |
+--------------------------------------+-------------------+--------------------------------------------+---------+------+
| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | sec-group         | security group to allow traffic on port 80 | None    | []   |
+--------------------------------------+-------------------+--------------------------------------------+---------+------+
  • The name of your subnet

$ openstack subnet list
+--------------------------------------+---------------------+--------------------------------------+-----------------+
| ID                                   | Name                | Network                              | Subnet          |
+--------------------------------------+---------------------+--------------------------------------+-----------------+
| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | private-subnet      | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 192.168.3.0/24  |
+--------------------------------------+---------------------+--------------------------------------+-----------------+

Once you have all of these resources you can move on to constructing your playbook.

Creating your Ansible playbook

For this example we will provide a template for your playbook. You will need to add the variables you collected in the previous step to the vars section of the template, but once this is done you should have a fully functioning Ansible playbook you can use to create a simulated webserver and a loadbalancer which looks after it:

#!/usr/bin/env ansible-playbook
#
# This playbook creates an instance running a simple webserver listening on
# port 80 and adds a load balancer to it. A floating ip is created and associated
# with the load balancer automatically. The floating ip address can be found
# in the `public_vip_address` field of the final output.
#
# Please refer to https://docs.ansible.com/ansible/latest/collections/openstack/cloud/loadbalancer_module.html
# for more detailed information about os_loadbalancer ansible module usage.
#

---
- name: Create loadbalancer and add a simple webserver
  hosts: localhost

  vars:
    vm_name: <insert_webserver_name>
    image: ubuntu-20.04-x86_64 # image used for webserver
    flavor: c1.c1r1 # A flavor with 1 core and 1 GB of RAM
    network: <insert_network_name>
    kaypair: <insert_key_name>
    security_groups:
      - <insert_lb_secgroup>
    lb_name: <insert_load_balancer_name>
    vip_subnet: <insert_subnet_name>
    public_network: public-net

  tasks:
    - name: Create a simple webserver
      openstack.cloud.server:
        name: "{{ vm_name }}"
        state: present
        auto_ip: true
        flavor: "{{ flavor }}"
        image: "{{ image }}"
        key_name: "{{ kaypair }}"
        network: "{{ network }}"
        security_groups: "{{ security_groups }}"
        config_drive: true
        userdata: |
          {%- raw -%}#!/usr/bin/python
          from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
          import time
          PORT_NUMBER = 80
          class myHandler(BaseHTTPRequestHandler):
              def do_GET(self):
                  self.send_response(200)
                  self.send_header('Content-type','text/html')
                  self.end_headers()
                  res_content = "Hello %s:%s - %s\n" % (self.client_address[0], self.client_address[1], time.strftime("%H:%M:%S", time.gmtime()))
                  self.wfile.write(res_content)
                  return
          try:
              server = HTTPServer(('', PORT_NUMBER), myHandler)
              print 'Started httpserver on port ' , PORT_NUMBER
              server.serve_forever()
          except KeyboardInterrupt:
              print 'shutting down the web server...'
              server.socket.close()
          {% endraw %}

    - name: Gather facts about webserver
      openstack.cloud.server_info:
        server: "{{ vm_name }}"

    - name: Create the openstack loadbalancer
      openstack.cloud.loadbalancer:
        name: "{{ lb_name }}"
        state: present
        vip_subnet: "{{ vip_subnet }}"
        auto_public_ip: yes
        public_network: "{{ public_network }}"
        wait: yes
        timeout: "600"
        listeners:
        - name: listener-80
          protocol: TCP
          protocol_port: 80
          pool:
            name: listener_80_pool
            protocol: TCP
            members:
              - name: member-1
                address: openstack.cloud.server.ip
                protocol_port: 80
                subnet: <<INSERT THE NAME OF YOUR SUBNET HERE>> #using a reference to the variable does not work for this declaration
      vars:
        query: 'addresses.{{ network }}[?"OS-EXT-IPS:type"==`fixed`].addr | [0]'
      register: lb_info

    - name: Create a health monitor for our load balancer
      openstack.cloud.lb_health_monitor:
        wait: true
        admin_state_up: True
        expected_codes: '200'
        max_retries_down: '3'
        http_method: GET
        url_path: "/status"
        pool: "listener_80_pool"
        name: 'healthmonitor01'
        delay: '10'
        max_retries: '3'
        resp_timeout: '5'
        state: present

Once you have changed the variables in your template we can move on to building the resources on your project.

Running your Ansible playbook

Firstly, we need to know exactly what tasks our playbook is going to take. We can use the following command to display each of our playbook’s tasks:

$ ansible-playbook --list-tasks create_loadbalancer_playbook.yaml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

playbook: create_loadbalancer_playbook.yaml

play #1 (localhost): Create loadbalancer and add a simple webserver TAGS: []
  tasks:
    Create a simple webserver       TAGS: []
    Gather facts about webserver    TAGS: []
    Create the openstack loadbalancer       TAGS: []
    Create a health monitor for our load balancer   TAGS: []

Now that we know what tasks our playbook is going to perform, we can run our playbook and create our webserver and load balancer.

To run our playbook we use the following:

$ ansible-playbook create_loadbalancer_playbook.yaml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [Create loadbalancer and add a simple webserver] ***************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************
ok: [localhost]

TASK [Create a simple webserver] ************************************************************************************************************************************
ok: [localhost]

TASK [Gather facts about webserver] *********************************************************************************************************************************
ok: [localhost]

TASK [Create the openstack loadbalancer] ****************************************************************************************************************************
ok: [localhost]

TASK [Create a health monitor for our load balancer] ****************************************************************************************************************
ok: [localhost]

PLAY RECAP **********************************************************************************************************************************************************
localhost                  : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Once your playbook has completed, you should be able to see your loadbalancer on your project:

$ openstack loadbalancer list
+--------------------------------------+------------+----------------------------------+--------------+---------------------+------------------+----------+
| id                                   | name       | project_id                       | vip_address  | provisioning_status | operating_status | provider |
+--------------------------------------+------------+----------------------------------+--------------+---------------------+------------------+----------+
| a797a67d-bab5-4ae5-af30-5f7d633286e5 | ansible-lb | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | 192.168.0.39 | ACTIVE              | ONLINE           | amphora  |
+--------------------------------------+------------+----------------------------------+--------------+---------------------+------------------+----------+

Deleting your resources

To delete your resources you will need to construct another Ansible playbook. With the variable names that you used in the previous playbook, you will need to fill out the following template.This playbook will delete the webserver, loadbalancer, and healthmonitor created in the previous playbook:

#!/usr/bin/env ansible-playbook
#

---
- name: Clean up the resources from the previous playbook
  hosts: localhost

  vars:
    vm_name: <insert_webserver_name>
    lb_name: <insert_load_balancer_name>
    health_monitor: <insert_health_monitor_name>

  tasks:
    - name: Gather facts about webserver
      openstack.cloud.server_info:
        server: "{{ vm_name }}"

    - name: Remove the openstack loadbalancer
      openstack.cloud.loadbalancer:
        state: absent
        name: "{{ lb_name }}"

    - name: Remove the webserver
      openstack.cloud.server:
        state: absent
        name: "{{ vm_name }}"

Once you have set your variables you simply run the playbook like we did previously:

$ ansible-playbook remove_loadbalancer_playbook.yaml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [Clean up the resources from the previous playbook] ************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************
ok: [localhost]

TASK [Gather facts about webserver] *********************************************************************************************************************************
ok: [localhost]

TASK [Remove the openstack loadbalancer] ****************************************************************************************************************************
changed: [localhost]

PLAY RECAP **********************************************************************************************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0