Schritt-für-Schritt-Anleitung: Guest-Cluster in VMware vSphere Kubernetes Service erstellen

Dieser Beitrag ist aus einer Notiz entstanden, die die ersten Schritte mit VKS (VMware vSphere Kubernetes Service) erklärt, insbesondere die Erstellung eines eigenen Guest-Clusters in einem eigenen Namespace.

Die Begrifflichkeiten

Falls Sie sich hauptsächlich mit Vanilla Kubernetes beschäftigen, lohnt es sich, die VKS in wenigen Worten zu erklären. Die Kubernetes-Cluster werden ganz grob in vSphere nach dem Prinzip der Matrjoschka-Schachtelpuppen angelegt:

Supervisor > Namespaces (mehrere) > einzelne VKS Cluster (Guest Cluster)

Supervisor

Die höchste Ebene ist ein Objekt namens „Supervisor”. Im VKS gibt es immer genau drei Supervisor-Control-Plane-VMs. Die Worker-Nodes sind die ESXi-Hosts selbst. Jeder Host wird über den Spherelet (ein kubelet-Äquivalent auf dem Host) zum Worker-Node. Separate Worker-VMs gibt es nicht. Die Zahl der Worker entspricht damit der Zahl der ESXi-Hosts im vSphere Cluster.

Auf dem Supervisor ist VKS selbst als Kubernetes Service installiert (Workload Management > Services > Kubernetes Service). z. B. v1.32.7+vmware.5-fips-vsc…. Seit vSphere 8.0 U3 ist dieser Service von der Supervisor-Version entkoppelt und lässt sich getrennt aktualisieren.

Namespace

Ein Namespace ist eine logische Gliederung innerhalb des Supervisors. Die Kernaufgabe eines Namespaces ist, eine logische Grenze für die Vergabe von Permissions (RBAC), Storage-Policies, VM-Classes und Compute-Ressourcen zu bilden.

Guest Cluster

Ein Guest Cluster ist im Grunde ein vollwertiger CNCF-konformer Kubernetes-Cluster, der innerhalb eines Namespace angelegt wird.

Die drei Bausteine: ClusterClass, VKr und OS-Image

Um den Guest Cluster in der YAML-Datei korrekt beschreiben zu können, sind weitere Angaben erforderlich. Zwei davon sind echte Felder in der YAML-Datei: die ClusterClass (classRef) und die Version (version).

ClusterClass

ClusterClass beschreibt die VKS-Topologie als eine Vorlage, die den Aufbau des Clusters definiert (Control-Plane, Worker-Pools, verfügbare Variablen). VKS liefert fertige, versionierte Klassen mit.

Wie prüft man verfügbare ClusterClasses?

kubectl get clusterclass -A | grep vmware-system-vks-public

Erwartbare Ergebnisse:

NAMESPACE                  NAME                     READY   AGE
vmware-system-vks-public   builtin-generic-v3.1.0   True    8d
vmware-system-vks-public   builtin-generic-v3.2.0   True    8d
vmware-system-vks-public   builtin-generic-v3.3.0   True    8d
vmware-system-vks-public   builtin-generic-v3.4.0   True    8d
vmware-system-vks-public   builtin-generic-v3.5.0   True    7d6h
vmware-system-vks-public   builtin-generic-v3.6.0   True    7d6h
vmware-system-vks-public   builtin-generic-v3.7.0   True    7d6h

Der Name builtin-generic-vX.Y.0 wird in classRef.name verwendet, der Namespace vmware-system-vks-public in classRef.namespace. Die älteren (3.1.x > v3.7.0), kompatiblen Klassen sind weiterhin vorhanden und können genutzt werden.

Es ist ganz üblich, dass mehrere builtin-generic-Versionen nebeneinander existieren. Jede VKS-Version bringt ihre eigene ClusterClass mit, ältere bleiben für bestehende Cluster erhalten. Die jüngeren Versionen(hier v3.5.0–v3.7.0, erkennbar am kleineren AGE) sind mit der Aktualisierung des Kubernetes Service auf die Version 3.7.1+v1.36 dazugekommen.

Diese offizielle Broadcom-Seite „ClusterClass Variable Reference” ist Ihre absolute Single Source of Truth für die Variablen der VKS-ClusterClass.

KubernetesRelease

KubernetesRelease (VKr) beschreibt die jeweilige Kubernetes-Version. Es handelt sich um eine von VMware signierte Kubernetes-Distribution, die eine bestimmte Kubernetes-Version zusammen mit einem passenden OS-Image, Bootstrap-Artefakte sowie den Kernpaketen (CNI, CSI) enthält (oder darauf verweist).

Wie prüft man verfügbare KubernetesReleases? Der Befehl kubectl get kubernetesreleases lässt sich auch als kubectl get kr abkürzen.

kubectl get kubernetesreleases | grep -E 'NAME|1\.35|1\.36'

Erwartbare Ergebnisse:

NAME                       VERSION                  READY   COMPATIBLE   UPDATES AVAILABLE          AGE
v1.35.0---vmware.2-vkr.4   v1.35.0+vmware.2-vkr.4   False   False        v1.35.2---vmware.1-vkr.3   8d
v1.35.2---vmware.1-vkr.3   v1.35.2+vmware.1-vkr.3   True    True                                    8d
v1.35.5---vmware.1-vkr.1   v1.35.5+vmware.1-vkr.1   True    True                                    8d
v1.35.6---vmware.2-vkr.3   v1.35.6+vmware.2-vkr.3   True    True                                    8d
v1.36.1---vmware.4-vkr.5   v1.36.1+vmware.4-vkr.5   True    True                                    8d
v1.36.2---vmware.2-vkr.3   v1.36.2+vmware.2-vkr.3   True    True                                    8d

Eine Zeile mit READY=True und COMPATIBLE=True bedeutet, dass diese Versionen wählbar sind. Der Wert aus der Spalte NAME (mit – – -) wird in version eingetragen.

OSImage

OSImage legt fest, welche Image-Datei mit welcher Version als OS-Basis (Photon, Ubuntu) für die Node-VM verwendet wird.

Wie prüft man verfügbare OSImages?

kubectl get osimages | grep -E 'NAME|1\.35|1\.36'

Erwartbare Ergebnisse:

NAME                    VERSION            OS-NAME   OS-VERSION   ARCH    TYPE   AGE
vmi-062b7e890f2e09522   v1.35.6+vmware.2   ubuntu    22.04        amd64   cvmi   8d
vmi-09e08419c49071c82   v1.35.2+vmware.1   photon    5            amd64   cvmi   8d
vmi-0ca12bf184479de9a   v1.36.1+vmware.4   ubuntu    24.04        amd64   cvmi   8d
vmi-15cdf41add09f3439   v1.35.0+vmware.2   photon    5            amd64   cvmi   8d
vmi-1e3549495aafdad02   v1.36.1+vmware.4   photon    5            amd64   cvmi   8d
vmi-27b4cf8faef707f6b   v1.35.2+vmware.1   ubuntu    24.04        amd64   cvmi   8d
vmi-2c43b2570fdffa599   v1.35.5+vmware.1   ubuntu    24.04        amd64   cvmi   8d
vmi-3a3c5cef08debea3d   v1.36.1+vmware.4   ubuntu    22.04        amd64   cvmi   8d
vmi-751c19d8094c5222f   v1.35.2+vmware.1   ubuntu    22.04        amd64   cvmi   8d
vmi-7c87cd337fbd49951   v1.35.0+vmware.2   ubuntu    22.04        amd64   cvmi   8d
vmi-7e83543eef5f58d65   v1.35.6+vmware.2   photon    5            amd64   cvmi   8d
vmi-7f82c96298ecc4630   v1.35.5+vmware.1   photon    5            amd64   cvmi   8d
vmi-902c05d4fd4b92fd2   v1.35.5+vmware.1   ubuntu    22.04        amd64   cvmi   8d
vmi-b032c228b746e2b56   v1.36.2+vmware.2   ubuntu    24.04        amd64   cvmi   8d
vmi-b7ca083ec018c5470   v1.36.2+vmware.2   ubuntu    24.04        amd64   cvmi   8d
vmi-b9fd53a6e5ab721df   v1.36.2+vmware.2   photon    5            amd64   cvmi   8d
vmi-c476f321ab526101c   v1.35.6+vmware.2   ubuntu    24.04        amd64   cvmi   8d
vmi-da7c501408e59230f   v1.36.2+vmware.2   ubuntu    22.04        amd64   cvmi   8d

Standardmäßig wird Photon ausgewählt. Soll eine bestimmte Version von Ubuntu verwendet werden, müssen folgende Parameter in die YAML-Datei eingefügt werden:

    controlPlane:
      metadata:
        annotations:
          run.tanzu.vmware.com/resolve-os-image: os-name=ubuntu,os-version=24.04
      replicas: 1
    workers:
      machineDeployments:
        - class: node-pool
          name: worker-pool-01
          replicas: 1
          metadata:
            annotations:
              run.tanzu.vmware.com/resolve-os-image: os-name=ubuntu,os-version=24.04

und zwar nicht als eigenes Feld, sondern als Annotation run.tanzu.vmware.com/resolve-os-image an zwei Stellen. Zuerst für die Control-Plane und dann für den Worker-Pool. Mehr dazu in der offiziellen Dokumentation.

Namespace – Erstellung

Dieser Beitrag ist für die VKS-Nutzer vorgesehen, die bereits einen eigenen Namespace vom zuständigen Team, welches VKS-Infrastruktur verwaltet, zugewiesen bekommen. Dabei gehe ich von der folgenden Annahme aus: Wenn Sie VKS in Ihrem Unternehmen verwenden, haben Sie keine unbegrenzte Anzahl von Resources, deswegen findet die Erstellung von Namespace nicht automatisiert, sondern in vorheriger Absprache statt.

Bei der Erstellung von Namespace wird nämlich Folgendes definiert:

Permissions: Wer und mit welchen Rechten (Rolle wie Can view / Can edit / Owner) auf den Namespace über den Cluster-API-Endpunkt zugreifen darf.

Storage: Hier werden Storage-Policies hinzugefügt, die im Namespace zu nutzbaren StorageClasses übersetzt werden. storageClass-Wert im YAML.

Capacity and Usage: Es besteht auch die Möglichkeit die zur Verfügung stehenden Hardware-Ressourcen, wie CPU-, Arbeitsspeicher- und/oder Disk-Speicher zu begrenzen.

VM Service: Hier wird ausgewählt, welche VMs gemäß den T-Shirt-Größen bei der Clustererstellung verwendet werden können. vmClass-Wert im YAML. Ebenfalls hier wird die Content Library hinzugefügt. Die OS- und VKr-Images stammen aus einer Content Library.

Login am Supervisor

Um den Guest Cluster im zugewiesenen Namespace zu erstellen, muss man sich entsprechend anmelden, da er nur aus dem Namespace-Kontext des Supervisors heraus erstellt werden kann.

kubectl vsphere login \
 --server=<IP-oder-FQDN-Supervisor> \
 --insecure-skip-tls-verify \
 --vsphere-username=<benutzername> \
 --tanzu-kubernetes-cluster-namespace=<Namespace-Name>

kubectl config current-context # Dient zur Kontrolle, zeigt "namespace-name"

–insecure-skip-tls-verify – Dieser Parameter deaktiviert die Zertifikatsprüfung. Nur verwenden, wenn das Supervisor-Zertifikat auf dem Client nicht vertrauenswürdig ist.

Durch die Verwendung des Parameters tanzu-kubernetes-cluster-namespace wird die Ausführung des Befehls kubectl config use-context namespace-name überflüssig.

YAML-Datei für den Guest Cluster

Diese YAML-Datei dient ausschließlich Lernzwecken und kann nicht für das Cluster-Deployment verwendet werden. In dieser Datei steht jedes · für genau ein Leerzeichen. Die Punkte dürfen nicht in die echte YAML-Datei übernommen werden. Die gültige YAML-Datei befindet sich unter dieser Erklärung.

# apiVersion legt die verwendete Kubernetes-API für das Cluster-Objekt fest.
apiVersion: cluster.x-k8s.io/v1beta2

# Es wird ein Cluster-API-Clusterobjekt angelegt.
kind: Cluster

# Metadaten des neuen Guest Clusters.
metadata:
··# Frei wählbarer Name des Guest Clusters.
··name: akr-test01

··# Supervisor-Namespace, in dem der Guest Cluster erstellt wird.
··namespace: akr-001

# Gewünschte Konfiguration des Clusters.
spec:
··# Netzwerkbereiche des Kubernetes-Clusters.
··clusterNetwork:
····# CIDR für Kubernetes Services (ClusterIP-Adressen).
····services:
······cidrBlocks:
········- 10.96.0.0/12

····# CIDR für die Pod-Adressen im Guest Cluster.
····pods:
······cidrBlocks:
········- 192.168.0.0/16

····# Interne Kubernetes-DNS-Domain.
····serviceDomain: cluster.local

··# Topologie und VKS-spezifische Cluster-Konfiguration.
··topology:
····# Verwendete VKS ClusterClass.
····classRef:
······# Name der ClusterClass.
······name: builtin-generic-v3.7.0

······# Namespace, in dem VMware die öffentliche ClusterClass bereitstellt.
······namespace: vmware-system-vks-public

····# Gewünschter KubernetesRelease.
····# Vorher mit "kubectl get kubernetesreleases" prüfen.
····# Der Release sollte READY=True und COMPATIBLE=True sein.
····version: v1.35.6---vmware.2-vkr.3

····# Anzahl der Control-Plane-Nodes.
····# Für einen Testcluster genügt ein Node; produktiv wird typischerweise HA geplant.
····controlPlane:
······replicas: 1

····# Definition der Worker-Nodes.
····workers:
······machineDeployments:
········# VKS-Worker-Klasse innerhalb der ClusterClass.
········- class: node-pool

··········# Name des Worker-Pools.
··········name: worker-pool-01

··········# Anzahl der Worker-Nodes.
··········replicas: 1

····# Parameter, die an die ClusterClass übergeben werden.
····variables:
······# VM Class bestimmt die virtuelle Hardwaregröße der Nodes.
······- name: vmClass
········value: best-effort-medium

······# StorageClass für die Root-Volumes der Kubernetes-Nodes.
······# Die virtuellen Systemplatten der Control-Plane-
······# und Worker-VMs werden also darüber angelegt.
······- name: storageClass
········value: vsan-default-storage-policy

······# Zusätzliche vSphere-spezifische Optionen.
······- name: vsphereOptions
········value:
··········# Konfiguration für Persistent Volumes.
··········persistentVolumes:
············# Default StorageClass INNERHALB des Guest Clusters.
············# Sie wird verwendet, wenn ein PersistentVolumeClaims (PVC)
············# keine eigene storageClassName angibt.
············defaultStorageClass: vsan-default-storage-policy

Die Werte 10.96.0.0/12 und 192.168.0.0/16 sind nur Beispiele, wobei 10.96.0.0/12 standardmäßige internal IP Address Range (CIDR) ist.  Die Service- und Pod-Netze dürfen untereinander sowie mit relevanten Infrastruktur-/Routing-Netzen nicht kollidieren.

Unter diesem LINK lässt sich eine gültige YAML-Datei mit identischer Konfiguration wie oben herunterladen. Passen Sie die Datei entsprechend an. Es lohnt sich, die Datei serverseitig zu validieren, bevor Sie den Cluster erstellen.

kubectl apply --dry-run=server -f guest-cluster-vorlage.yaml

Danach:

kubectl apply -f guest-cluster-vorlage.yaml

Nach wenigen Minuten sollte der Cluster vollständig provisioniert sein. Dies lässt sich mit der folgenden Befehlskette verifizieren:

kubectl get cluster,machines,machinedeployments,virtualmachines

Erwartbare Ergebnisse:

NAME                                  CLUSTERCLASS             AVAILABLE   CP DESIRED   CP AVAILABLE   CP UP-TO-DATE   W DESIRED   W AVAILABLE   W UP-TO-DATE   PHASE         AGE     VERSION
cluster.cluster.x-k8s.io/akr-test01   builtin-generic-v3.7.0   True        1            1              1               1           1             1              Provisioned   3d19h   v1.35.6+vmware.2

NAME                                                                   CLUSTER      NODE NAME                                     FAILURE DOMAIN   READY   AVAILABLE   UP-TO-DATE   PHASE     AGE     VERSION
machine.cluster.x-k8s.io/akr-test01-cnbpf-wcqjr                        akr-test01   akr-test01-cnbpf-wcqjr                        domain-c11       True    True        True         Running   3d19h   v1.35.6+vmware.2
machine.cluster.x-k8s.io/akr-test01-worker-pool-01-smcpz-k6n9h-prnj6   akr-test01   akr-test01-worker-pool-01-smcpz-k6n9h-prnj6   domain-c11       True    True        True         Running   3d19h   v1.35.6+vmware.2

NAME                                                                 CLUSTER      AVAILABLE   DESIRED   CURRENT   READY   UP-TO-DATE   PHASE     AGE     VERSION
machinedeployment.cluster.x-k8s.io/akr-test01-worker-pool-01-smcpz   akr-test01   True        1         1         1       1            Running   3d19h   v1.35.6+vmware.2

NAME                                                                               POWER-STATE   AGE
virtualmachine.vmoperator.vmware.com/akr-test01-cnbpf-wcqjr                        PoweredOn     3d19h
virtualmachine.vmoperator.vmware.com/akr-test01-worker-pool-01-smcpz-k6n9h-prnj6   PoweredOn     3d19h

 

Login in den neuen Guest Cluster

Die folgende Befehlskette sorgt für eine direkte Anmeldung auf der Ebene des Guest Clusters.

kubectl vsphere login \
 --server=<IP-oder-FQDN-Supervisor> \
 --insecure-skip-tls-verify \
 --vsphere-username=<Benutzername> \
 --tanzu-kubernetes-cluster-namespace=<Namespace-Name>
 --tanzu-kubernetes-cluster-name=<Guest-Cluster-Name>

.