vSphere Tanzu – GuestCluster
Mit vSphere with Tanzu (ehemals Project Pacific), bzw. VCF with Tanzu hat VMware es geschafft die Welt von Containern mit der von VMs zu verbinden. Dadurch ist ein ESXi-Server in der Lage Container auszuführen.
Dabei werden vSphere-Cluster zu sogenannten Workload Domains. Pro Workload Domain wird ein Set an sogenannten SupervisorControlPlaneVMs erstellt. Dies sind mindestens zwei, standardmäßig drei. Diese bilden die ControlPlane des Kubernetesclusters und jeder ESXi-Server in dieser Workload Domain ist eine WorkerNode.
Nun liegt die große Funktion von Tanzu aber etwas versteckt: Guest Cluster.
Dabei können innerhalb des SuperVisorClusters weitere Kubernetes Cluster deployt werden. Diese Cluster können dabei in einer anderen Version als der SuperVisorCluster laufen und sind von diesem auch größtenteils unabhängig. Doch wie erstellt man diese?
Dazu gibt es in den weiten des Internets verschiedene Möglichkeiten, eine möchte ich euch hier zeigen.
Vorraussetzungen
Zunächst einmal muss aber eine ContentLibrary angelegt werden um die entsprechenden Images der WorkloadDomain bereitzustellen. Dazu legt man eine neue ContentLibrary vom Typ “subscribed” an und gibt als subscribed URL diese an: https://wp-content.vmware.com/v2/latest/lib.json.
Nun kann man in der Konfiguration der Workload Domain diese ContentLibrary angeben:
Ebenso ist es notwendig mindestens einen Namespace anzulegen und diesem Namespace mindestens eine StoragePolicy zuzuweisen:
In diesem Beispiel heißt der Namespace “namespace001”.
Deployment
Als nächstes ist es Zeit den GuestCluster zu deployen. Dazu verwende ich die folgende Definition. Diese erstellt einen Cluster mit dem Namen guest-cluster-01 im Namespace namespace001.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
apiVersion: run.tanzu.vmware.com/v1alpha1 kind: TanzuKubernetesCluster metadata: name: guest-cluster-01 namespace: namespace001 spec: topology: controlPlane: count: 1 class: guaranteed-xsmall # vmclass to be used for master(s) storageClass: vsan-default-storage-policy workers: count: 2 class: guaranteed-xsmall # vmclass to be used for workers(s) storageClass: vsan-default-storage-policy distribution: version: v1.17.8+vmware.1-tkg.1.5417466 settings: network: cni: name: calico services: cidrBlocks: ["198.51.100.0/12"] pods: cidrBlocks: ["192.0.2.0/16"] |
Dabei wird ein Guest Cluster mit einem Node in der ControlPlane und zwei Workern erstellt. Dabei wird der Cluster die Version 1.17.8 haben. Als CNI Plugin kann man entweder Antrea oder Calico verwenden.
Zu beachten ist das die angegebene Version in der vorher erstellten ContentLibrary vorhanden ist. Zu beachten ist der Unterschied in der Schreibweise:
ContentLibrary: ob-16551547-photon-3-k8s-v1.17.8---vmware.1-tkg.1.5417466
Definition: v1.17.8+vmware.1-tkg.1.5417466
Man lässt also alles vor der Version (v1.17.8) weg und tauscht die drei Minuszeichen gegen ein Plus.
Welche Vorlagen in der ContentLibrary vorhanden sind, kann man sich im vCenter anschauen.
Alternativ gibt es einen schnellen Weg über kubectl
kubectl get virtualmachineimages. Dies zeigt uns eine Liste an verfügbaren Images an:
1 2 3 4 5 6 7 |
NAME VERSION OSTYPE ob-15957779-photon-3-k8s-v1.16.8---vmware.1-tkg.3.60d2ffd v1.16.8+vmware.1-tkg.3.60d2ffd vmwarePhoton64Guest ob-16466772-photon-3-k8s-v1.17.7---vmware.1-tkg.1.154236c v1.17.7+vmware.1-tkg.1.154236c vmwarePhoton64Guest ob-16545581-photon-3-k8s-v1.16.12---vmware.1-tkg.1.da7afe7 v1.16.12+vmware.1-tkg.1.da7afe7 vmwarePhoton64Guest ob-16551547-photon-3-k8s-v1.17.8---vmware.1-tkg.1.5417466 v1.17.8+vmware.1-tkg.1.5417466 vmwarePhoton64Guest ob-16897056-photon-3-k8s-v1.16.14---vmware.1-tkg.1.ada4837 v1.16.14+vmware.1-tkg.1.ada4837 vmwarePhoton64Guest ob-16924027-photon-3-k8s-v1.17.11---vmware.1-tkg.1.15f1e18 v1.17.11+vmware.1-tkg.1.15f1e18 vmwarePhoton64Guest |
An dieser Stelle kann man sich einfach die Version kopieren.
Nachdem der Cluster erstellt wurde und läuft ist es Zeit sich auf diesen zu Verbinden:
1 2 3 |
kubectl vsphere login --server=<SuperVisorAPI> \ --tanzu-kubernetes-cluster-namespace namespace001 \ --tanzu-kubernetes-cluster-name guest-cluster-01 |
Zu beachten ist hierbei das man den Namespace und den Namen des Clusters angibt den man erstellt hat. Die SuperVisorAPI ist natürlich die ControlPlane-Adresse des SuperVisorClusters.
Mittels dem Befehl
kubectl config get-contexts kann man sich die aktuell vorhandenen Kontexts anzeigen lassen. Mittels
kubectl config use-context <context> kann man dann in den Kontext des GuestClusters wechseln.
Berechtigungen?
Versucht man nun Pods in diesem GuestCluster zu deployen, schlägt dies vermutlich mit dieser Fehlermeldung fehl:
message: 'pods "hello-kubernetes-fb869d65c-" is forbidden: unable to validate
against any pod security policy: []'
reason: FailedCreate
status: "True"
type: ReplicaFailure
Dies liegt daran das ein SSO-User standardmäßig keine Berechtigung hat Workload im soeben erstellten GuestCluster zu deployen. Hierzu muss erst ein entsprechendes RoleBinding erstellt werden:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: rolebinding-default-privileged-sa-ns_default namespace: default roleRef: kind: ClusterRole name: psp:vmware-system-privileged apiGroup: rbac.authorization.k8s.io subjects: - kind: Group apiGroup: rbac.authorization.k8s.io name: system:serviceaccounts |
Segmentiert man den GuestCluster weiter mit Namespaces, so muss das RoleBinding für jeden Namespace erneut erstellt werden. Danach sollte das Deployen von Workload funktionieren. Mit der Anwendung dieser Definition erhält jeder Serviceaccount die Cluster Rolle psp:vmware-system-privileged. Diese Rolle wiederum wurde beim Tanzu Deployment automatisch angelegt und enthält die notwendigen Berchtigungen um Workload zu deployen.
Alternativ kann man innerhalb des GuestClusters eigene User anlegen und über das RoleBasedAccessManagement von Kubernetes mit den entsprechenden Rollen und Privilegien versehen.
Nacharbeiten
Nutzt man nun im SuperVisorCluster Harbor als Image Registry und hat dort ein privates, evtl. selbstsigniertes Zertifikat im Einsatz und möchte die dort gehosteten Images auch im GuestCluster nutzen, so muss man dessen Root-Zertifikat auf die GuestCluster kopieren. Aktuell muss man dazu ein Script auf dem Master der ControlPlane des SuperVisorClusters ausführen, in Zukunft soll dies automatisch geschehen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#!/bin/bash rootCA="/root/ca.crt" # path of the manually downloaded root CA gcname="guest-cluster-01" # guest cluster name gcnamespace="namespace001" # Supervisor cluster namespace where guest cluster get deployed [ -z "$rootCA" -o -z "$gcname" -o -z "$gcnamespace" ] && echo "Please populate rootCA/gcname/gcnamespace variable" && exit workdir="/tmp/$gcnamespace-$gcname" mkdir -p $workdir sshkey=$workdir/gc-sshkey # path for gc private key gckubeconfig=$workdir/kubeconfig # path for gc kubeconfig # @param1: ip # @param2: ca installCA() { node_ip=$1 capath=$2 scp -q -i $sshkey -o StrictHostKeyChecking=no $capath vmware-system-user@$node_ip:/tmp/ca.crt [ $? == 0 ] && ssh -q -i $sshkey -o StrictHostKeyChecking=no vmware-system-user@$node_ip sudo cp /etc/pki/tls/certs/ca-bundle.crt /etc/pki/tls/certs/ca-bundle.crt_bk [ $? == 0 ] && ssh -q -i $sshkey -o StrictHostKeyChecking=no vmware-system-user@$node_ip 'sudo cat /etc/pki/tls/certs/ca-bundle.crt_bk /tmp/ca.crt > /tmp/ca-bundle.crt' [ $? == 0 ] && ssh -q -i $sshkey -o StrictHostKeyChecking=no vmware-system-user@$node_ip sudo mv /tmp/ca-bundle.crt /etc/pki/tls/certs/ca-bundle.crt # if error occurred, restore ca-bundler.crt_bk [ $? == 0 ] && ssh -q -i $sshkey -o StrictHostKeyChecking=no vmware-system-user@$node_ip sudo systemctl restart docker.service } # get guest cluster private key for each node kubectl get secret -n $gcnamespace $gcname"-ssh" -o jsonpath='{.data.ssh-privatekey}' | base64 --decode > $sshkey [ $? != 0 ] && echo " please check existence of guest cluster private key secret" && exit chmod 600 $sshkey #get guest cluster kubeconfig kubectl get secret -n $gcnamespace $gcname"-kubeconfig" -o jsonpath='{.data.value}' | base64 --decode > $gckubeconfig [ $? != 0 ] && echo " please check existence of guest cluster private key secret" && exit # get IPs of each guest cluster nodes iplist=$(KUBECONFIG=$gckubeconfig kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")].address}') for ip in $iplist do echo "installing root ca into node $ip (needs about 10 seconds)... " installCA $ip $rootCA && echo "Successfully installed root ca into node $ip" || echo "Failed to install root ca into node $ip" done |
Die entsprechende IP-Adresse und das Root-Passwort des Masters kann man über das vCenter erhalten. Dazu führt man dort einfach dieses Script aus: /usr/lib/vmware-wcp/decryptK8Pwd.py
Bei dem Script muss man den Namen und den Namespace, mit dem der GuestCluster definiert wurde, korrekt angeben. Alternativ kann man aber auch natürlich die Arbeit des Scripts auf andere Weise erledigen.
Das Script stammt nicht von mir persönlich, sondern wurde mir im Rahmen einer Installation von VMware übergeben.
Use-Cases
Ja, jetzt haben wir einen voll funktionierenden GuestCluster, aber wozu?
GuestCluster können verschiedene Anwendungen haben. Seien es Developer die ihre Software testen wollen und dazu einen ganzen Cluster unter ihrer Verwaltung haben wollen. Oder man betreibt mehrere Cluster für mehrere Abteilungen. Oder man nutzt automatisierte Systeme wie z.B. GitLab, Jenkins oder Ansible Tower die diese Cluster nutzen können um sich selbst den Anforderungen an sie entsprechend zu sizen.