vr_balancing - OpenNebula/one-apps GitHub Wiki

Load Balancing

Introduction

The virtual router provides to open source solutions for load balancing TCP services:

  1. Keepalived + LVS (IPVS).
  2. HAProxy.

In this guide we will assume the following configuration:

        public network
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€   Users
      β”‚
   β”Œβ”€β”€β”΄β”€β”  10.0.1.1
β”Œβ”€β”€β”€eth0β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  β””β”€β”€β”€β”€β”˜        β”‚   LB service (http:80)
β”‚ Virtual Router β”‚
β”‚                β”‚   10.0.1.1:80 β”Œβ”€β”€β”€β”€β–Ί 172.20.0.104:8080
β”‚ Load Balancer  β”‚               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”        β”‚               └────► 172.20.0.105:8080
└───eth1β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”˜
   β””β”€β”¬β”€β”€β”˜
     β”‚   private network
     └────┬─────────────────┬───
     172.20.0.104      172.20.0.105
       β”Œβ”€β”€β”΄β”€β”€β”           β”Œβ”€β”€β”΄β”€β”€β”
       β”‚ LB1 β”‚           β”‚ LB2 β”‚
       β””β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”˜

Note

The Load Balancing service can be provisioned in a failover setup activating the associated keepalived services, see more details here

Additionally we will consider two LB deployment modes:

  • Static, when the number LB servers are fixed and known
  • Dynamic, when LB servers can be added/removed from the backend

IP Addresses Placeholders

To configure the LB services you need to define the public (user-facing) IP. This IP is only known once the Virtual Router is deployed and the IP assignment is resolved. Usually, you can refer to the public facing IP of the Virtual router as ETH0_EP0

For advanced scenarios, OpenNebula provides you with some convenient placeholders that are resolved automatically at runtime:

  • <ETHx_IPy> means "interpolate (in-place) the y-th IP of the x-th NIC"
  • <ETHx_VIPy> means "interpolate (in-place) the y-th Virtual IP of the x-th NIC"
  • <ETHx_EPy> means "interpolate (in-place) the y-th EndPoint of the x-th NIC"

Values of ETHx_IPy and ETHx_VIPy are always merged together to produce ETHx_EPy. When ETHx_VIPy is undefined, then ETHx_EPy is always set to ETHx_IPy as a fallback, that way you can always have some valid "endpoints" even in non-HA scenarios (no VIPs defined).

Note

Virtual IP refers to the IP assigned to the NIC of the virtual router when FLOATING_IP has been set.

Static LBs

Keepalived LVS

To configure keepalived LVS you need to define:

  • The front-end IP and port to expose the service ONEAPP_VNF_LBx_IP, ONEAPP_VNF_LBx_PORT
  • The IP and port of the backend severs ONEAPP_VNF_LBx_SERVERy_HOST, ONEAPP_VNF_LBx_SERVERy_PORT set at deployment time
  • The protocol and mode where keepalive will use to load balance the services (e.g. ONEAPP_VNF_LBx_PROTOCOL)

Note

Each variable is indexed for each balanced service LB0, LB1, etc.. and each backend server is indexed as well with SERVER0, SERVER1,.. etc..

For example, to create a static LVS-based TCP LB, for the topology above, you can use:

CONTEXT = [
  ...
  ONEAPP_VNF_LB_ENABLED = "YES",

  ONEAPP_VNF_LB0_IP        = "<ETH0_EP0>", # Interpolate the first "endpoint".
  ONEAPP_VNF_LB0_PORT      = "80",
  ONEAPP_VNF_LB0_PROTOCOL  = "TCP",
  ONEAPP_VNF_LB0_METHOD    = "NAT",
  ONEAPP_VNF_LB0_SCHEDULER = "rr", # "Round-robin".

  # Static backends:

  ONEAPP_VNF_LB0_SERVER0_HOST = "172.20.0.104",
  ONEAPP_VNF_LB0_SERVER0_PORT = "8080",
  ONEAPP_VNF_LB0_SERVER1_HOST = "172.20.0.105",
  ONEAPP_VNF_LB0_SERVER1_PORT = "8080",
  ...
]

You can see in the example above, that we defined the LB0 by providing the IP / PORT pair with some extra settings. Then two (static) backends are defined for the LB0.

HAProxy

Similarly for HAProxy you need to define:

  • The front-end IP and port to expose the service ONEAPP_VNF_HAPROXY_LBx_IP, ONEAPP_VNF_HAPROXY_LBx_PORT
  • The IP and port of the backend severs ONEAPP_VNF_HAPROXY_LBx_SERVERy_HOST, ONEAPP_VNF_HAPROXY_LBx_SERVERy_PORT

For example, to create a HAProxy-based TCP LB for the topology above, you can use:

CONTEXT = [
  ...
  ONEAPP_VNF_HAPROXY_ENABLED = "YES",

  ONEAPP_VNF_HAPROXY_LB0_IP   = "<ETH0_EP0>", # Interpolate the first "endpoint".
  ONEAPP_VNF_HAPROXY_LB0_PORT = "80",

  # Static backends:

  ONEAPP_VNF_HAPROXY_LB0_SERVER0_HOST = "172.20.0.104",
  ONEAPP_VNF_HAPROXY_LB0_SERVER0_PORT = "8080",
  ONEAPP_VNF_HAPROXY_LB0_SERVER1_HOST = "172.20.0.105",
  ONEAPP_VNF_HAPROXY_LB0_SERVER1_PORT = "8080",
  ...
]

Dynamic LBs

Important

To dynamically add and remove LBs the Virutal Router cannot be instantiated as an standalone VM. It has to be an OpenNebula Virtual Router or part of an OpenNebula flow.

Important

To dynamically add and remove LBs the OpenNebula Gate service needs to be configured.

The procedure is as follows:

  1. Add ONEAPP_VNF_LB_ONEGATE_ENABLED="YES" or ONEAPP_VNF_HAPROXY_ONEGATE_ENABLED="YES" to the Virtual Router context, to set one or both LBs in the dynamic mode.
  2. Create a new Virtual Machine that includes BACKEND = YES in its CONTEXT attribute (not required in OneFlow).
  3. Update the VM to include the IP/HOST, and PORT pairs with the target IP

Keepalived LVS

Let's assume you already have deployed a Virtual Router in static mode with backends 172.20.0.104 and 172.20.0.105, as described above. To add a new backend. Create a new Virtual Machine, for example you could use the following template:

NAME = MyBackend
...
NIC = [ NETWORK = "private" ]

# The ONEGATE_* values below are created inside the "User Template" of the backend VM.

ONEGATE_LB0_IP = "<ETH0_EP0>"
ONEGATE_LB0_PORT = 80

CONTEXT = [
  ...
  NETWORK = YES,
  SSH_PUBLIC_KEY = "$USER[SSH_KEY]",
  BACKEND = YES, # NOTE: This isn't required when running inside OneFlow.
  ...
]

Once the VM is created look for the assigned IP in the private network (for example let's assume it is 172.20.0.133) and update the user template contents with the IP/PORT pairs (e.g. onevm update):

ONEGATE_LB0_SERVER_HOST = "172.20.0.133"
ONEGATE_LB0_SERVER_PORT = 8080

Note

Dynamic variable ONEGATE_LB0_SERVER_HOST does not contain the server index, this is different from the static definition ONEAPP_VNF_LB0_SERVER0_HOST.

Important

The LB's ONEGATE_LBx_IP / ONEGATE_LBx_PORT pair in the VM template must match those of a statically defined Virtual Router. If this is not the case, then no change will be produced.

HAProxy

Equivalently you'll need to create a VM that includes the following attributes in its template:

NAME = MyBackend
...
NIC = [ NETWORK = "private" ]

# The ONEGATE_* values below are created inside the "User Template" of the backend VM.

ONEGATE_HAPROXY_LB0_IP = "<ETH0_EP0>"
ONEGATE_HAPROXY_LB0_PORT = 80

CONTEXT = [
  ...
  NETWORK = YES,
  SSH_PUBLIC_KEY = "$USER[SSH_KEY]",
  BACKEND = YES, # NOTE: This isn't required when running inside OneFlow.
  TOKEN = YES
  ...
]

And then update it with the target IP/PORT pairs (e.g. onevm update):

ONEGATE_HAPROXY_LB0_SERVER_HOST = "172.20.0.133"
ONEGATE_HAPROXY_LB0_SERVER_PORT = 8080

This can be added as a start script on the VM template, so the VM is configured on instantiation:

onegate vm update --data "ONEGATE_HAPROXY_LB0_SERVER_HOST=\"$ETH0_IP\""
onegate vm update --data "ONEGATE_HAPROXY_LB0_SERVER_PORT=\"8080\""

Virtual Router Standalone vs OneFlow Modes.

The Virtual Router appliance can deployed in three different ways:

  1. As a proper OpenNebula Virtual Router instance.
  2. As a standalone VM, but inside an OneFlow service instance.
  3. As a standalone VM. (not supported)

When running both types of LBs (static and dynamic) in modes 1. and 2. the context and OneGate interface is exactly the same (with the important caveat, that the mode 1. requires backends to have the CONTEXT = [..., BACKEND = "YES", ...] flag defined).

Under the hood the source of updates is different, it both cases OneGate is used, but in the mode 1. all VNETs attached to the VR are scanned (recursively), in the mode 2. OneFlow provides API responses.

In that sense mode 1. is superior to mode 2., because a "proper" VR can reverse-proxy to any VMs in its attached VNETs (also to VMs running in OneFlow instances), at the same time mode 2. allows for backends from within its OneFlow instance only.

Context Configuration Attributes

Keepalived LVS

Parameter Default Description
ONEAPP_VNF_LB_ENABLED NO Enable/Disable LB feature (YES/NO)
ONEAPP_VNF_LB_ONEGATE_ENABLED NO Enable/Disable dynamic real servers via OneGate (YES/NO)
ONEAPP_VNF_LB_INTERFACES all NICs List of NICs to listen on (<[!]ethX> ...)
ONEAPP_VNF_LB_REFRESH_RATE 30 Refresh rate between updates of the pool of real servers (seconds)
ONEAPP_VNF_LB_FWMARK_OFFSET 10000 Default starting firewall mark for LVS/IPVS
ONEAPP_VNF_LB[0-9]_IP none Mandatory: Load balanced IP address
ONEAPP_VNF_LB[0-9]_PORT none Optional: IP port to specify connection
ONEAPP_VNF_LB[0-9]_PROTOCOL UDP IP protocol to specify connection (TCP or UDP)
ONEAPP_VNF_LB[0-9]_METHOD NAT LVS/IPVS method (NAT or DR)
ONEAPP_VNF_LB[0-9]_SCHEDULER wlc LVS/IPVS scheduler
ONEAPP_VNF_LB[0-9]_SERVER[0-9]_HOST none Real server address (IP or hostname)
ONEAPP_VNF_LB[0-9]_SERVER[0-9]_PORT none Optional: Real server port
ONEAPP_VNF_LB[0-9]_SERVER[0-9]_WEIGHT 1 Real server weight
ONEAPP_VNF_LB[0-9]_SERVER[0-9]_ULIMIT 0 (disabled) Real server upper limit on connections
ONEAPP_VNF_LB[0-9]_SERVER[0-9]_LLIMIT 0 (disabled) Real server lower limit on connections
ONEGATE_LB[0-9]_IP none Mandatory: The load balanced IP address
ONEGATE_LB[0-9]_PORT none Mandatory: The load balanced IP port
ONEGATE_LB[0-9]_SERVER_HOST none Real server address (IP or hostname)
ONEGATE_LB[0-9]_SERVER_PORT none Real server port
ONEGATE_LB[0-9]_SERVER_WEIGHT 1 Real server weight
ONEGATE_LB[0-9]_SERVER_ULIMIT 0 (disabled) Real server upper limit on connections
ONEGATE_LB[0-9]_SERVER_LLIMIT 0 (disabled) Real server lower limit on connections

Warning

In the static scenario parameters ONEAPP_VNF_LB[0-9]_PORT and ONEAPP_VNF_LB[0-9]_SERVER[0-9]_PORT are optional, when undefined, LVS assumes all ports have to be forwarded. In result a mapping similar to SDNAT4is created, the differences are that (unlike SDNAT4) it's unidirectional and load-balanced. Since LVS takes precedence over ports opened in the VR, you may lose SSH access to your VR instance when misconfigured.

HAProxy

Parameter Default Description
ONEAPP_VNF_HAPROXY_ENABLED NO Enable/Disable HAProxy feature (YES/NO)
ONEAPP_VNF_HAPROXY_ONEGATE_ENABLED NO Enable/Disable dynamic backends via OneGate (YES/NO)
ONEAPP_VNF_HAPROXY_INTERFACES all NICs List of NICs to listen on (<[!]ethX> ...)
ONEAPP_VNF_HAPROXY_REFRESH_RATE 30 Refresh rate between updates of the pool of backends (seconds)
ONEAPP_VNF_HAPROXY_LB[0-9]_IP none Mandatory: Load balanced IP address
ONEAPP_VNF_HAPROXY_LB[0-9]_PORT none Mandatory: IP port to specify connection
ONEAPP_VNF_HAPROXY_LB[0-9]_SERVER[0-9]_HOST none Backend address (IP or hostname)
ONEAPP_VNF_HAPROXY_LB[0-9]_SERVER[0-9]_PORT none Backend port
ONEGATE_HAPROXY_LB[0-9]_IP none Mandatory: The load balanced IP address
ONEGATE_HAPROXY_LB[0-9]_PORT none Mandatory: The load balanced IP port
ONEGATE_HAPROXY_LB[0-9]_SERVER_HOST none Backend address (IP or hostname)
ONEGATE_HAPROXY_LB[0-9]_SERVER_PORT none Backend port
⚠️ **GitHub.com Fallback** ⚠️