Skip to content

Ahoi – Kubernetes Cluster im Homelab aufbauen (2/2)

Im ersten Teil des Beitrags haben wir es geschafft, einen Kubernetes Cluster im Homelab aufzubauen. Im zweiten Teil bestücken wir den Cluster mit nützlichen Komponenten wie einem “Cloud” Loadbalancer und einem Ingress Controller. Auch werden wir weitere Storage Klassen in unserem Cluster definieren.

Metallb – Loadbalancer

Wer seinen Kubernetes Cluster bei einem der gängigen Cloud Anbieter betreibt, hat die Möglichkeit öffentliche IP Adressen von deren Cloud Loadbalancer zu beziehen. In unserem Homelab haben wir eine solche Komponente in aller Regel natürlich nicht, wollen aber dennoch eine Komponente in unserem Cluster bereitstellen, die einem “Loadbalancer Service” in unserem Cluster, eine IP Adresse aus dem IP Bereich zuweist, in dem unser Homelab erreichbar ist.

Hier kommt Metallb ins Spiel. Es handelt sich um einen Cloud Loadbalancer Ersatz der einen definierten IP Adressbereich verwaltet (dieser sollte natürlich außerhalb eines eventuell vorhandenen DHCP Bereichs liegen). Fragt nur ein Loadbalancer Service unseres Clusters nach einer IP Adresse, bekommt er von Metallb eine zugewiesen und ist somit in unserem Heimnetz erreichbar. Eine detailierte Installationsanleitung von Metallb findet sich hier.

Im ersten Schritt aktivieren wir den “Strict ARP” Mode in unserem Kube-Proxy. Da verwendet man folgendes Kommando.

kubectl edit configmap -n kube-system kube-proxy

Nun passen wir den Teil unter ipvs an und setzen dort den Parameter strictARP: true. Das sollte dann so aussehen.

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "ipvs"
ipvs:
  strictARP: true

Um Metallb zu installieren, erstellen wir uns einen Namespace mit dem Namen metallb-system.

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.11.0/manifests/namespace.yaml

Nun bereiten wir uns eine ConfigMap vor, in der wir den gewünschten Adressbereich angeben. In diesem Beispiel nenne ich sie metallb-config.yaml.

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.2.240-192.168.2.250

Das Manifest wenden wir nun gegen unseren Cluster an. Direkt im Anschluss installieren wir Metallb selbst.

kubectl apply -f metallb-config.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.11.0/manifests/metallb.yaml

Damit ist Metallb in unserem Cluster bereitgestellt. Einen Loadbalancer Service, den wir nun in unserem Cluster erstellen, muss mit der Annotation metallb.universe.tf/address-pool: default deployed werden und erhält somit die nächste freie IP Adresse aus dem Adressbereich zugewiesen.

Persistent Storage

In diesem Kapitel schauen wir uns an, wie wir eigene Storage Klassen in unserem Cluster definieren. Dazu installieren wir zwei Storage Klassen. Einmal den local-path Storage Provider von Rancher und einmal einen NFS-Subdir Storage Provider.

local-path Storage Provider

Dabei handelt es sich um einen Storage Provider, der auf unseren Kubernetes Nodes in einem lokalen Verzeichnis ein Persistent Volume bereitstellt. Das schöne dabei ist, dass dies anpassbar ist. So können wir z.B. auf jedem Node einen Mount Punkt bereitstellen, in den unsere persistenten Daten abgelegt werden sollen. Der Nachteil von local-path ist, dass die persistenten Daten auf jeweils nur dem Node liegen, auf dem der jeweilige Pod läuft. Sollte ein Node ausfallen, können wir z.B. unseren Datenbank Pod nicht auf einem verbleibenden Node starten, da die Daten dort nicht vorhanden sind.

Die Installation von local-path als Storage Provider können wir folgendermaßen durchführen. Eine detailierte Installationsanleitung findet ihr hier. Dort findet ihr auch ein Beispiel, wie ihr ein Persistent Volume erzeugt und Daten darin ablegt.

kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml

Damit ist der Storage Provider installiert. Standardmäßig werden die persistenten Daten auf dem jeweiligen Node unter dem Verzeichnis /opt/local-path-provisioner angelegt. Solltet ihr das ändern wollen, reicht es aus die ConfigMap local-path-config zu editieren. Hier könnt ihr auch den Mountpunkt je Node anpassen, sollte das euer Wunsch sein.

kubectl edit configmaps -n local-path-storage local-path-config

Hier könnt ihr den data Teil der ConfigMap anpassen um z.B. wie ich, die Daten unter /kubernetes auf den Nodes zu speichern.

data:
  config.json: |-
    {
            "nodePathMap":[
            {
                    "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
                    "paths":["/kubernetes"]
            }
            ]
...

NFS-Subdir Storage Provider

NFS-Subdir ist der zweite Storage Provider, den ich in meinen Cluster installiert habe. Wie der Name sagt, handelt es sich dabei um einen Storage Provider, der NFS Shares in unseren Cluster einbindet. Der Vorteil dabei ist, dass ein Volume im Gegensatz zum local-path Provider auf mehreren Nodes (auch gleichzeitig) eingehängt werden kann. Somit sind die Daten im Falle eines Ausfalls eines Worker Nodes auch weiter vorhanden und unsere Pods können auf einem verbleibenden Worker Node gestartet werden. Eine detailiert Installationsanleitung findet ihr hier.

Es versteht sich, dass ihr für die Verwendung von NFS-Subdir, einen NFS Server benötigt, der einen NFS Share bereitstellt, der über das Netzwerk von eurem Kubernetes Cluster erreichbar ist.

Am einfachsten ist die Installation über Helm. Hier benötigen wir lediglich einen Aufruf um NFS-Subdir zu installieren und geben im Installationsaufruf gleichzeitig unsere NFS Konfiguration mit an. Mit --set nfs.server und --set nfs.path gebt ihr die IP Adresse eures NFS Servers und den Pfad des NFS Exports an.

helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner     --set nfs.server=192.168.2.210     --set nfs.path=/volume2/Kubernetes

Solltet ihr Helm nicht im Einsatz haben, könnt ihr die Installation auf ohne Helm durchführen. Dazu ladet ihr euch die Manifeste von diesem Repository Pfad herunter und speichert diese in einem Verzeichnis mit dem Namen deploy. Ihr könnt natürlich auch das ganze Repository klonen. Wir passen nun den Namespace an und deployen die Rollen usw.

kubectl create ns nfs-subdir
NAMESPACE=nfs-subdir
sed -i'' "s/namespace:.*/namespace: $NAMESPACE/g" ./deploy/rbac.yaml ./deploy/deployment.yaml
kubectl create -f deploy/rbac.yaml

Nun passen wir das deployment.yaml Manifest an und tragen dort unsere NFS Server Konfiguration ein.

...
          env:
            - name: PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              value: 192.168.2.210
            - name: NFS_PATH
              value: /volume2/Kubernetes
...

Nun wenden wir noch das Deployment und die Storage Klasse an.

kubectl apply -f deploy/deployment.yaml
kubectl apply -f deploy/class.yaml

Jetzt können wir die Storage Klasse noch testen. Dazu existieren ebenfalls im deploy Verzeichnis zwei Manifest Dateien, die wir für einen Test nutzen können.

kubectl create -f deploy/test-claim.yaml -f deploy/test-pod.yaml

Hat alles geklappt, solltet ihr auf eurem NFS Server ein Verzeichnis mit dem PVC Namen sehen und eine Datei darin finden, mit dem Namen SUCCESS.

Ingress Controller – nginx

Kommen wir zum Thema Ingress Controller. Ich sage es vorweg, ich habe gefühlt eine Ewigkeit benötigt um das Konzept dahinter zu verstehen. Ich hoffe, dass es nur mir so geht. Mit ein Grund dafür war, dass der Begriff Loadbalancer in Kubernetes mehrfach für unterschiedliche Dinge verwendet wird. So wird bei einem Ingress Controller auch oft von einem Ingress Loadbalancer oder nur Loadbalancer gesprochen. Da mich das völlig verwirrt, versuche ich den Begriff zu vermeiden und stattdessen Ingress Controller zu verwenden.

Ein Ingress Controller kann zwar auch Loadbalancing, ist aber auch eine Reverse Proxy Komponente. Das Ziel soll sein, dass (meist HTTP) Traffic, der über eine IP Adresse unter verwendung von unterschiedlichen DNS Namen auf unseren Cluster gelangt, vom Ingress Controller dem richtigen Service oder Pod zugewiesen wird. Noch ein Wort zu der Traffic Art. Wie oben erwähnt, sind alle Ingress Controller (zumindest soweit mir bekannt) in der Lage mit HTTP Verkehr umzugehen. Das ist bei TCP Traffic leider nicht so. Nginx kann dies zwar, ich habe es aber selbst noch nicht ausprobiert. Mehr dazu könnt ihr hier nachlesen.

In meinem Homelab habe ich mich für nginx als Ingress Controller entschieden. Es gibt dutzende Alternativen wie z.B. Traefik, Contour, Istio usw. Nginx ist sehr einfach zu installieren und für meine Zwecke in aller Regel ausreichend.

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.0/deploy/static/provider/cloud/deploy.yaml

Das sollte dazu führen, dass einige Pods im ingress-nginx Namespace erstellt werden. Nach wenigen Minuten solltet ihr einen Pod mit dem Namen ingress-nginx-controller sehen. Auch solltet ihr einen Service vom Typ Loadbalancer mit dem Namen ingress-nginx-controller sehen. Wenn ihr metallb korrekt konfiguriert habt, hat dieser Service eine IP Adresse aus eurem IP Bereich zugewiesen bekommen.

kubectl get svc -n ingress-nginx

NAME                                 TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.100.34.151   192.168.2.241   80:30492/TCP,443:31184/TCP   15d

In meinem Fall hat der Controller Loadbalancer Service die IP Adresse 192.168.2.241 bekommen.

Jetzt wollen wir uns rasch einen nginx Webserver erstellen, den wir dann über einen DNS Namen im Browser ansprechen wollen. Dazu müsst ihr auf dem DNS Server der euer Homelab verwaltet den gewünschten DNS Namen (z.B. nginx.myhome.lab) auf die oben angegebene IP Adresse verweisen. Nun können wir mit einem simplen Befehl den Webserver starten und eine Ingress Rule Ressource erzeugen, die den Ingress Controller dazu veranlasst, jeden Traffic auf Port 80 über den DNS Namen nginx.myhome.lab an den demo Pod weiterzugeben.

kubectl create deployment demo --image=httpd --port=80
kubectl expose deployment demo

kubectl create ingress demo --class=nginx \
  --rule=nginx.myhome.lab/*=demo:80

Diese drei Befehle haben drei Ressourcen erstellt. Einen Pod namens demo, in diesem läuft der Webserver als Container. Der Pod kann über Port 80 erreicht werden. Einen Service vom Typ ClusterIP der ebenfalls den Port 80 an den demo Pod als Endpoint weitergibt und einen Ingress oder besser Ingress Rule der die Beziehung zu der externen IP Adresse bzw. dem DNS Namen nginx.myhome.lab und dem demo ClusterIP Service herstellt. Haben wir alles richtig gemacht, können wir http://nginx.myhome.lab bei uns im Browser aufrufen und bekommen folgendes zu sehen.

Damit sind wir am Ende. Ich hoffe, der Beitrag hat euch geholfen euren Kubernetes Cluster bei euch im Homelab zu erstellen.

Philip

Leave a Reply

Your email address will not be published. Required fields are marked *