Uncategorized

Kubernetes und helm charts

Autor: Dr. Götz Güttich, Senior Security Consultant bei Twinsoft

In diesem Beitrag befassen wir uns mit der Funktionsweise von „helm“ und den dazugeörigen „charts“. helm ist eine Paketverwaltung für Kubernetes und übernimmt im Betrieb ähnliche Aufgaben wie apt und dnf unter Linux oder pip für Python. Die charts sind die dazugehörigen Pakete. helm reduziert die Komplexität von Kubernetes-Umgebungen, ermöglicht standardisierte Deployments und vereinfacht Updates beziehungsweise Rollbacks.

Die Live-Logs im Kubernetes-Dashboard – Screenshot: Dr. Götz Güttich

Da wird über keinen Test-Kubernetes-Cluster verfügen, richten wir uns zunächst eine Testumgebung mit Hilfe von Minikube ein. Minikube ermöglicht es, einen lokalen Kubernetes-Cluster auf dem eigenen Endpoint zu betreiben. Das Tool eignet sich hervorragend für Schulungen, Tests und Entwicklungsarbeiten.

Nach der Installation von Minikube werden wir zunächst einmal einen nginx-Webserver mit helm in Kubernetes als Pod zum Laufen bringen, Ingress und DNS so konfigurieren, dass man auf diesen Webserver zugreifen kann, die Überwachungsfunktionen des Systems nutzen, um den Ressourcenverbrauch zu analysieren und eine WordPress-Installation mit einer Datenbank mit Hilfe von helm vornehmen. Danach skalieren wir die Zahl der Pods automatisch nach Last mit Hilfe des Kubernetes Autoscalers und erzeugen ein eigenes helm chart.

Die Übersicht über die Pods und die CPU- und Speicherauslastung im Kubernetes-Dashboard – Screenshot: Dr. Götz Güttich

Noch kurz zum Verständnis: Pods sind logische Hosts, auf ihnen laufen einer oder mehrere Container, sie haben eine externe IP-Adresse, sie sind über Namen und Namespace identifizierbar und sie teilen Storage-Ressourcen. Die interne Kommunikation findet via IPC (Inter Process Communication) statt. Pods sind kurzlebig und werden über Controller gehandhabt.

Minikube installieren

Als Testsystem kommt ein Ubuntu 26.04 zum Einsatz. Dieses sollte mindestens 2 CPUs und 4 GByte RAM mitbringen. Eine der Grundvoraussetzungen für den Betrieb von Minikube ist eine vorhandene Docker -Umgebung zum Betreiben von Kubernetes. Deswegen bringen wir erstmal das installierte Linux auf den aktuellen Stand und installieren danach curl, Minikube und Docker.

sudo apt install curl

curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64

sudo install minikube-linux-amd64 /usr/local/bin/minikube

sudo snap install kubectl –-classic

sudo apt install docker.io

sudo systemctl enable –now docker

sudo apt install docker-compose

sudo usermod -aG docker $USER && newgrp docker

Jetzt können wir Minikube starten:

minikube start –-driver=docker

Nachdem Minikubes Startvorgang abgeschlossen wurde, lässt sich der Status des Clusters abfragen:

minikube status

Die Ausgabe sollte so aussehen:

minikube

type: Control Plane

host: Running

kubelet: Running

apiserver: Running

kubeconfig: Configured

Mit dem Befehl:

kubectl get nodes

bringen wir in Erfahrung, dass unser Node „Ready“ ist. Jetzt kann es daran gehen, helm zu installieren, ein Repo hinzuzufügen und letzteres auf den aktuellen Stand zu bringen:

sudo snap install helm –classic

helm repo add bitnami https://charts.bitnami.com/bitnami

helm repo update

Jetzt kann es daran gehen, ein chart im Minikube-Cluster zu installieren. Für diesen Test nehmen wir „nginx“:

helm install my-release bitnami/nginx

Der Status der Pods lässt sich immer mit

kubectl get pods

überprüfen. Hier sollte jetzt etwas wie das Folgende erscheinen:

NAME                                                                 READY             STATUS    RESTARTS      AGE

my-release-594c4c58bf-vdhnm                   1/1                      Running   1 (13d ago)   13d

Weitere wichtige Befehle:

helm list

zeigt alle installierten Apps und

helm search repo nginx

zeigt die in den Repositories verfügbaren Versionen der Apps.

helm status my-release

präsentiert die Dokumentation des chart-Erstellers und

helm get values my-release

zeigt die beim Installieren gesetzten Einstellungen. Entfernen lässt ich das chart über

helm uninstall my-release

Das Aktualisieren der Software

Um Minikube und Kubernetes im laufenden Betrieb zu aktualisieren sind diverse Schritte erforderlich. Zunächst einmal ist Minikube selbst auf den aktuellen Stand zu bringen:

curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-latest.x86_64.rpm

sudo install minikube-linux-amd64 /usr/local/bin/minikube

Die einfachste Option, Kubernetes zu aktualisieren ist eine Neuerstellung des Clusters:

minikube delete

minikube start –-driver=docker –-kubernetes-version=latest

kubectl und helm werden über snap aktualisiert:

sudo snap refresh helm

sudo snap refresh kubectl

Weitere wichtige Befehle für Minikube sind

minikube pause

Das friert den Cluster ein. Wieder in Betrieb nehmen lässt er sich mit

minikube unpause

Ausschalten lässt sich das System mit

minikube stop

helm in der Praxis

Nachdem wir jetzt alles installiert und auf den aktuellen Stand gebracht haben, können wir einen neuen Cluster erzeugen und mit der eigentlichen Arbeit beginnen:

minikube delete

minikube start –-driver=docker

Jetzt installieren wir erst einmal ein neues nginx-pod. Wir nennen es „my-nginx“ und setzen den Service-Typ auf „LoadBalancer“. Letzteres muss angegeben werden, damit die Webseite später über das Netz erreichbar ist:

helm install my-nginx bitnami/nginx –set service.type=LoadBalancer

Kubernetes lädt jetzt das Image und startet den Container. Der aktuelle Status lässt sich wieder mit

kubectl get pods

überprüfen. Sobald unter “STATUS” Running steht, ist nginx bereit.

Um nun auf den Webserver zuzugreifen, kann entweder ein Tunnel oder ein weitergeleiteter Port zum Einsatz kommen. Für den Tunnel müsste der Befehl

minikube tunnel

in einem zusätzlichen Terminalfenster ausgeführt werden. Das Port-Forwarding wird über

kubectl port-forward svc/my-nginx 8080:80

aktiviert. Danach ist der Server über die URL http://localhost:8080 erreichbar.

Einer der großen Vorteile von helm ist, dass sich Anwendungen ändern lassen, ohne sie zu löschen. Wenn wir beispielsweise die Anzahl der Instanzen von nginx erhöhen möchten, um Hochverfügbarkeit zu simulieren, reicht ein

helm upgrade my-nginx bitnami/nginx –set replicaCount=3

Jetzt kann man mit

kubectl get pods

ansehen, wie das System zwei weitere Instanzen hochfährt. Löscht man Instanzen während des Betriebs durch

kubectl delete pod {Instanzname}

so startet das System gleich wieder neue, um die Zahl der definierten Instanzen wieder zu erreichen.

Den Ressourcenverbrauch analysieren

Um herauszufinden, wieviel CPU und RAM der nginx verbraucht, müssen wir das metrics-server-Addon aktivieren:

minikube addons enable metrics-server

Jetzt zeigt

kubectl top pods

den Ressourcenverbrauch an.

Ingress und DNS konfigurieren

Momentan greifen wir per localhost auf den Web-Server zu. Sinnvoller ist es, den nginx über die Domäne http://mein-nginx.local erreichbar zu machen. Dazu installieren wir einen Ingress-Controller, der als zentraler Einstiegspunkt den Traffic annimmt, konfigurieren eine Ingress-Ressource und passen die hosts-Datei so an, dass der Browser die Domäne findet. Letzteres ließe sich auch über eine DNS-Konfiguration realisieren, da das Anpassen von DNS-Servern aber nicht der Fokus dieses Playbooks ist, wählen wir die hosts-Datei. Los geht es mit

minikube addons enable ingress

Jetzt prüfen wir, ob der Controller-Pod hochgefahren ist:

kubectl get pods -n ingress-nginx

Sobald der Controller-Pod auf „running“ steht, können wir die Ingress-Ressource erstellen. Dazu verwenden wir eine yaml-Datei, in der steht „Alles was mit dem Hostnamen ‚mein-nginx.local‘ ankommt, soll auf Port 80 des Service ‚my-nginx‘ weitergeleitet werden. Wir nennen die Datei „ingress.yaml“ und sie sieht folgendermaßen aus:

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

  name: nginx-ingress

  annotations:

    nginx.ingress.kubernetes.io/rewrite-target: /

spec:

  rules:

  – host: mein-nginx.local

    http:

      paths:

      – path: /

        pathType: Prefix

        backend:

          service:

            name: my-nginx

            port:

              number: 80

Anwenden lässt sich die Datei nun mit

kubectl apply -f ingress.yaml

Jetzt finden wir die IP-Adresse des Minikube-Knotens heraus:

minikube ip

Diese wird nun in die /etc/hosts-Datei eingetragen:

{IP-Adresse}    mein-nginx.local

Jetzt lässt sich der Webserver über die URL http://mein-nginx.local im Browser öffnen.

Wenn wir einen zweiten Dienst über die gleiche IP-Adresse anbieten wollen, müssen wir nun lediglich diesen zweiten Dienst (eine andere Webseite oder ein anderer Server wie Apache) in Kubernetes starten, in der ingress.yaml eine zweite Regel hinzufügen, beispielsweise für die URL „meine-app.local“ und die URL der zweiten App mit der gleichen Minikube-IP-Adresse in die hosts-Datei eintragen.

Dashboard und Monitoring

Um die Monitoring-Funktionen zu aktivieren, müssen (falls nicht schon geschehen) folgende addons aktiviert werden:

minikube addons enable dashboard

minikube addons enable metrics-server

Dann öffnet Minikube eine Kubernetes-Dashboard-Seite, die die vorhandenen Workloads/Pods, Services und ConfigMaps anzeigt. Klickt der Anwender auf einen Pod-Eintrag, lassen sich die Live-Logs über das kleine Linien-Symbol oben anzeigen.

Unter Services sollten jetzt der nginx-Dienst und der Ingress-Controller erscheinen. Die ConfigMaps ermöglichen es, die Konfigurationsdaten von Container-Images zu trennen, also beispielsweise unter nginx eine andere Index.html anzuzeigen als die Default-Webseite. Während ConfigMaps unkritische Daten enthalten, existieren für sensible Daten die so genannten Secrets, die ähnlich funktionieren aber auf sichere Art und Weise Passwörter, Tokens und Keys bereitstellen können.

Das Dashboard zeigt uns an, dass es praktisch keine Last auf dem nginx-Server gibt. Jetzt werden wir Last erzeugen, um das Echtzeit-Monitoring unter die Lupe zu nehmen. Dazu nutzen wir den Befehl „ab“ (Apache Benchmark). Dieser lässt sich folgendermaßen installieren:

sudo apt install apache2-utils

Nun können wir 50.000 Anfragen mit 100 gleichzeitigen Verbindungen schicken:

ab -n 50000 -c 100 http://mein-nginx.local/

Jetzt steigt die CPU-Auslastung des nginx-Pods sprunghaft an, was im Dashboard klar zu sehen ist.

Installation von WordPress mit Datenbank als zweite Instanz parallel zu nginx

Zunächst installieren wir WordPress mit MariaDB und einem eigenen Namen sowie einer Ingress-Anbindung. Außerdem setzen wir ein Passwort, damit wir uns einloggen können:

helm install mein-blog bitnami/wordpress \

  –set wordpressUsername=admin \

  –set wordpressPassword=geheim \

  –set mariadb.auth.rootPassword=geheim-db \

  –set service.type=ClusterIP \

  –set ingress.enabled=true \

  –set ingress.hostname=mein-blog.local \

  –set ingress.annotations.“kubernetes\.io/ingress\.class“=nginx

Wenn wir jetzt mit

kubectl get pods

nachsehen, so beobachten wir, wie das WordPress-Pod und das MariaDB-Pod erstellt werden. Dazu kommen zwei Persistent Volumes, eines für die Datenbank und eines für WordPress. Letztere verhindern, dass Blog-Posts verloren gehen, wenn die Maschine neu startet. Im nächsten Schritt tragen wie die URL „mein-blog.local“ mit der Minikube-IP in die hosts-Datei ein. Dazu schreiben wir sie einfach in die gleiche Zeile hinter die nginx-Domain.

Im Dashboard kann man nun unter „Pods“ die Interaktionen zwischen WordPress und MariaDB sehen und unter Persistent Volume Claims (PVC) findet sich der reservierte Speicherbereich. Das Blog selbst lässt sich über http://mein-blog.local aufrufen und nutzen. Wird ein Blogpost erstellt und das Pod gelöscht, so startet Kubernetes sofort ein neues Pod und die Daten des Blogposts gehen nicht verloren. Auf diese Weise lassen sich mehrere Anwendungen sauber getrennt und hochverfügbar auf einer Hardware ausführen.

Das Autoscaling

Wenden wir uns jetzt dem Autoscaling zu. Wir möchten dabei erreichen, dass standardmäßig in unserem Cluster 2 nginx-Pods laufen. Steigt die Last, so sollen weitere Pods (bis maximal 5) hinzukommen. Sinkt die Last wieder, so sollen die zusätzlichen Pods automatisch abgebaut werden. Dazu verwenden wir den Horizontal Pod Autoscaler (HPA) von Kubernetes. Diese behält die CPU- und Speicherlast im Blick und schaltet dynamisch Instanzen dazu oder weg.

Damit Kubernetes erkennen kann, wann ein Pod ausgelastet ist, müssen wir festlegen, wieviel CPU es verbrauchen darf. Deswegen aktualisieren wir die nginx-Instanz mit helm und geben ihr neue Limits mit. Gleichzeitig setzen wir die Zahl der standardmäßig aktiven Instanzen auf 2:

helm upgrade my-nginx bitnami/nginx \

  –reuse-values \

  –set replicaCount=2 \

  –set resources.requests.cpu=100m \

  –set resources.limits.cpu=200m

100m steht hier für 100 Millicores, also 10 % eines CPU-Kerns. Jetzt folgt die Regel, die besagt „Wenn die Pods im Schnitt mehr als 50 % ihrer angeforderten CPU verbrauchen, skaliere hoch bis auf maximal 5“:

kubectl autoscale deployment my-nginx –cpu-percent=50 –min=2 –max=5

Mit

kubectl get hpa

lässt sich der Status überwachen. Es dauert etwa 1 bis 2 Minuten, bis der Server die Daten zuverlässig liefert. Jetzt starten wir den Lasttest in einem zweiten Terminal:

ab -n 500000 -c 100 http://mein-nginx.local/

Jetzt können wir im ersten Terminal mit Hilfe von

kubectl get hpa

sehen, dass der Prozentwert über 50 % steigt (unter TARGETS). Kurz darauf zeigt

kubectl get pods

an, dass Kubernetes neue Pods erstellt. Gleichzeitig kann man im Dashboard sehen, wie die Kurve nach oben geht. Nach dem Stoppen des ab-Befehls fällt die Last und die zusätzlichen Pods werden 5 Minuten später wieder gelöscht. Diese Wartezeit verhindert ein ständiges Hoch- und Runterfahren bei kurzen Lastspitzen.

Sichern und Wiederherstellen von Pod- und Deployment-Konfigurationen

Hat man ein Pod zur Zufriedenheit konfiguriert und alles läuft wie gewünscht, besteht übrigens die Möglichkeit, die Konfiguration des laufenden Pods mit dem Befehl

kubectl get pod {Name des Pods} -o yaml > datei.yaml

in einer yaml-Datei zu sichern. Wird das Pod gelöscht, so lässt es sich später problemlos mit dem Befehl

kubectl apply -f datei.yaml

wiederherstellen. Soll das ganze Deployment, das die Pods steuert, gesichert werden, so lässt sich das mit dem Befehl

kubectl get deployment {Name} -o yaml > datei.yaml

realisieren.

Der Status der Workloads im Kubernetes-Dashboard – Screenshot: Dr. Götz Güttich

Eigene helm charts erstellen

Zum Schluss wollen wir noch demonstrieren, wie sich eigene helm charts erzeugen lassen. Dazu installieren wir den Klassiker „Nethack“ in unserem Cluster, für den in den Repositories kein chart vorhanden ist.

Als Basis verwenden wir das Docker-Image „jwodder/nethack“. Der Befehl

helm create nethack-chart

erstellt eine Grundstruktur für das neue chart. Im nächsten Schritt öffnen wir die Datei „nethack-chart/values.yaml“ und passen das Image an. Der Bereich „image“ der yaml-Datei muss folgendermaßen aussehen:

image:

  repository: jwodder/nethack

  pullPolicy: IfNotPresent

  tag: „latest“

Jetzt müssen wir dafür sorgen, dass Kubernetes weiß, dass das Pod einen interaktiven Terminal-Stream öffnen muss, da sonst niemand mit dem Spiel interagieren kann. Das geht über „tty: true“ und „stdin: true“. Wir öffnen die Datei „nethack-chart/templates/deployment.yaml“ und fügen unter dem Abschnitt „containers:“ auf der gleichen Ebene wie „image:“ hinzu:

containers:

        – name: {{ .Chart.Name }}

          image: „{{ .Values.image.repository }}:{{ .Values.image.tag }}“

          stdin: true  # Entspricht -i

          tty: true    # Entspricht -t

Außerdem ergibt es auch Sinn, die Abschnitte „livenessProbe“ und „readinessProbe“ in der deployment.yaml auszukommentieren, sonst startet Kubernetes das Pod ständig neu, da es nicht auf HTTP-Anfragen reagiert (Nethack ist ja kein Webserver). Jetzt lässt sich das chart installieren:

helm install my-nethack ./nethack-chart

Um sich mit dem Spiel zu verbinden, müssen wir erst den Namen des Nethack-Pods herausfinden. Das geht mit

kubectl get pods

Danach wird die Verbindung mit

kubectl attach -it {Name des Pods}

hergestellt.

Datenschutz-Übersicht

Diese Website verwendet Cookies, damit wir Ihnen die bestmögliche Benutzererfahrung bieten können. Cookie-Informationen werden in Ihrem Browser gespeichert und führen Funktionen aus, wie das Wiedererkennen von Ihnen, wenn Sie auf unsere Website zurückkehren, und hilft unserem Team zu verstehen, welche Abschnitte der Website für Sie am interessantesten und nützlichsten sind.