Skip to content

Backend server instances order can be inconsistent across controller instances #823

Description

@davoustp

Symptom

The haproxy configuration generated on controller instances are not consistent from one to another: the order of backends can be different, which becomes a problem with a non randomized / round-robin load balancing algorithm.

As a result, when using load balancing algorithm such as first, the request will be routed differently depending on which haproxy controller instance received the request in the first place.
In other words, the routing will be inconsistent across controller instances.

Expected behaviour

The backend orders must be the same on all controller instances across a ingress controller deployment.

Environment details

kubernetes-ingress is a custom build (bringing FIPS compliance) using tag v3.2.11 as the baseline (FIPS-compliant Ubuntu 22.04.5 base image), haproxy tag 3.2.20 and go version 1.26.2.

Deployed using the official Helm chart inDeployment mode with 2 replicas.

Use case details

The haproxy ingress controller is deployed with 2 instances, sitting behind an edge load balancer.

An external (non Kubernetes) HTTP service is running on two distinct IP addresses, same port.
This service is declared using an Ingress pointing at a Service with a corresponding EndpointSlice as shown below:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ext-svc
  namespace: test-ns
  annotations:
    haproxy.org/check: "true"
    haproxy.org/server-ca: test-ns/server-ca
    haproxy.org/server-ssl: "true"
spec:
  ingressClassName: ingress-classname
  rules:
  - host: a.fully.qualified.domain
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: ext-svc
            port:
              name: https
  tls:
  - hosts:
    - a.fully.qualified.domain
    secretName: ingress-tls
apiVersion: v1
kind: Service
metadata:
  name: ext-svc
  namespace: test-ns
  annotations:
    haproxy.org/check-http: GET /
    haproxy.org/check-interval: 5s
    haproxy.org/load-balance: first
    haproxy.org/pod-maxconn: "65535"
spec:
  ports:
  - name: https
    port: 443
    protocol: TCP
    targetPort: 443
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: ext-svc
  namespace: test-ns
  labels:
    kubernetes.io/service-name: ext-svc
addressType: IPv4
ports:
- name: https
  port: 443
  protocol: TCP
endpoints:
- addresses:
  - 10.164.37.182
  - 10.164.37.183
  conditions:
    ready: true
    serving: true
    terminating: false

The ingress controller is deployed as a Deployment(using the official Helm chart) with 2 replicas.

Important to reproduce:

  • there are two nodes in the cluster, node A (IP 10.164.37.182) and node B (IP 10.164.37.183)
  • the two haproxy controllers are running on these nodes A and B
  • the two external service instances run on the same node A and B (as a systemd unit, not as Kubernetes workload)
  • restart node A, then wait for it to become ready again before restarting node B.

The backend generated for the resources above (located in file /etc/haproxy/haproxy.cfg) are:

  • Ingress Controller instance 1 (node A)
backend test-ns_svc_ext-svc_https from haproxytech
  mode http
  balance first
  option forwardfor
  no option abortonclose
  option httpchk GET /
  default-server check ssl alpn h2,http/1.1 ca-file /etc/haproxy/certs/ca/test-ns_server-ca.pem inter 5000 maxconn 32767 verify required
  server SRV_1 10.164.37.182:443 enabled
  server SRV_2 10.164.37.183:443 enabled
  server SRV_3 127.0.0.1:1 disabled

=> SRV_1 is 10.164.37.182

  • Ingress Controller instance 2 (node B)
backend test-ns_svc_ext-svc_https from haproxytech
  mode http
  balance first
  option forwardfor
  no option abortonclose
  option httpchk GET /
  default-server check ssl alpn h2,http/1.1 ca-file /etc/haproxy/certs/ca/test-ns_server-ca.pem inter 5000 maxconn 32767 verify required
  server SRV_1 10.164.37.183:443 enabled
  server SRV_2 10.164.37.182:443 enabled
  server SRV_3 127.0.0.1:1 disabled

=> SRV_1 is 10.164.37.183

Hitting the front load balancer routes to either haproxy instances, and shows that both backend server instances are hit instead of only one of them, defeating the firstalgorithm entirely.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions