Load balancing on OpenStack

Limitations of load balancer services

OKD clusters on OpenStack use Octavia to handle load balancer services. As a result of this choice, such clusters have a number of functional limitations.

OpenStack Octavia has two supported providers: Amphora and OVN. These providers differ in terms of available features as well as implementation details. These distinctions affect load balancer services that are created on your cluster.

Local external traffic policies

You can set the external traffic policy (ETP) parameter, .spec.externalTrafficPolicy, on a load balancer service to preserve the source IP address of incoming traffic when it reaches service endpoint pods. However, if your cluster uses the Amphora Octavia provider, the source IP of the traffic is replaced with the IP address of the Amphora VM. This behavior does not occur if your cluster uses the OVN Octavia provider.

Having the ETP option set to Local requires that health monitors be created for the load balancer. Without health monitors, traffic can be routed to a node that doesn’t have a functional endpoint, which causes the connection to drop. To force Cloud Provider OpenStack to create health monitors, you must set the value of the create-monitor option in the cloud provider configuration to true.

In OpenStack 16.1 and 16.2, the OVN Octavia provider does not support health monitors. Therefore, setting the ETP to local is unsupported.

In OpenStack 16.1 and 16.2, the Amphora Octavia provider does not support HTTP monitors on UDP pools. As a result, UDP load balancer services have UDP-CONNECT monitors created instead. Due to implementation details, this configuration only functions properly with the OVN-Kubernetes CNI plugin. When the OpenShift SDN CNI plugin is used, the UDP services alive nodes are detected unreliably.

Load balancer source ranges

Use the .spec.loadBalancerSourceRanges property to restrict the traffic that can pass through the load balancer according to source IP. This property is supported for use with the Amphora Octavia provider only. If your cluster uses the OVN Octavia provider, the option is ignored and traffic is unrestricted.

Using the Octavia OVN load balancer provider driver with Kuryr SDN

Kuryr is a deprecated feature. Deprecated functionality is still included in OKD and continues to be supported; however, it will be removed in a future release of this product and is not recommended for new deployments.

For the most recent list of major functionality that has been deprecated or removed within OKD, refer to the Deprecated and removed features section of the OKD release notes.

If your OKD cluster uses Kuryr and was installed on a OpenStack 13 cloud that was later upgraded to OpenStack 16, you can configure it to use the Octavia OVN provider driver.

Kuryr replaces existing load balancers after you change provider drivers. This process results in some downtime.

Prerequisites

  • Install the OpenStack CLI, openstack.

  • Install the OKD CLI, oc.

  • Verify that the Octavia OVN driver on OpenStack is enabled.

    To view a list of available Octavia drivers, on a command line, enter openstack loadbalancer provider list.

    The ovn driver is displayed in the command’s output.

Procedure

To change from the Octavia Amphora provider driver to Octavia OVN:

  1. Open the kuryr-config ConfigMap. On a command line, enter:

    1. $ oc -n openshift-kuryr edit cm kuryr-config
  2. In the ConfigMap, delete the line that contains kuryr-octavia-provider: default. For example:

    1. ...
    2. kind: ConfigMap
    3. metadata:
    4. annotations:
    5. networkoperator.openshift.io/kuryr-octavia-provider: default (1)
    6. ...
    1Delete this line. The cluster will regenerate it with ovn as the value.

    Wait for the Cluster Network Operator to detect the modification and to redeploy the kuryr-controller and kuryr-cni pods. This process might take several minutes.

  3. Verify that the kuryr-config ConfigMap annotation is present with ovn as its value. On a command line, enter:

    1. $ oc -n openshift-kuryr edit cm kuryr-config

    The ovn provider value is displayed in the output:

    1. ...
    2. kind: ConfigMap
    3. metadata:
    4. annotations:
    5. networkoperator.openshift.io/kuryr-octavia-provider: ovn
    6. ...
  4. Verify that OpenStack recreated its load balancers.

    1. On a command line, enter:

      1. $ openstack loadbalancer list | grep amphora

      A single Amphora load balancer is displayed. For example:

      1. a4db683b-2b7b-4988-a582-c39daaad7981 | ostest-7mbj6-kuryr-api-loadbalancer | 84c99c906edd475ba19478a9a6690efd | 172.30.0.1 | ACTIVE | amphora
    2. Search for ovn load balancers by entering:

      1. $ openstack loadbalancer list | grep ovn

      The remaining load balancers of the ovn type are displayed. For example:

      1. 2dffe783-98ae-4048-98d0-32aa684664cc | openshift-apiserver-operator/metrics | 84c99c906edd475ba19478a9a6690efd | 172.30.167.119 | ACTIVE | ovn
      2. 0b1b2193-251f-4243-af39-2f99b29d18c5 | openshift-etcd/etcd | 84c99c906edd475ba19478a9a6690efd | 172.30.143.226 | ACTIVE | ovn
      3. f05b07fc-01b7-4673-bd4d-adaa4391458e | openshift-dns-operator/metrics | 84c99c906edd475ba19478a9a6690efd | 172.30.152.27 | ACTIVE | ovn

Scaling clusters for application traffic by using Octavia

OKD clusters that run on OpenStack can use the Octavia load balancing service to distribute traffic across multiple virtual machines (VMs) or floating IP addresses. This feature mitigates the bottleneck that single machines or addresses create.

If your cluster uses Kuryr, the Cluster Network Operator created an internal Octavia load balancer at deployment. You can use this load balancer for application network scaling.

If your cluster does not use Kuryr, you must create your own Octavia load balancer to use it for application network scaling.

Scaling clusters by using Octavia

If you want to use multiple API load balancers, or if your cluster does not use Kuryr, create an Octavia load balancer and then configure your cluster to use it.

Prerequisites

  • Octavia is available on your OpenStack deployment.

Procedure

  1. From a command line, create an Octavia load balancer that uses the Amphora driver:

    1. $ openstack loadbalancer create --name API_OCP_CLUSTER --vip-subnet-id <id_of_worker_vms_subnet>

    You can use a name of your choice instead of API_OCP_CLUSTER.

  2. After the load balancer becomes active, create listeners:

    1. $ openstack loadbalancer listener create --name API_OCP_CLUSTER_6443 --protocol HTTPS--protocol-port 6443 API_OCP_CLUSTER

    To view the status of the load balancer, enter openstack loadbalancer list.

  3. Create a pool that uses the round robin algorithm and has session persistence enabled:

    1. $ openstack loadbalancer pool create --name API_OCP_CLUSTER_pool_6443 --lb-algorithm ROUND_ROBIN --session-persistence type=<source_IP_address> --listener API_OCP_CLUSTER_6443 --protocol HTTPS
  4. To ensure that control plane machines are available, create a health monitor:

    1. $ openstack loadbalancer healthmonitor create --delay 5 --max-retries 4 --timeout 10 --type TCP API_OCP_CLUSTER_pool_6443
  5. Add the control plane machines as members of the load balancer pool:

    1. $ for SERVER in $(MASTER-0-IP MASTER-1-IP MASTER-2-IP)
    2. do
    3. openstack loadbalancer member create --address $SERVER --protocol-port 6443 API_OCP_CLUSTER_pool_6443
    4. done
  6. Optional: To reuse the cluster API floating IP address, unset it:

    1. $ openstack floating ip unset $API_FIP
  7. Add either the unset API_FIP or a new address to the created load balancer VIP:

    1. $ openstack floating ip set --port $(openstack loadbalancer show -c <vip_port_id> -f value API_OCP_CLUSTER) $API_FIP

Your cluster now uses Octavia for load balancing.

If Kuryr uses the Octavia Amphora driver, all traffic is routed through a single Amphora virtual machine (VM).

You can repeat this procedure to create additional load balancers, which can alleviate the bottleneck.

Scaling clusters that use Kuryr by using Octavia

Kuryr is a deprecated feature. Deprecated functionality is still included in OKD and continues to be supported; however, it will be removed in a future release of this product and is not recommended for new deployments.

For the most recent list of major functionality that has been deprecated or removed within OKD, refer to the Deprecated and removed features section of the OKD release notes.

If your cluster uses Kuryr, associate the API floating IP address of your cluster with the pre-existing Octavia load balancer.

Prerequisites

  • Your OKD cluster uses Kuryr.

  • Octavia is available on your OpenStack deployment.

Procedure

  1. Optional: From a command line, to reuse the cluster API floating IP address, unset it:

    1. $ openstack floating ip unset $API_FIP
  2. Add either the unset API_FIP or a new address to the created load balancer VIP:

    1. $ openstack floating ip set --port $(openstack loadbalancer show -c <vip_port_id> -f value ${OCP_CLUSTER}-kuryr-api-loadbalancer) $API_FIP

Your cluster now uses Octavia for load balancing.

If Kuryr uses the Octavia Amphora driver, all traffic is routed through a single Amphora virtual machine (VM).

You can repeat this procedure to create additional load balancers, which can alleviate the bottleneck.

Scaling for ingress traffic by using OpenStack Octavia

Kuryr is a deprecated feature. Deprecated functionality is still included in OKD and continues to be supported; however, it will be removed in a future release of this product and is not recommended for new deployments.

For the most recent list of major functionality that has been deprecated or removed within OKD, refer to the Deprecated and removed features section of the OKD release notes.

You can use Octavia load balancers to scale Ingress controllers on clusters that use Kuryr.

Prerequisites

  • Your OKD cluster uses Kuryr.

  • Octavia is available on your OpenStack deployment.

Procedure

  1. To copy the current internal router service, on a command line, enter:

    1. $ oc -n openshift-ingress get svc router-internal-default -o yaml > external_router.yaml
  2. In the file external_router.yaml, change the values of metadata.name and spec.type to LoadBalancer.

    Example router file

    1. apiVersion: v1
    2. kind: Service
    3. metadata:
    4. labels:
    5. ingresscontroller.operator.openshift.io/owning-ingresscontroller: default
    6. name: router-external-default (1)
    7. namespace: openshift-ingress
    8. spec:
    9. ports:
    10. - name: http
    11. port: 80
    12. protocol: TCP
    13. targetPort: http
    14. - name: https
    15. port: 443
    16. protocol: TCP
    17. targetPort: https
    18. - name: metrics
    19. port: 1936
    20. protocol: TCP
    21. targetPort: 1936
    22. selector:
    23. ingresscontroller.operator.openshift.io/deployment-ingresscontroller: default
    24. sessionAffinity: None
    25. type: LoadBalancer (2)
    1Ensure that this value is descriptive, like router-external-default.
    2Ensure that this value is LoadBalancer.

You can delete timestamps and other information that is irrelevant to load balancing.

  1. From a command line, create a service from the external_router.yaml file:

    1. $ oc apply -f external_router.yaml
  2. Verify that the external IP address of the service is the same as the one that is associated with the load balancer:

    1. On a command line, retrieve the external IP address of the service:

      1. $ oc -n openshift-ingress get svc

      Example output

      1. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
      2. router-external-default LoadBalancer 172.30.235.33 10.46.22.161 80:30112/TCP,443:32359/TCP,1936:30317/TCP 3m38s
      3. router-internal-default ClusterIP 172.30.115.123 <none> 80/TCP,443/TCP,1936/TCP 22h
    2. Retrieve the IP address of the load balancer:

      1. $ openstack loadbalancer list | grep router-external

      Example output

      1. | 21bf6afe-b498-4a16-a958-3229e83c002c | openshift-ingress/router-external-default | 66f3816acf1b431691b8d132cc9d793c | 172.30.235.33 | ACTIVE | octavia |
    3. Verify that the addresses you retrieved in the previous steps are associated with each other in the floating IP list:

      1. $ openstack floating ip list | grep 172.30.235.33

      Example output

      1. | e2f80e97-8266-4b69-8636-e58bacf1879e | 10.46.22.161 | 172.30.235.33 | 655e7122-806a-4e0a-a104-220c6e17bda6 | a565e55a-99e7-4d15-b4df-f9d7ee8c9deb | 66f3816acf1b431691b8d132cc9d793c |

You can now use the value of EXTERNAL-IP as the new Ingress address.

If Kuryr uses the Octavia Amphora driver, all traffic is routed through a single Amphora virtual machine (VM).

You can repeat this procedure to create additional load balancers, which can alleviate the bottleneck.

Configuring an external load balancer

You can configure an OKD cluster on OpenStack to use an external load balancer in place of the default load balancer.

You can also configure an OKD cluster to use an external load balancer that supports multiple subnets. If you use multiple subnets, you can explicitly list all the IP addresses in any networks that are used by your load balancer targets. This configuration can reduce maintenance overhead because you can create and destroy nodes within those networks without reconfiguring the load balancer targets.

If you deploy your ingress pods by using a machine set on a smaller network, such as a /27 or /28, you can simplify your load balancer targets.

You do not need to specify API and Ingress static addresses for your installation program. If you choose this configuration, you must take additional actions to define network targets that accept an IP address from each referenced vSphere subnet.

Prerequisites

  • On your load balancer, TCP over ports 6443, 443, and 80 must be reachable by all users of your system that are located outside the cluster.

  • Load balance the application ports, 443 and 80, between all the compute nodes.

  • Load balance the API port, 6443, between each of the control plane nodes.

  • On your load balancer, port 22623, which is used to serve ignition startup configurations to nodes, is not exposed outside of the cluster.

  • Your load balancer can access the required ports on each node in your cluster. You can ensure this level of access by completing the following actions:

    • The API load balancer can access ports 22623 and 6443 on the control plane nodes.

    • The ingress load balancer can access ports 443 and 80 on the nodes where the ingress pods are located.

External load balancing services and the control plane nodes must run on the same L2 network, and on the same VLAN when using VLANs to route traffic between the load balancing services and the control plane nodes.

Procedure

  1. Enable access to the cluster from your load balancer on ports 6443, 443, and 80.

    As an example, note this HAProxy configuration:

    A section of a sample HAProxy configuration

    1. ...
    2. listen my-cluster-api-6443
    3. bind 0.0.0.0:6443
    4. mode tcp
    5. balance roundrobin
    6. server my-cluster-master-2 192.0.2.2:6443 check
    7. server my-cluster-master-0 192.0.2.3:6443 check
    8. server my-cluster-master-1 192.0.2.1:6443 check
    9. listen my-cluster-apps-443
    10. bind 0.0.0.0:443
    11. mode tcp
    12. balance roundrobin
    13. server my-cluster-worker-0 192.0.2.6:443 check
    14. server my-cluster-worker-1 192.0.2.5:443 check
    15. server my-cluster-worker-2 192.0.2.4:443 check
    16. listen my-cluster-apps-80
    17. bind 0.0.0.0:80
    18. mode tcp
    19. balance roundrobin
    20. server my-cluster-worker-0 192.0.2.7:80 check
    21. server my-cluster-worker-1 192.0.2.9:80 check
    22. server my-cluster-worker-2 192.0.2.8:80 check
  2. Add records to your DNS server for the cluster API and apps over the load balancer. For example:

    1. <load_balancer_ip_address> api.<cluster_name>.<base_domain>
    2. <load_balancer_ip_address> apps.<cluster_name>.<base_domain>
  3. From a command line, use curl to verify that the external load balancer and DNS configuration are operational.

    1. Verify that the cluster API is accessible:

      1. $ curl https://<loadbalancer_ip_address>:6443/version --insecure

      If the configuration is correct, you receive a JSON object in response:

      1. {
      2. "major": "1",
      3. "minor": "11+",
      4. "gitVersion": "v1.11.0+ad103ed",
      5. "gitCommit": "ad103ed",
      6. "gitTreeState": "clean",
      7. "buildDate": "2019-01-09T06:44:10Z",
      8. "goVersion": "go1.10.3",
      9. "compiler": "gc",
      10. "platform": "linux/amd64"
      11. }
    2. Verify that cluster applications are accessible:

      You can also verify application accessibility by opening the OKD console in a web browser.

      1. $ curl http://console-openshift-console.apps.<cluster_name>.<base_domain> -I -L --insecure

      If the configuration is correct, you receive an HTTP response:

      1. HTTP/1.1 302 Found
      2. content-length: 0
      3. location: https://console-openshift-console.apps.<cluster-name>.<base domain>/
      4. cache-control: no-cacheHTTP/1.1 200 OK
      5. referrer-policy: strict-origin-when-cross-origin
      6. set-cookie: csrf-token=39HoZgztDnzjJkq/JuLJMeoKNXlfiVv2YgZc09c3TBOBU4NI6kDXaJH1LdicNhN1UsQWzon4Dor9GWGfopaTEQ==; Path=/; Secure
      7. x-content-type-options: nosniff
      8. x-dns-prefetch-control: off
      9. x-frame-options: DENY
      10. x-xss-protection: 1; mode=block
      11. date: Tue, 17 Nov 2020 08:42:10 GMT
      12. content-type: text/html; charset=utf-8
      13. set-cookie: 1e2670d92730b515ce3a1bb65da45062=9b714eb87e93cf34853e87a92d6894be; path=/; HttpOnly; Secure; SameSite=None
      14. cache-control: private