Kubernetes bietet mit “Helm” einen eigenen Package Manager, mit dem Anwendungen bequem installiert und aktualisiert werden können. Das Administrations-Werkzeug “Rancher” für Kubernetes Cluster bietet eine gute, elegante Einbindung von Helm-Repositories. Davon profitiert letztlich auch die Enterprise Container Plattform “Oracle Verrazzano”, die eine vollwertige aktuelle Version des Rancher-Tools enthält. Wir möchten heute an jeweils einem Beispiel zeigen, wie die Integration von Helm in Rancher aussieht und wie man ein eigenes einfaches Helm Chart erstellt um damit ein Repository zu befüllen.
 

Teil 1: Helm Chart Repositories in Rancher einbinden und benutzen

Rancher und Helm DuoMöchte man auf komfortable Weise komplexe Software bereitstellen und aktuell halten empfiehlt es sich, diese über einen Package Manager zu verwalten. In Kubernetes Umgebungen steht dafür das Werkzeug Helm auf Clients zur Verfügung, um Software zu installieren, Repositories einzubinden aber auch um eigene Pakete zu schnüren. Wurde in ein bestehendes Kubernetes Cluster hinein bereits Oracle Verrazzano installiert oder zumindest Rancher, so steht die Paketverwaltung bereits zur Verfügung und muß nicht nachinstalliert werden.
Ein besonders umfassendes Repository aus nützlichen OpenSource Werkzeugen wie Kafka, Jupyter Hub, MongoDB und sehr vielen weiteren bietet Bitnami. Diese Organisation erstellt und pflegt Installations-Skripte, Terraform Stacks usw. für Container und VMs und stellt sie auf gängigen Cloud Umgebungen sowie für Kubernetes Cluster zur Verfügung. Bitnami tritt somit als gängiger Software-Provider für Open Source Software im jeweiligen Cloud-Marketplace auf. Auch im lokalen Kubernetes Cluster kann ein solcher Marketplace aufgebaut werden: Rancher hilft bei der Verwaltung der Software Provider und ihrer bereitgestellten Helm Repositories. Nur wenige Mausklicks und die gewünschte Software ist ausgewählt und schnell per Helm Chart im Cluster eingerichtet.

Um Bitnami als Provider beispielhaft einzubinden, möchten wir nun die komfortable Variante über ein von Rancher bereitgestelltes Browser UI vorstellen und nicht auf die Kommandozeilen-Aufrufe von Helm (wie “helm repo add …” in Kurzform) eingehen. Zunächst melden Sie sich in Ihrer Rancher-Umgebung an z.B. mit dem Benutzer “admin”. Bei einer Verrazzano-Installation erhalten Sie das Kennwort aus dem Kubernetes Secret “rancher-admin-secret” im Namespace “cattle-system”, z.B.:

kubectl get secret rancher-admin-secret -n cattle-system -o jsonpath={.data.password}| base64 -d - ; echo
 

Rancher Login

 

Wählen Sie Ihren Lieblings-Cluster aus, z.B. “local”:

Cluster Auswahl

 

Wechseln Sie nun in den Bereich “Apps & Marketplace”, “Repositories”:

Apps and Marketplace

 

Klicken Sie rechts oben auf den blauen “Create” Button und tragen Sie ein neues Repository ein. Am besten, Sie wählen als Namen “bitnami”: Dieses ist öffentlich ohne Authentisierung über diese URL: https://charts.bitnami.com/bitnami verfügbar.

Repository angeben


Nach wenigen Sekunden sollte das Repository als “Active” gekennzeichnet sein, d.h. die Repository Informationen wurden heruntergeladen und stehen zur Verfügung. Das einzige Problem, das auftreten kann, ist wenn das Repository nur über einen Proxy erreichbar ist. Der Status verbleibt dann bei “loading”. Die nötigen Umgebungsvariablen HTTPS_PROXY, HTTP_PROXY und NO_PROXY können Sie dem Rancher-Container auf den Weg über dessen Deployment YAML geben oder Sie laden die Repository-Daten separat herunter und stellen sie auf einem eigenen, besser zugänglichen Web Server über eine andere URL zur Verfügung.

So oder so –  klicken Sie nun auf “Charts”, wählen Sie “bitnami” aus und erfreuen Sie sich an den ca 100 Programmen aller Kategorien, die Ihnen ab jetzt zur Verfügung stehen:

 

Verfügbare Charts bei bitnami


Gerne können Sie eines der Charts probehalber installieren, um die Funktionweise von Helm zu testen. Es ist dabei egal, für welches Chart Sie sich interessieren. Nach dem Klick auf die gewünschte Kachel werden Sie gefragt werden, in welchen Namespace Sie hinein installieren möchten, wie die Anwendung in Ihrer Umgebung heißen soll und es wird Ihnen eine mehr oder weniger große Liste an Parametern präsentiert, mit denen Sie die Installation nach Ihren Bedürfnissen anpassen können. Meist genügen jedoch die Standard-Werte und die Installation teilt Ihnen nachträglich mit, wie Sie sich beispielsweise an die jeweilige Software anmelden können.

Hier die beispielhafte Installation von Jupyterhub, einer Software für die Steuerung von Abfrage-Notebooks für Data Scientists und Analysten:

Jupyterhub

Nach Klick auf den “Install” Button:

Jupyterhub namespace und name

Nach Klick auf “Next” folgen die Parameter. Einfach alle Defaults belassen und “Install” anklicken:

Parameter Screen

 

Die Installation startet und lädt benötigte Container herunter, richtet die Software komplett ein. Ein kleiner Info-Text beschreibt am Ende der Installation, wie Sie sich mit dem UI verbinden können und wie Sie das Kennwort für den “admin” Benutzer erhalten. Sie können nun auf das UI wechseln und ihren ersten Jupyter Server starten, der sich Ihnen dann knapp präsentiert:

Jupyter UI nach Server start

Der Blick per Rancher auf den ausgewählten Namespace zeigt, daß nicht nur ein einfacher Container installiert wurde sondern eine durchaus komplexe Landschaft an Containern und anderen Komponenten:

Komplexes Deployment

 

Nachdem dieses geschafft ist, begeben wir uns nun “auf zu neuen Ufern”: Wie erstellt man ein eigenes Helm Chart an einem einfachen Beispiel, und wie kann man ein Chart Repository erzeugen, um es in Rancher einzubinden?
 

Teil 2: Erstellung eines Helm Charts, Aufbau eines Chart Repository und Veröffentlichung auf github.org

Das Kommandozeilen-Werkzeug Helm kann nicht nur Softwarepakete installieren, aktualisieren und deinstallieren. Damit lassen sich auch neue Softwarepakete erstellen und zu Repositories zusammenfassen, entweder in Dateiform für lokale Tests oder auch für die Bereitstellung über generische Webserver und github.org mit dort integrierter Versionierung der Deployment-Informationen.

Bei der Installation eines Softwarepakets führt Helm prinzipiell die nötigen “kubectl apply” Kommandos aus, um Kubernetes YAML Dateien in das Cluster hochzuladen. Die zu verwaltenden YAML Dateien sind herkömmliche Deskriptoren von Kubernetes Ressourcen wie ConfigMaps, Pods, Secrets usw., was auch die Strukturen des Open Application Model in Oracle Verrazzano selbstverständlich umfaßt. In diese YAML Strukturen sind normalerweise einige Variablen in Helm-eigener Syntax eingebettet, die während der Installation durch benutzerdefinierbare Werte ersetzt werden. Damit läßt sich eine Software-Installation an örtliche Gegebenheiten anpassen, z.B. Name der Applikation, Benutzernamen und Kennwörter, Datenbank-Connects und Storage-Parameter.
Die freie Definition dieser Variablen samt Angabe ihrer Default-Werte erfolgt in einer separaten YAML Datei. Doch schauen wir uns die von Helm benötigten Strukturen und Dateien an einem praktischen Beispiel an.

Zunächst benötigen wir einen Kommandozeilen-Zugang zu einem Kubernetes Client, d.h. einem Rechner mit vorinstalliertem “kubectl” und eingerichtetem Zugang zu einem Kubernetes Cluster Ihrer Wahl. Dazu sollte eine Konfigurationsdatei “.kube/config” in Ihrem Benutzerverzeichnis existieren mit den nötigen IP Adressen und Zertifikaten zum Anmelden am Cluster. Liefert ein Test-Aufruf mit “kubectl get nodes” ein sinnvolles Ergebnis, ist der Client für unser Vorhaben korrekt eingerichtet.

Laden Sie nun das Helm Kommandozeilen-Werkzeug herunter. Es ist frei verfügbar für verschiedene Plattformen wie MacOS, Linux, Windows und z.B. in der Version 3.9.0 von folgender URL beziehbar: Release Helm v3.9.0 · helm/helm (github.com)
Entpacken Sie die .zip Datei und kopieren Sie die Helm Binärdatei in ein Verzeichnis, das sich in Ihrem PATH befindet, z.b. /usr/local/bin.
Neben github bieten auch zahlreiche anerkannte Paketmanager eine Installation von Helm an, z.B. homebrew für MacOS, chocolatey für Windows, apt für Debian Linux usw.

Ist in Ihrem Kubernetes Cluster bereits Rancher bzw. Oracle Verrazzano installiert, können Sie testen, ob Sie mit dem eben heruntergeladenen Werkzeug Helm einige der Softwarepakete sehen können, die bereits in Ihrem Kubernetes Cluster eingerichtet wurden, denn Rancher selbst wurde bereits mittels Helm-Technologie provisioniert. Setzen Sie die Umgebungsvariable HELM_NAMESPACE auf den Wert “cattle-system” oder “fleet-system”, müßten Sie einige der Pakete auflisten können. Zum Beispiel:

$ export HELM_NAMESPACE=cattle-system
$ helm ls
NAME            NAMESPACE       REVISION   UPDATED                                 STATUS     CHART                           APP VERSION
rancher         cattle-system   1          2022-04-05 14:24:22.590318487 +0000 UTC deployed   rancher-2.5.9                   v2.5.9
rancher-webhook cattle-system   1          2022-04-05 14:29:35.223517669 +0000 UTC deployed   rancher-webhook-0.1.200+up0.1.2 0.1.2

Wir benötigen den Kubernetes Zugang nur, um unsere selbst erstellten Softwarepakete zu testen. Für das Anlegen eigener Softwarepakete genügt ein lokales Arbeitsverzeichnis.
Bitte legen Sie nun ein neues Verzeichnis an, wechseln Sie dort hinein und lassen Sie mit Helm eine neue Projekt-Struktur anlegen:

mkdir helm_test
cd helm_test
helm create nginx

Der Name des Projektes “nginx” ist gewollt, da der Aufruf von “helm create” immer eine Beispiel-Struktur anlegt um die Software “nginx” einzurichten, ein Software-Loadbalancer und einfacher Web Server. Dabei kann Helm alle seine Features präsentieren wie Einbettung von Variablen und ganzer Unterstrukturen in einige standardmäßige Kubernetes YAMLs. Schauen wir uns die Projekt-Struktur etwas näher an:

$ find nginx
nginx
nginx/Chart.yaml
nginx/values.yaml
nginx/.helmignore
nginx/templates
nginx/templates/ingress.yaml
nginx/templates/deployment.yaml
nginx/templates/service.yaml
nginx/templates/serviceaccount.yaml
nginx/templates/hpa.yaml
nginx/templates/NOTES.txt
nginx/templates/_helpers.tpl
nginx/templates/tests
nginx/templates/tests/test-connection.yaml
nginx/charts

 

Die Dateien und Verzeichnisse kurz erklärt:
nginx/Chart.yaml – enthält Metadaten zur Applikation wie Name des Charts, Versionsnummer zwecks Updating-Möglichkeit, Beschreibung, Angaben zum Author und z.B. Link zu einem hübschen Icon wie einem Anwendungs-Logo.
nginx/values.yaml – definiert per YAML-typischer Baumstruktur diverse Variablen und deren Standard-Werte.
nginx/templates/*.yaml – enthält Kubernetes (bzw. Verrazzano) Deskriptoren mit Platzhaltern für Variablen-Inhalte, die während der Installation aus nginx/values.yaml gelesen werden oder als Parameter separat angegeben werden können. Eine bestimmte Reihenfolge, in der YAML Dateien eingespielt werden, ist nicht gegeben und auch nicht erforderlich – typisch Kubernetes.
nginx/templates/NOTES.txt – enthält Angaben als Freitext zur Applikation, zum Beispiel zum Aufruf oder Zugang, Auslesen von Kennwörtern usw.
nginx/templates/_helpers.tpl – enthält beliebige zusätzliche Definitionen von Variablen und Konstrukten in Helm-Syntax. .tpl-Dateien werden interpretiert, aber nicht nach Kubernetes hochgeladen.
nginx/templates/tests/*.yaml – optional, enthält eine Ressourcenbeschreibung, die nach erfolgter Installation prüfen kann, ob die Installation erfolgreich war und z.B. Web Container lauffähig sind.
nginx/charts – optional, enthält weitere Charts, von der die aktuelle Chart abhängig ist und die miteingespielt werden sollen.

Das Zusammenspiel von Variablen und Platzhaltern in values.yaml und templates/*.yaml wird am einfachsten an einem kurzen Auszug aus unserer Projektstruktur erklärt.
Die Datei values.yaml enthält unter anderem folgende Variablen-Definition:

service:
  type: ClusterIP
  port: 80

Es können einzelne Werte aus dieser Datei in alle templates/*.yaml Dateien übernommen werden. Beispielsweise holt sich die Datei nginx/templates/service.yaml den nötigen Zugangs-Port und Service-Type über die Variablen .Values.service.type und .Values.service.port . Die Platzhalter für diese Variablen werden in doppelte geschweifte Klammern gesetzt um Verwechslungen mit jeglicher anderer Kubernetes Syntax auszuschließen:

spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}

 

Auch das Einfügen ganzer Baum-Strukturen ist möglich, sogar mit Angabe der für YAML typischen und nötigen Einrück-Position (Indentation). Einbindung globaler Variablen wie die aus Charts.yaml (“.Chart.Name”, “.Chart.AppVersion”) oder aus der Datei _helpers.tpl generierte (“nginx.labels”, “nginx.fullName”) sind auch möglich. Abfragen von Variablen-Inhalten mit “if-then”-Konstrukten mitsamt Klammerungen werden ebenfalls unterstützt (siehe Beispiel ingress.yaml , deren Ressource nur angelegt wird wenn “.Values.ingress.enabled” auf “true” steht). Für die allermeisten Fälle dürften jedoch die einfachen Ersetzungen von Platzhaltern durch Variablen-Inhalte genügen. Viele weitere Hinweise sind in der Online-Dokumentation von Helm verfügbar: Helm | Using Helm

Die Helm Chart ist dann installationsfertig, wenn alle Dateien geprüft und in ein Archiv verpackt wurden. Dafür geben Sie folgende Helm-Kommandos ein:

$ helm lint nginx

==> Linting nginx

1 chart(s) linted, 0 chart(s) failed

$ helm package nginx

Successfully packaged chart and saved it to: /home/oracle/helm_test/nginx-0.1.0.tgz


Nun könnten Sie das Software-Paket bereits installieren lassen. Helm bietet Ihnen vorab die Möglichkeit, den Installationslauf zu testen und aufzuzeigen, welche YAML Dateien mit welchem Inhalt gefüllt wurden. Ein Helm “dry run” unseres nginx Softwarepaketes sähe wie folgt aus:

$ helm install nginx nginx-0.1.0.tgz --dry-run
NAME: nginx
LAST DEPLOYED: Mon Jul 11 13:18:00 2022
NAMESPACE: cattle-system
STATUS: pending-install
REVISION: 1
HOOKS:
---
# Source: nginx/templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "nginx-test-connection"
  labels:
    helm.sh/chart: nginx-0.1.0
    app.kubernetes.io/name: nginx
    app.kubernetes.io/instance: nginx
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
  annotations:
    "helm.sh/hook": test
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args: ['nginx:80']
  restartPolicy: Never
MANIFEST:
---
# Source: nginx/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nginx
  labels:
    helm.sh/chart: nginx-0.1.0
    app.kubernetes.io/name: nginx
    app.kubernetes.io/instance: nginx
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
---
# Source: nginx/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    helm.sh/chart: nginx-0.1.0
    app.kubernetes.io/name: nginx
    app.kubernetes.io/instance: nginx
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: nginx
    app.kubernetes.io/instance: nginx
---
# Source: nginx/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    helm.sh/chart: nginx-0.1.0
    app.kubernetes.io/name: nginx
    app.kubernetes.io/instance: nginx
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: nginx
      app.kubernetes.io/instance: nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/name: nginx
        app.kubernetes.io/instance: nginx
    spec:
      serviceAccountName: nginx
      securityContext:
        {}
      containers:
        - name: nginx
          securityContext:
            {}
          image: "nginx:1.16.0"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {}
NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace cattle-system -l "app.kubernetes.io/name=nginx,app.kubernetes.io/instance=nginx" -o jsonpath="{.items[0].metadata.name}")
  export CONTAINER_PORT=$(kubectl get pod --namespace cattle-system $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
  echo "Visit http://127.0.0.1:8080 to use your application"

  kubectl –namespace cattle-system port-forward $POD_NAME 8080:$CONTAINER_PORT
 

 

Ein Aufruf von “helm install” ohne –dry-run Parameter würde die Software in unserem Kubernetes Cluster einrichten, ein “helm ls” würde das installierte Package zeigen.
Nun aber zum letzten Schritt, in dem wir unser Package einem zunächst lokalen Repository hinzufügen, das wir dann per github.org zugänglich machen.
Fügen Sie zunächst der Datei “Chart.yaml” einen Eintrag hinzu, der auf ein schönes nginx-Icon verweisen soll. Danach paketieren Sie erneut:
 

echo "icon: \"https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Nginx_logo.svg/320px-Nginx_logo.svg.png\"" >>nginx/Chart.yaml
helm lint nginx
helm package nginx


Nun nehmen wir an, daß das lokale Verzeichnis ein Helm Chart Repository sei. Es lassen sich alle Helm Charts unterhalb dieser Verzeichnisstruktur zu einem Repository zusammenfassen. Dafür muß lediglich ein Repository Index angelegt (und regelmäßig gepflegt) werden. Dieser Index ist eine weitere Datei, “index.yaml” genannt, die später von Tools wie Rancher oder “helm repo” Operationen ausgelesen und interpretiert wird. Um einen Repository Index zu erzeugen setzen Sie nun folgendes Kommando ab:
 

helm repo index .

In der Datei index.yaml sehen Sie nun, daß unser Helm Chart die lokale URL “nginx-0.1.0.tgz” trägt. Wenn wir nun das Chart Repository und alle Charts darin per github.org bereitstellen möchten ist die Basis-URL zu Ihrem github-Repository anzugeben. Natürlich wäre ein generisches Web Server auch denkbar, der statische Inhalte wie Helm Charts und Repository Index zur Verfügung stellt. Durch Verwendung von github, gitlab und ähnlichen profitieren Sie von der Möglichkeit zur Versionierung und leichteren Uploads Ihrer Dateien.
Erzeugen wir also den Index nochmals neu unter Angabe Ihres sogenannten github Repository Environments, einer dynamischen Webseite auf Basis der Dateien, die im zugehörigen git-Repository “myhelmrepo” liegen:

helm repo index . --url https://<youraccount>.github.io/myhelmrepo/

Damit alles zueinander paßt, legen Sie im finalen Abschnitt ein github Repository unter Ihrem freien github-Account an mit dem Namen “myhelmrepo”:
 

github repo

 

github repo 2

 

 

Laden Sie nun alle angelegten Charts und den Index dorthin hoch. Voraussetzung ist das Tool “git”, das Sie oft unter Linux vorinstalliert finden. Für den Login mittels git-Kommandozeile lassen Sie sich zunächst ein Login Token erzeugen. Das geschieht durch Klick auf Ihr Benutzerkonto -> Settings -> Developer settings -> Personal Access Tokens -> Generate new token.
Dann setzen Sie folgende Kommandos ab und geben Sie als git-Kennwort das eben erzeugte, kopierte Token an:
 

git init
git add index.yaml
git add *tgz
git add nginx
git commit -m "repo index and nginx chart"
git branch -M main
git remote add origin https://github.com/<myaccount>/myhelmrepo.git
git push -u origin main

 

Ihr github Repository müßte nun einige Dateien enthalten und sollte in etwa so aussehen:
 

github contents

Erzeugen Sie nun eine statische Website aus diesen Inhalten, “Github Pages” genannt. Damit können “helm repo” Kommandos und auch das Rancher Tool unser neu angelegtes Chart Repository einbinden und darstellen. Klicken Sie dafür auf “Settings” -> “Pages”. Wählen Sie den “main” branch aus und klicken Sie auf “Save”:
 

github pages

Testen Sie nun den Zugriff auf Ihre github Pages. Der Helm Index sollte erreichbar sein über die URL https://<meinaccount>.github.io/myhelmrepo/index.yaml :
 

index.yaml

Diese URL können Sie nun in Rancher im Bereich “Apps&Marketplace” angeben, so wie bereits in Teil 1 dieses Blogs beschrieben:
 

myhelmrepo einbinden

Sie erhalten ein weiteres Repository zur Auswahl für die Installation eigener, beliebig komplexer Anwendungen:

myhelmrepo auflisten

Hier angekommen möchte ich mich bei Ihnen für Ihr Interesse herzlich bedanken! Gerne stehe ich für weitere Fragen und Tests zur Verfügung.
 

Fazit:

Sie können mit den Werkzeugen Helm und Rancher eigene komplexe (Container-)Softwarepakete schnüren und elegant bereitstellen. Somit ist es möglich einen eigenen Marktplatz von Anwendungen zu definieren, die sich nicht nur gut in Ihre IT Infrastruktur integrieren, sondern auch leicht aktualisierbar und zentral verwaltbar sind. Stunden- oder gar tagelange Installationen und Konfigurationen verkürzen sich drastisch auf wenige Minuten, wenn die Installationsskripte gut vorbereitet und keine weiteren händischen Schritte vonnöten sind. Sie können mit Hilfe von Kubernetes und Helm, insbeonderes mit dessen Package Manager, einen großen Schritt näher an eine private Cloud Infrastruktur kommen, die auch noch zukunftsfähig weil portabel auf beliebige Public Clouds wie AWS, Azure und natürlich die Oracle Cloud Infrastructure ist.