Neuigkeiten von trion.
Immer gut informiert.

Crossplane Tutorial

Kubernetes

Im DevOps Kontext haben sich über die Zeit verschiedene Werkzeuge entwickelt:
Waren Puppet und Chef noch frühe Vertreter mit unterschiedlichen Paradigmen (deklarativ vs. imperativ) verwendeten doch beide einen Agent. Ansible setzte sich weitgehend als imperative Lösung ohne Agent für die Provisionierung von Systemen durch, während Terraform als Infrastruktur- und Cloudwerkzeug de-factor Standard wurde.
Sowohl Ansible als auch Puppet setzen auf eine Konfiguration, die nahe an typischen Datenformaten von APIs sind (YAML bzw. JSON).

In diesem Beitrag soll es um ein neuartiges Werkzeug gehen, dass den deklarativen Ansatz verfolgt, jedoch die Kubernetes API als Schnittstelle verwendet. Crossplane geht damit einen spannenden Weg und erlaubt es, bestehende Verfahren zur Authentifizierung und Autorisierung weiter zu nutzen und sich dank zahlreicher Kubernetes Libraries auch einfach an eigene oder bestehende Anwendungen zu integrieren. Dies ermöglicht eine konsistente Verwaltung von (Cloud-)Ressourcen und Kubernetes-Workloads. Crossplane bietet somit eine engere Integration zwischen Anwendungen und Infrastruktur, was die Automatisierung und das Management vereinfacht.

Dies wird dank der erweiterbaren API von Kubernetes möglch: Alle Crossplane Ressourcen sind Custom Resource Definitions (CRDs) in Kubernetes. Die Erweiterung von Crossplane ist durch die erweiterbare API von Kubernetes ebenfalls gegeben, so können sogenannte Provider nachgerüstet werden. Dazu können vorhandene Kubernetes Konzepte wie ConfigMaps oder Secrets verwendet werden.

Aus diesem Ansatz ergeben sich folgende Vorteile von Crossplane:

  1. Infrastrukturverwaltung kann durch kubectl erfolgen

  2. Auf der Kubernetes API aufsetzende Werkzeuge, wie ArgoCD oder Flux, können nahtlos eingesetzt werden

  3. Durch die grundsätzlich Abstraktion wird die Unterstützung unterschiedlichster Zielinfrastruktur vereinfacht

  4. Eigene Anbindungen - sogenannte Provider - können auf einfache Weise entwickelt werden

  5. Durch die Verwendung von Kubernetes Standards kann Crossplane auf beliebigen Kubernetes basierten Clustern betrieben werden

  6. Durch den deklarativen Ansatz kann Crossplane die von Kubernetes bekannten Self-Healing-Eigenschaften bieten

Crossplane benötigt jedoch einen Kubernetes API Server als Basisinfrastruktur. Im folgenden schauen wir uns die Installation und Verwendung von Crossplane genauer an.

Für einen einfachen und schnellen Start bietet sich ein lokales Kubernetes an. Das kann ein Docker Desktop, kind (Kubernetes-in-Docker) oder auch Minikube sein. Minikube hat dabei den Vorteil, dass es verschiedene Isolationsmechanismen unterstützt. Speziell unter Linux sind kind und Minikube mit dem Docker Driver besonders leichtgewichtig aufzusetzen.

Crossplane auf Minikube

Um Crossplane auf Minikube zu testen, sind einige Werkzeuge erforderlich:

  1. Das kubectl Werkzeug zur Steuerung von Kubernetes

  2. Minikube um den Kubernetes Cluster aufzusetzen

  3. Helm als Paketmanager zur Installation von Crossplane

  4. Optional das Crossplane CLI mit dem Crossplane komfortabel gesteuert werden kann

Das Setup eines Minikube Kubernetes Clusters für Crossplane ist im Prinzip ein ganz gewöhnlicher Cluster, hier reicht auch eine einzelne Node.

Setup Minikube Kubernetes Cluster mit Docker Driver
$ minikube start --driver docker --memory 8192 --cpus 4

Zur Sicherheit sollte verifiziert werden, dass auf dem richtigen Cluster gearbeitet wird. Dies ist eine Routineabfrage, die sich vor dem Start von Arbeiten mit Kubernetes bewährt hat, um zu vermeiden, dass irrtümlich auf dem falschen Cluster gearbeitet wird.

Check des aktuellen Kubernetes Context und Clusters
$ kubectl config get-contexts
CURRENT   NAME          CLUSTER      AUTHINFO    NAMESPACE
*         minikube      minikube     minikube    default

Wenn der Cluster bereit steht, kann die Installation von Crossplane im Minikube Cluster vorgenommen werden. Zur Installation wird Helm verwendet, das Crossplane Helm Repository ist unter https://charts.crossplane.io/stable zu finden.
Crossplane sollte standardmäßig im Namespace crossplane-system installiert werden, entsprechend ist der Helm Aufruf mit diesem Namespace konfiguriert.

Installation von Crossplane mit Helm
$ helm repo add crossplane-stable https://charts.crossplane.io/stable
$ helm repo update
$ helm install crossplane \
  --create-namespace --namespace crossplane-system \
   crossplane-stable/crossplane

Die Installation von Crossplane selbst ist eine Anwendung, die ganz normal in Kubernetes läuft. Entsprechend können die Helm und kubectl-Mittel verwendet werden, um den Status der Installation abzufragen. Gerade bei der ersten Installation von Crossplane lohnt ein Blick darauf, dass die Crossplane Pods korrekt starten.

Abfrage der Crossplane Installation
$ helm list -n crossplane-system
$ kubectl get all -n crossplane-system
$ kubectl -n crossplane-system get pods --watch

Das optionale Crossplane CLI liegt als Binary für verschiedene Plattformen vor. Zur komfortablen Installation wird durch Crossplane ein Shellscript ausgeliefert, das die Plattform ermittelt und den richtigen Download startet.
Angenehm daran ist, dass keine administrativen Rechte vorausgesetzt werden, sondern der Download von der globalen Einrichtung im $PATH getrennt sind. Wer ein zusätzliches Verzeichnis im $PATH zur lokalen Installation von Software aufgenommen hat, kommt gänzlich ohne administrative Rechte bei der Installation des Crossplane CLI aus.
Alternativ kann das Crossplane CLI unter /usr/local/bin global installiert werden.

Installation des Crossplane CLI in einem lokalen Verzeichnis
$ curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | sh
$ mv kubectl-crossplane ~/bin
$ kubectl crossplane --help

Die Installation von Crossplane ist damit soweit abgeschlossen und Crossplane ist im Prinzip einsatzbereit.
Zur Unterstützung spezifischer Infrastruktur werden noch sogenannte Crossplane Provider benötigt. Eine Liste verschiedener Provider ist im Crossplane Marketspace unter https://marketplace.upbound.io/providers/ zu finden.

Um die Funktionsweise von Crossplane genauer kennenzulernen, ohne jedoch eine zusätzliche Abhängigkeit auf eine Cloud wie Amazon AWS, Azure oder Google zu haben, bietet sich an mit dem ohnehin für Crossplane erforderlichen Kubernetes Cluster zu arbeiten.

Crossplane Kubernetes Provider

Crossplane kann mit dem Crossplane Kubernetes Provider Objekte in Kubernetes verwalten. Nun kann argumentiert werden, dass dazu bereits Werkzeuge, wie ArgoCD, existieren. Das ist natürlich auch korrekt, doch geht es hier zunächst darum, die Funktionsweise von Crossplane und Providern vorzustellen. Zum anderen kann es durchaus sinnvoll sein, Crossplane als einheitliche Basis zur Verwaltung unterschiedlicher Arten von Infrastruktur zu nutzen, und dazu können dann auch Kubernetes Objekte gehören.

Der Crossplane Kubernetes Provider ist im Marketplace unter https://marketplace.upbound.io/providers/crossplane-contrib/provider-kubernetes/ zu finden.
Das zugehörige GitHub Repository findet sich unter https://github.com/crossplane-contrib/provider-kubernetes/tree/main .

Zur Installation des Providers gibt es drei Optionen, die alle darauf hinauslaufen, dass ein entsprechendes Provider Objekt, eine Crossplane Custom Resource in Kubernetes, angelegt wird:

  1. Direkte Verwendung der Crossplane Provider CRD Objekte in Kubernetes

  2. Konfiguration über Helm direkt bei der Installation von Crossplane oder als nachträgliches Upgrade

  3. Verwendung des Crossplane CLI

Crossplane reagiert auf diese Objekte mit der Installation des jeweiligen Providers, ebenfalls in Kubernetes.

Installation des Crossplane Kubernetes Providers mit Kubernetes API
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-kubernetes
spec:
  package: xpkg.upbound.io/crossplane-contrib/provider-kubernetes:v0.9.0

Das Crossplane Helm Chart unterstützt sowohl bei der initialen Installation von Crossplane, als auch als nachträgliches Upgrade den Wert provider.packages, um darüber zu installierende Provider zu konfigurieren. Als Werte werden darin die Paketnamen und Versionen zu installierender Provider angegeben.

Installation des Crossplane Kubernetes Providers mit einem nachträglichen Upgrade der Helm Installation
$ helm upgrade crossplane --reuse-values \
  --namespace crossplane-system \
  --set provider.packages='{xpkg.upbound.io/crossplane-contrib/provider-kubernetes:v0.9.0}' \
  crossplane-stable/crossplane

Das Crossplane CLI bietet dazu ebenfalls ein Kommando: xpkg install provider. Im Vergleich zu Helm ist hier weniger zu tippen und es ist davon auszugehen, dass das Crossplane CLI zukünftig noch im weitere Funktionalität erweitert wird.

Installation des Crossplane Kubernetes Providers mit dem Crossplane CLI
$ crossplane xpkg install provider xpkg.upbound.io/crossplane-contrib/provider-kubernetes:v0.9.0
provider/crossplane-contrib-provider-kubernetes created

Unerheblich, für welche Installationsvariante man sich entscheidet, im Ergebnis legen alle drei ein entsprechendes Provider Objekt an. Wie alle anderen Kubernetes Objekte wird nicht nur der Zielzustand, sondern auch der aktuelle Zustand über die Kubernetes API bereitgestellt. Damit lässt sich der Stand der Installation des Providers verfolgen. In der Übersicht von kubectl get providers ist der Installationszustand und Health-Status des Crossplane Providers aufgeführt. Mehr Details erhält man wie üblich durch kubectl describe.

Abfrage des Crossplane Provider Status
$ kubectl get providers
NAME                                    INSTALLED  HEALTHY
crossplane-contrib-provider-kubernetes  True       True

PACKAGE                                                     AGE
xpkg.upbound.io/crossplane-contrib/provider-kubernetes:v0.9.0   31s

Damit ist der Crossplane Provider installiert und prinzipiell einsatzbereit. Zur Verwaltung von Objekten in einem Kubernetes Cluster benötigt der Provider jedoch noch Zugriff auf den Kubernetes Zielcluster. Dies könnte ein anderer Cluster sein, oder aber auch der Cluster, in dem der Crossplane Provider installiert wurde. Der Einfachheit halber soll hier der selbe Cluster verwendet werden, in dem auch Crossplane installiert ist. Die entsprechenden Rechte zur Cluster Administration, die der Provider benötigt, werden dem ServiceAccount zugeordnet, mit dem Crossplane Kubernetes Provider betrieben wird.
Da der Name des ServiceAccount generiert ist, wird dieser dynamisch mit den Unix Tools grep und sed hergeleitet. Anschließend wird für den ServiceAccount ein ClusterRoleBinding auf die Rolle cluster-admin hergestellt. Diese Rolle hat alle Rechte im Kubernetes Cluster. Ob dies für den produktiven Betrieb unter Sicherheitsgesichtspunkten das beste Vorgehen ist, sollte im Einzelfall betrachtet und diskutiert werden. Das Thema Clustersicherheit wird leider oft erst dann ernst genommen, wenn es bereits zu problematischen Situationen gekommen ist.

Berechtigung des Crossplane Kubernetes Providers für den Kubernetes Cluster
$ SA=$(kubectl -n crossplane-system get sa -o name | grep provider-kubernetes | sed -e 's|serviceaccount\/|crossplane-system:|g')
$ kubectl create clusterrolebinding provider-kubernetes-admin-binding --clusterrole cluster-admin --serviceaccount="${SA}"

Der Provider selbst muss nun noch wissen, wie er auf den Kubernetes Cluster zugreift. Dies wird durch eine ProviderConfig konfiguriert. Eine ProviderConfig wird entsprechend auch für den Zugriff anderer Provider auf externe APIs verwendet. Der Inhalt richtet sich dabei dann nach dem spezifischen Provider.

Im folgenden Beispiel wird durch source: InjectedIdentity konfiguriert, dass der eigene Cluster mit der eigenen ServiceAccount Identität verwendet werden soll.

ProviderConfig für den umgebenden Kubernetes Cluster im Crossplane Kubernetes Provider
apiVersion: kubernetes.crossplane.io/v1alpha1
kind: ProviderConfig
metadata:
  name: kubernetes-provider
spec:
  credentials:
    source: InjectedIdentity

Damit ist nun Crossplane mitsam einem ersten Provider installiert und konfiguriert. Die Verwendung des Providers wird im folgenden aufgezeigt.

Crossplane Verwaltung von Kubernetes Objekten

Als Beispiel für ein Kubernetes Objekt wird eine ConfigMap verwendet. Die API wird durch den Crossplane Kubernetes Provider im Namespace kubernetes.crossplane.io bereitgestellt. Ein Kubernetes Objekt ist dann vom Typ kind: Object.
In der Spezifikation werden dann durch die Felder forProvider das Kubernetes Objekt und durch providerConfigRef die zu verwendende Clusterkonfiguration spezifiziert.

Beispiel einer ConfigMap für den Crossplane Kubernetes Provider
apiVersion: kubernetes.crossplane.io/v1alpha1
kind: Object
metadata:
  name: xp-managed-sample
spec:
  forProvider:
    manifest:
      apiVersion: v1
      kind: ConfigMap
      metadata:
        # name: in manifest is optional and defaults to Object name
        namespace: default
        labels:
          crossplane: "true"
      data:
        sample-key: sample-value
  providerConfigRef:
    name: kubernetes-provider

Nachdem das Objekt für Crossplane konfiguriert wurde, kann die erzeugte ConfigMap abgefragt werden.

Erzeugte ConfigMap
$ kubectl get cm
NAME                DATA   AGE
kube-root-ca.crt    1      25h
xp-managed-sample   1      2s

Änderungen des Objektes werden durch den Provider automatisch ausgebracht. Wird die ConfigMap manuell gelöscht oder editiert, so stellt der Crossplane Kubernetes Provider den konfigurierten Zielzustand nach kurzer Zeit wieder her. Änderungen an der Objektdefinition wirken sich dabei unmittelbar aus.
Um die ConfigMap zu löschen muss die Objektdefinition des Providers gelöscht werden. Auch hier erfolgt unmittelbar eine Anpassung im Kubernetes Cluster durch den Provider.

Das genaue Verhalten von Providern kann weiter beeinflusst werden, ob bspw. lediglich Aktualisierungen durch den Provider verwaltet werden sollen, jedoch ein Administrator die Verantwortung für die Erzeugung und/oder Löschen der Objekte inne hat.

Helm

Um die Flexibilität von Crossplane konkreter aufzuzeigen wird noch ein zweiter Provider verwendet: Der Helm Provider erlaubt es, Helm Charts zu verwalten. Dabei läuft die Installation und Konfiguration recht ähnlich ab, erst bei der konkreten Konfiguration von zu verwaltenden Objekten zeigen sich deutlichere Unterschiede.

Installation des Crossplane Helm Providers mit dem Crossplane CLI
$ crossplane xpkg install provider xpkg.upbound.io/crossplane-contrib/provider-helm:v0.16.0
provider/crossplane-contrib-provider-helm created

Auch der Helm Provider benötigt einen Kubernetes Cluster, auf dem Helm angewandt werden soll. Die Konfiguration ist analog zum Crossplane Provider Kubernetes.

Berechtigungen des Kubernetes Zugriffs für den Crossplane Helm Provider
$ SA=$(kubectl -n crossplane-system get sa -o name | grep provider-helm | sed -e 's|serviceaccount\/|crossplane-system:|g')
$ kubectl create clusterrolebinding provider-helm-admin-binding --clusterrole cluster-admin --serviceaccount="${SA}"

Eine entsprechende ProviderConfig für den Crossplane Helm Provider wird ebenfalls benötigt.

ProviderConfig für den umgebenden Kubernetes Cluster im Crossplane Helm Provider
apiVersion: helm.crossplane.io/v1alpha1
kind: ProviderConfig
metadata:
  name: helm-provider
spec:
  credentials:
    source: InjectedIdentity

Die Konfiguration eines Helm Releases wird durch die API helm.crossplane.io und kind: Release ausgedrückt. In dem Objekt werden dann die Parameter für den Helm Aufruf selbst, beispielsweise der zu nutzende Namespace, der zu nutzende Chart mit Namen, Repository und Version angegeben. Zusätzlich befindet sich auch die Konfiguration des Charts in dem Objekt: Die Helm-typischen Konfigurationsausprägungen als Values, die der jeweilige Chart anbietet. Für den Clusterzugriff wird noch die zu verwendende ProviderConfig referenziert.

Zur Demonstration wird das von Bitnami als Helm Chart bereitgestellte Wordpress verwendet. Weitere Informationen dazu finden sich hier: https://bitnami.com/stack/wordpress/helm und auf GitHub: https://github.com/bitnami/charts/tree/main/bitnami/wordpress/#installing-the-chart

Beispiel für Installation von Wordpress als Helm Chart durch den Crossplane Helm Provider
apiVersion: helm.crossplane.io/v1beta1
kind: Release
metadata:
  name: wordpress-example
spec:
  forProvider:
    chart:
      name: wordpress
      repository: https://charts.bitnami.com/bitnami
      version: 19.0.1
    namespace: wordpress
    skipCreateNamespace: false
    # Konfiguration im Format von values.yaml
    values:
      service:
        type: ClusterIP
    # Konfiguration als key-value Parameter
    set:
      - name: param1
        value: value2
  providerConfigRef:
    name: helm-provider

Nachdem das entsprechende Objekt erzeugt wurde wir der Crossplane Helm Provider aktiv und installiert das Chart.

Abfrage des Crossplane Helm Release Status
$ kubectl get releases
NAME                CHART       VERSION   SYNCED   READY   STATE   REVISION   DESCRIPTION   AGE
wordpress-example   wordpress   19.0.1    False                                             67s
...
NAME                CHART       VERSION   SYNCED   READY   STATE      REVISION   DESCRIPTION        AGE
wordpress-example   wordpress   19.0.1    True     True    deployed   1          Install complete   9m3s

Entsprechend wird der Namespace wordpress angelegt und in dem Namespace die zugehörigen Objekte des Wordpress Helm Charts erzeugt.

Fazit

Mit Crossplane lässt sich die flexible Kubernetes API nutzen, um darauf aufbauend DevOps Prozesse und Automatisierungen umzusetzen. Als Administrator profitiert man von der Einheitlichkeit der Werkzeuge und Konfigurationssyntax, wenn Kubernetes bereits im Einsatz ist.
Als Entwickler von Plattformlösungen bekommt man eine einheitliche Abstraktion samt API geliefert, die es ermöglicht darauf aufbauend mit entsprechend geringem Entwicklungsauwand eigene Lösungen zu schaffen.

Im Verlgeich zu Terraform oder Ansible kann Kubernetes mit dem bestehenden Berechtigungssystem zur Abstraktion und Trennung der Verantwortlichkeiten genutzt werden. Auf diese Weise ergibt sich eine attraktive Alternative zu Produkten wie Ansible Tower oder der Terraform Cloud.




Zu den Themen Kubernetes, Docker und Cloudarchitektur bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:

Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.

Feedback oder Fragen zu einem Artikel - per Twitter @triondevelop oder E-Mail freuen wir uns auf eine Kontaktaufnahme!

Los geht's!

Bitte teilen Sie uns mit, wie wir Sie am besten erreichen können.