Kubernetes Load-Balancer service

When the services are enabled for kube-vip a watcher is enabled on all services that match the type loadBalancer. The "watcher" will only advertise a kubernetes service once the metadata.annotations["kube-vip.io/loadbalancerIPs"](Since kube-vip 0.5.12) or spec.loadBalancerIP has been populated, which is the role performed by a cloud controller. Additionally kube-vip may ignore or act upon a service depending on various annotations.

Configure kube-vip to ignore a service

To configure kube-vip to ignore a service add a kube-vip.io/ignore=true annotation in the metadata.annotations

Kubernetes LoadBalancer Class (Kubernetes v1.24+) (kube-vip v0.5.0+)

Default behavior

The watcher will always examine the load balancer class , and if it isn't set then will assume that classes aren't being set and act upon the service. If the spec.loadBalancerClass has been set in the service spec then kube-vip will only act IF the spec has been set to:

spec.loadBalancerClass=kube-vip.io/kube-vip-class.

Optional behavior

Alternate LoadBalancer Class handlig behavior can be enabled by setting LoadBalancerClassLegacyHandling config flag to false. In this mode, kube-vip will only process the service if it's spec.loadBalancerClass value is equal to the class configured in kube-vip using LoadBalancerClassName flag.

Multiple services on the same IP

It is entirely possible for multiple services to share the same IP, as long as the exposed port is unique.

The below example will create two load balancer services that listen on the same ip 192.168.0.220 but expose port 80 & 81.

$ kubectl expose deployment test-deploy \
    --port=80 --target-port=80 \
    --type=LoadBalancer --name http1 \
    --load-balancer-ip=192.168.0.220
service/http1 exposed

$ kubectl expose deployment test-deploy \
    --port=81 --target-port=80 \
    --type=LoadBalancer --name http2 \
    --load-balancer-ip=192.168.0.220
service/http2 exposed

Load Balancing Load Balancers (when using ARP mode, yes you read that correctly) (kube-vip v0.5.0+)

By default ARP mode provides a HA implementation of a VIP (your service IP address) which will receive traffic on the kube-vip leader. This leader kube-vip instance will then drop traffic onto the kube-proxy managed services network and load-balance it to one of the pods under the service. Whilst this method works and has been used for years in a variety of implementations it has a severe limitation that all services and their traffic are ultimately pinned to a single node, which has been elected as the leader. This will eventually produce a bottleneck or large failure domain whenever this node needs downtime. To circumvent this kube-vip has implemented a new function which is "leader election per service", instead of one node becoming the leader for all services an election is held across all kube-vip instances and the leader from that election becomes the holder of that service. Ultimately, this means that every service can end up on a different node when it is created in theory preventing a bottleneck in the initial deployment.

The kube-vip yaml will require the following:

1- name: enableServicesElection
2  value: "true"

External traffic policy (kube-vip v0.5.0+)

By default Kubernetes will use the policy cluster as the policy for all traffic that is external coming into the cluster. What this means is that as traffic enters the Kubernetes cluster through the load balancer address it is then placed on the service networking managed by kube-proxy where it is then NAT'd and directed to one of the pods anywhere within the cluster. This mode is fantastic as it automatically sends traffic to a pod regardless of where it is running inside the cluster and the end-user is normally non-the-wiser. However there are issues that this policy can create, namely source IP address presevation or direct access to pods etc. For more information consult the Kubernetes documentation

In order for the local traffic policy to work a load balancer services VIP needs to be exposed from a node that has one of the pods that is part of that service. This is because the traffic will go direct to that pod and not onto the services network ensuring that the source IP address is preserved.

In kube-vip a service can be created with the local traffic policy, if the enableServicesElection has been enabled. This is because when this service is created kube-vip will only allow nodes that have a local pod instance running to participate in the leaderElection.

Example for exposing an nginx deployment:

 1apiVersion: v1
 2kind: Service
 3metadata:
 4  name: nginx-service
 5spec:
 6  selector:
 7    app: nginx
 8  ports:
 9    - port: 80
10      targetPort: 80
11  externalTrafficPolicy: Local
12  type: LoadBalancer

Using DHCP for Load Balancers (experimental) (kube-vip v0.2.1+)

With kube-vip > 0.2.1, it is possible to use the local network DHCP server to provide kube-vip with a load balancer address that can be used to access a Kubernetes service on the network.

In order to do this, we need to signify to kube-vip and the cloud provider that we don't need one of their managed addresses. We do this by explicitly exposing a Service on the address 0.0.0.0. When kube-vip sees a Service on this address, it will create a macvlan interface on the host and request a DHCP address. Once this address is provided, it will assign it as the LoadBalancer IP and update the Kubernetes Service.

 1$ kubectl expose deployment nginx-deployment --port=80 --type=LoadBalancer --name=nginx-dhcp --load-balancer-ip=0.0.0.0; kubectl get svc
 2service/nginx-dhcp exposed
 3NAME         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
 4kubernetes   ClusterIP      10.96.0.1       <none>          443/TCP        17m
 5nginx-dhcp   LoadBalancer   10.97.150.208   0.0.0.0         80:31184/TCP   0s
 6
 7{ ... a second or so later ... }
 8
 9$ kubectl get svc
10NAME         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
11kubernetes   ClusterIP      10.96.0.1       <none>          443/TCP        17m
12nginx-dhcp   LoadBalancer   10.97.150.208   192.168.0.155   80:31184/TCP   3s

DCHP hostname support

You can also specify a hostname used for the DHCP lease by adding an annotation to your service.

apiVersion: v1
kind: Service
metadata:
  name: nginx-dhcp
  annotations:
    kube-vip.io/loadbalancerHostname: mydhcp-test
spec:
  loadBalancerIP: 0.0.0.0
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: hello-world
  type: LoadBalancer

Using UPnP to expose a Service to the outside world

With kube-vip > 0.2.1, it is possible to expose a Service of type LoadBalancer on a specific port to the Internet by using UPnP (on a supported gateway).

Most simple networks look something like the following:

<----- <internal network 192.168.0.0/24> <Gateway / router> <external network address> ----> Internet

Using UPnP we can create a matching port on the <external network address> allowing your Service to be exposed to the Internet.

Enable UPnP

Add the following to the kube-vip env: section of either the static Pod or DaemonSet for kube-vip, and the rest should be completely automated.

Note some environments may require (Unifi) Secure mode being disabled (this allows a host with a different address to register a port).

1- name: enableUPNP
2  value: "true"

Exposing a Service with UPnP

To expose a port successfully, we'll need to change the command slightly:

--target-port=80 the port of the application in the pods (HTT/NGINX) --port=32380 the port the Service will be exposed on (and what you should connect to in order to receive traffic from the Service)

kubectl expose deployment plunder-nginx --port=32380 --target-port=80 --type=LoadBalancer --namespace plunder

The above example should expose a port on your external (Internet facing) address that can be tested externally with:

1$ curl externalIP:32380
2<!DOCTYPE html>
3<html>
4...