BareMetal LoadBalancer for Kubernetes using MetalLB

Sat, Oct 13, 2018 2-minute read

In this post we’ll install and use the MetalLB L2 load balancer for Kubernetes.

The problem MetalLB solves is a familiar one: if you’re not on AWS, GKE or AKS, a LoadBalancer service on Kubernetes simply stays stuck in the pending state. MetalLB steps in to create a load balancer for your bare-metal Kubernetes, supporting both Border Gateway Protocol and L2.

To have something to work with, I created a DigitalOcean droplet in Frankfurt with 2 GB of RAM and installed kubeadm on it.

The MetalLB installation YAML takes care of the groundwork for us: it first creates a namespace for MetalLB, then creates and applies RBAC for that namespace.

Once the YAML is applied, it’s worth checking that our pods and services are running without any errors.

root@frankfurt-kubeadm1-ub2g:~/metallb# kubectl get po -n metallb-system
NAME                         READY   STATUS    RESTARTS   AGE
controller-765899887-jn8jq   1/1     Running   0          23s
speaker-z6p4q                1/1     Running   0          24s

These two pieces split the work between them: the speaker sends ARP requests and obtains IP addresses for the load balancer, while the controller allocates an IP address to the LoadBalancer service for Kubernetes.

The last step is to hand MetalLB a config, where we define which protocol and which IP addresses will be used for the service.

Since I’m building this example on kubeadm with just one node, I want to publish to that node’s own IP address.

That means giving MetalLB a single host CIDR, “/32”.

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 159.89.3**.***/32    

With the config applied, we can dig into the speaker logs to see it in action.

{"caller":"arp.go:102","interface":"weave","ip":"159.89.3**.***","msg":"got ARP request for service IP, sending response","responseMAC":"b2:1e:26:**:**:**","senderIP":"159.89.3**.***","senderMAC":"c6:a4:2a:**:**:**","ts":"2018-10-13T12:04:58.497978319Z"}

Our kubectl get svc looks awesome:

root@frankfurt-kubeadm1-ub2g:~/metallb# kubectl get svc nginx
NAME    TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
nginx   LoadBalancer   10.109.176.39   159.89.3**.***   80:30311/TCP   8m36s

Service panel:

kubernetes-dashboard-service

Trying it out:

root@frankfurt-kubeadm1-ub2g:~/metallb# kubectl get svc nginx
NAME    TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
nginx   LoadBalancer   10.109.176.39   159.89.3**.***   80:30311/TCP   17m
root@frankfurt-kubeadm1-ub2g:~/metallb# curl 159.89.3**.***
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>