Using Octavia (load balancing) with Kubernetes (k8s)

In Kubernetes objects of type LoadBalancer are an essential part for making applications accessible from the outside world. Of course, there are alternative methods like NodePort but only with a service of type LoadBalancer one can easily configure an ingress-controller to handle incoming traffic for different DNS-Names for the same external IP address and have incoming traffic automatically routed to the correct pods.

Following lines describe how to integrate the Openstack Cloud Controller Manager (OCCM) to your cluster. After the successful integration, whenever you create a service of type LoadBalancer, Oopenstack will spawn an amphora VM connect it to the appropriate network and make your service accessible via an external IP address. All resources will be deployed in the kube-system namespace.

Create an application secret

Create an application secret for the OCCM to be able to connect to Openstack.

openstack application credential create --description "Kubernetes" kubernetes --secret **********

Create a configuration secret

For the Kubernetes side of the communication you need to create a configuration secret which will contain the application id and secret (and a bit more) and will be mounted into the OCCM-Containers.

  • Create a file called cloud.conf which contains at least the following:

[Global]
auth-url=https://cc.lrz.de:5000
application-credential-id=XXXXXXXXXXXXXXXX
application-credential-secret=XXXXXXXXXXXXXXXX

[LoadBalancer]
use-octavia=true
floating-network-id=XXXXXXXXXXXXXXXXX
subnet-id=XXXXXXXXXXXXXXXXXXXXXXXXX

  • auth-url: self explanatory

  • application-credential-id: The id of the application credential you just created

  • application-credential-secret: The secret of the created application credential

  • floating-network-id: The ID of the external Openstack network. Eg.: 3f1c6c34-2be9-44b3-9f21-c3e031ab8e5c for the MWN.

  • subnet-id: The ID of the external pool to use. Eg. a3e4d020-c8b4-48b5-beb1-5f0d47d06ed7 for MWN addresses.

  • Create the configuration secret based on the file (make sure to have the name right!)

kubectl create secret -n kube-system generic cloud-config --from-file=cloud.conf

Rollout the OCCM

Execute following commands in the context of your cluster to deploy the Openstack Controller Manager

kubectl apply -f https://raw.githubusercontent.com/kubernetes/cloud-provider-openstack/master/manifests/controller-manager/cloud-controller-manager-roles.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/cloud-provider-openstack/master/manifests/controller-manager/cloud-controller-manager-role-bindings.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/cloud-provider-openstack/master/manifests/controller-manager/openstack-cloud-controller-manager-ds.yaml

The OCCM will now be deployed as a daemonset. Note, that by default it will prefer the control-plane for scheduling. So if you have non schedulable control-planes, adjust the nodeSelector in the daemonset manifest accordingly or just remove the entire two lines:

nodeSelector:
    node-role.kubernetes.io/control-plane: ""

in the daemonset manifest.

Test functionality

If everything is configured correctly OCCM will now create an amphora-VM in Openstack whenever you create an object of type LoadBalancer. In the following we create a simple test deployment with three replicas. Each replica will simply return the hostname of the underling container.

  • Create a file containing the declaration of the deployment and the service:

cat << 'EOF' >> test-octavia.yaml                                                                                                                               
> ---                                                                                                                                                                                                                                                                 
apiVersion: apps/v1                                                                                                                                                                                                                                                   
kind: Deployment                                                                                                                                                                                                                                                      
metadata:                                                                                                                                                                                                                                                             
  name: octavia-test-deployment                                                                                                                                                                                                                                       
spec:                                                                                                                                                                                                                                                                 
  replicas: 3                                                                                                                                                                                                                                                         
  selector:                                                                                                                                                                                                                                                           
    matchLabels:                                                                                                                                                                                                                                                      
      app: occm-test                                                                                                                                                                                                                                                  
  template:                                                                                                                                                                                                                                                           
    metadata:                                                                                                                                                                                                                                                         
      labels:                                                                                                                                                                                                                                                         
        app: occm-test                                                                                                                                                                                                                                                
    spec:                                                                                                                                                                                                                                                             
      containers:                                                                                                                                                                                                                                                     
      - name: occm-test                                                                                                                                                                                                                                               
        image: lingxiankong/alpine-test                                                                                                                                                                                                                               
        ports:                                                                                                                                                                                                                                                        
        - containerPort: 80                                                                                                                                                                                                                                           
---                                                                                                                                                                                                                                                                   
apiVersion: v1                                                                                                                                                                                                                                                        
kind: Service                                                                                                                                                                                                                                                         
metadata:                                                                                                                                                                                                                                                             
  name: occm-test-svc                                                                                                                                                                                                                                                 
spec:                                                                                                                                                                                                                                                                 
  selector:                                                                                                                                                                                                                                                           
    app: occm-test                                                                                                                                                                                                                                                    
  type: LoadBalancer                                                                                                                                                                                                                                                  
  ports:                                                                                                                                                                                                                                                              
  - name: http                                                                                                                                                                                                                                                        
    port: 80                                                                                                                                                                                                                                                          
    targetPort: 8080                                                                                                                                                                                                                                                  
> EOF

  • Create the resources:  kubectl apply -f test-octavia.yaml

  • Wait for the loadbalancer object to be created and get an ip assigned via octavia

kubectl get svc/occm-test-svc -w
NAME            TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
occm-test-svc   LoadBalancer   10.240.19.110   <pending>     80:31833/TCP   35s

After the process is completed it should look similar to:

kubectl get svc/occm-test-svc   
NAME            TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)        AGE
occm-test-svc   LoadBalancer   10.240.19.110   10.195.11.99   80:31833/TCP   2m9s

Note: The service now got an external IP assigned.

Verify actual loadbalancing

The functionality can easily be verified via curl. Just curl the external IP in a loop an see how the different containers respond to the requests.

for i in {1..27}; do curl http://10.195.11.99; echo -n; done
octavia-test-deployment-86d647c98d-z5nzj
octavia-test-deployment-86d647c98d-7xl2k
octavia-test-deployment-86d647c98d-lvslp
octavia-test-deployment-86d647c98d-lvslp
octavia-test-deployment-86d647c98d-z5nzj
octavia-test-deployment-86d647c98d-lvslp
octavia-test-deployment-86d647c98d-7xl2k
octavia-test-deployment-86d647c98d-z5nzj
octavia-test-deployment-86d647c98d-lvslp
octavia-test-deployment-86d647c98d-z5nzj
octavia-test-deployment-86d647c98d-z5nzj
octavia-test-deployment-86d647c98d-lvslp
octavia-test-deployment-86d647c98d-z5nzj
octavia-test-deployment-86d647c98d-z5nzj
octavia-test-deployment-86d647c98d-lvslp
octavia-test-deployment-86d647c98d-lvslp
octavia-test-deployment-86d647c98d-z5nzj
octavia-test-deployment-86d647c98d-lvslp
octavia-test-deployment-86d647c98d-lvslp
octavia-test-deployment-86d647c98d-7xl2k
octavia-test-deployment-86d647c98d-z5nzj
octavia-test-deployment-86d647c98d-lvslp
octavia-test-deployment-86d647c98d-lvslp
octavia-test-deployment-86d647c98d-7xl2k
octavia-test-deployment-86d647c98d-z5nzj
octavia-test-deployment-86d647c98d-7xl2k
octavia-test-deployment-86d647c98d-lvslp

We can also see that the traffic gets balanced to the different pods nearly equally:

for i in {1..27}; do curl http://10.195.11.99; echo -n; done &>/dev/null | sort | uniq  -c | sort -nr 
     11 octavia-test-deployment-86d647c98d-7xl2k
      8 octavia-test-deployment-86d647c98d-z5nzj
      8 octavia-test-deployment-86d647c98d-lvslp