PI4 Stories

Raspberry Pi 4 cluster Series - Replacing internal traefik with Metallb

The problem with have is that with our home pi4 cluster we don't have a decent external load-balancer. Therefore, it is hard to access pods via an external IP address, such as the ones we have on our hosts (in our case in the range of 192.168.0.200-254).

To read some more in-depth learnings from metallb and ingresses read the blog Ingresses and Load Balancers in Kubernetes with MetalLB and nginx-ingress

The steps we need to perform are...

Re-configure the pi4 cluster with ansible

Our k3s-ansible project was updated with:

$ cat inventory/my-cluster/group_vars/all.yml
---
k3s_version: v1.26.0+k3s2
ansible_user: gdha
systemd_dir: /etc/systemd/system
master_ip: "{{ hostvars[groups['master'][0]]['ansible_host'] | default(groups['master'][0]) }}"
extra_server_args: "--write-kubeconfig-mode 644 --disable traefik --disable servicelb"
extra_agent_args: ""

in such way by disabling the default traefik and internal load-balancer delivered with the standard k3s implementation. While we were busy we also used the latest k3s version available at this given moment.

Then it is just a matter of re-running:

ansible-playbook site.yml -i inventory/my-cluster/hosts.ini

It will remove k3s and re-implement it with the internal traefik, but all pods already installed remain present. Excellent news.

Install metalllb layer2 load-balancer

The main documentation of metallb can be found at https://metallb.universe.tf/installation/ [1]. We used the following steps:

$ helm repo add metallb https://metallb.github.io/metallb
$ helm repo list
NAME        URL                              
longhorn    https://charts.longhorn.io       
kiwigrid    https://kiwigrid.github.io       
metallb     https://metallb.github.io/metallb

$ cat metallb-values.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: loadbalancer-pool
  namespace: kube-system
spec:
  addresses:
  - 192.168.0.230-192.168.0.250


$ helm install metallb metallb/metallb --namespace kube-system -f metallb-values.yaml 
NAME: metallb
LAST DEPLOYED: Tue Jan 24 11:24:43 2023
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
MetalLB is now running in the cluster.

Now you can configure it via its CRs. Please refer to the metallb official docs
on how to use the CRs.

We still have the old config of metallb so we save it under the config.yaml

$ cat >config.yaml <<EOD
apiVersion: v1
data:
  config: |
    address-pools:
    - addresses:
      - 192.168.0.230-192.168.0.250
      name: default
      protocol: layer2
kind: ConfigMap
metadata:
  annotations:
    meta.helm.sh/release-name: metallb
    meta.helm.sh/release-namespace: kube-system
  creationTimestamp: "2022-05-23T10:38:34Z"
  labels:
    app.kubernetes.io/instance: metallb
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: metallb
    app.kubernetes.io/version: v0.12.1
    helm.sh/chart: metallb-0.12.1
  name: metallb
  namespace: kube-system
  resourceVersion: "3256248"
  uid: 774b05a7-7ad7-4de3-a9e1-2a636f988ed1
EOD

According the procedure to generate a new CRD resource file we got:

$ docker run -d -v $(pwd):/var/input quay.io/metallb/configmaptocrs
Unable to find image 'quay.io/metallb/configmaptocrs:latest' locally
latest: Pulling from metallb/configmaptocrs
9b18e9b68314: Pull complete 
24157a5425f3: Pull complete 
b73e28ff5ad3: Pull complete 
Digest: sha256:6c144621e060722a082f2d5a2c4bd72f81d84f6cedc1153c33bb8f4f1277fac0
Status: Downloaded newer image for quay.io/metallb/configmaptocrs:latest
4254ef07d9b01effb956a89628915e4f3da15624e92edfc6c6f415f6fbe201cc

$ cat resources.yaml 
# This was autogenerated by MetalLB's custom resource generator.
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  creationTimestamp: null
  name: default
  namespace: kube-system
spec:
  addresses:
  - 192.168.0.230-192.168.0.250
status: {}
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  creationTimestamp: null
  name: l2advertisement1
  namespace: kube-system
spec:
  ipAddressPools:
  - default
status: {}
---

Now, we can apply (or replace) this in our cluster:

$ kubectl create -f resources.yaml
ipaddresspool.metallb.io/default created
l2advertisement.metallb.io/l2advertisement1 created

To verify if our IP range was properly accepted by our helm command we could run:

$ kubectl get customresourcedefinitions.apiextensions.k8s.io ipaddresspools.metallb.io
NAME                        CREATED AT
ipaddresspools.metallb.io   2023-01-24T10:24:52Z

Install NGINX Ingress Controller

To verify the load-balancer is working we could execute [4]:

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.5.1/deploy/static/provider/cloud/deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
$ kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.43.243.82    192.168.0.231   80:30648/TCP,443:30920/TCP   2m28s
ingress-nginx-controller-admission   ClusterIP      10.43.232.246   <none>          443/TCP                      2m28s

Okay, so far so good. However, is the external IP address of our ingress-nginx-controller really open for a connection? To test execute:

$ nc -vz 192.168.0.231 80
Connection to 192.168.0.231 80 port [tcp/http] succeeded!
gdha@n1:~/projects/WIP$ nc -vz 192.168.0.231 443
Connection to 192.168.0.231 443 port [tcp/https] succeeded!

Install traefik2 as replacement for the internal traefik of k3s

Execute the following commands:

$ helm repo add traefik https://helm.traefik.io/traefik
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "metallb" chart repository
...Successfully got an update from the "longhorn" chart repository
...Successfully got an update from the "traefik" chart repository
...Successfully got an update from the "kiwigrid" chart repository
Update Complete. ⎈Happy Helming!⎈

We need to define a dummy (internal) name for our treafik2 application, therefore, create a file like the one shown below:

$ cat traefik-values.yaml 
dashboard:
 enabled: true
 domain: traefik.example.com
rbac:
 enabled: true

And, finally use helm to install traefik2 with our hand-crafted values yaml file:

$ helm install traefik traefik/traefik -n kube-system -f traefik-values.yaml 
NAME: traefik
LAST DEPLOYED: Mon May 23 14:53:46 2022
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None

Check if it is created properly:

$ kubectl get pods -n kube-system
NAME                                      READY   STATUS    RESTARTS   AGE
coredns-d76bd69b-8z7dh                    1/1     Running   0          4h25m
local-path-provisioner-6c79684f77-jvdf6   1/1     Running   0          4h25m
metrics-server-7cd5fcb6b7-d6wlx           1/1     Running   0          4h25m
metallb-controller-777cbcf64f-vfz5v       1/1     Running   0          136m
metallb-speaker-r7wbg                     1/1     Running   0          136m
metallb-speaker-5lxff                     1/1     Running   0          136m
metallb-speaker-cxskn                     1/1     Running   0          136m
metallb-speaker-24vgg                     1/1     Running   0          136m
metallb-speaker-wmzkg                     1/1     Running   0          136m
traefik-7b9cf77df9-cwp4l                  1/1     Running   0          67s

And, also very if the treafik service is present:

$ kubectl get svc -n kube-system | grep traefik
traefik                   LoadBalancer   10.43.78.204    192.168.0.230   80:31164/TCP,443:31610/TCP   13m

We can also check the logs of traefik:

$ kubectl -n kube-system logs $(kubectl -n kube-system get pods --selector "app.kubernetes.io/name=traefik" --output=name)
time="2023-01-24T13:23:31Z" level=info msg="Configuration loaded from flags."

When we see above listed line the we are sure traefik is properly installed and configured. Now, we are ready to do some more tests with our new load-balancer and traefik.

References

[1] Metallb

[2] Setting up your own k3s home cluster

[3] Configuring Traefik 2 Ingress for Kubernetes

[4] NGINX Ingress Controller

Edit History

  • update with new metallb and NGINC Ingress Controller - 25/Jan/2023