Introduction à Crossplane
Crossplane permet de déployer des ressources d’infrastructure en se connectant aux APIs de différents providers.
Ces providers peuvent être des solutions cloud comme AWS, GCP, Azure, ou IBM Cloud, mais aussi des providers plus divers comme GitLab, Helm, ou autres… A ce jour, un peu moins d’une vingtaine de providers est utilisable.
Crossplane se pose comme une alternative à Terraform, avec toutefois les particularités suivantes :
- Crossplane spécifie les ressources à créer dans des manifestes Kubernetes,
- Crossplane gère la réconciliation en continu, en synchronisant en temps réel l’état réel avec l’état souhaité.
Dans cet article, nous allons mettre en place Crossplane et déployer quelques ressources sur Google Cloud Platform (GCP).
Avec Crossplane la description des ressources d’infrastructure s’effectue via des manifestes Kubernetes. La première étape consiste donc à déployer un cluster Kubernetes, puis installer Crossplane sur ce cluster.
Création du cluster k8s
Commençons donc par créer un cluster Kubernetes. Par souci de simplicité, nous déployons ce cluster sur un poste local avec la solution Kind :
kind create cluster --name crossplane --image kindest/node:v1.22.4 --wait 5m
kubectl cluster-info --context kind-crossplane
Mise en place de Crossplane
Déploiement de Crossplane sur le cluster Kubernetes
Nous déployons ensuite Crossplane à l’aide d’un chart Helm dans un namespace spécifique sur le cluster Kubernetes :
kubectl create namespace crossplane-system
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update
helm install crossplane --namespace crossplane-system crossplane-stable/crossplane
helm list -n crossplane-system
kubectl get all -n crossplane-system
Installation de la CLI crossplane en local
Crossplane propose également un client à installer sur le poste local :
curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | sh
./kubectl-crossplane --help
Configuration du provider cible
La notion de provider dans Crossplane est la même que dans Terraform.
Dans l’exemple ci-dessous nous allons déployer des ressources sur le provider GCP.
Installation des CRDs pour GCP
Nous installons tout d’abord les CRDs (Custom Resources Definitions) qui permettront de définir les types de ressources GCP que Crossplane sera capable de manipuler.
./kubectl-crossplane install configuration registry.upbound.io/xp/getting-started-with-gcp:latest
Vérification de l’installation :
kubectl get configuration
kubectl get pkg
Création d’un secret pour utiliser le provider
Crossplane se connecte à l’API du provider en utilisant un secret Kubernetes. Dans le cas de GCP, il faut tout d’abord créer un service account GCP avec les droits suffisants pour pouvoir déployer des ressources GCP, puis générer une clé pour ce service account.
Nous pouvons ensuite créer un secret kubernetes avec le contenu de cette clé :
kubectl create secret generic gcp-creds -n crossplane-system --from-file=creds=./gcp-credentials.json
Configuration du provider
Le fichier gcp-providerconfig.yaml
va nous permettre de déclarer la configuration pour le provider :
apiVersion: gcp.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
name: gcp
spec:
projectID: <my-project-id>
credentials:
source: Secret
secretRef:
namespace: crossplane-system
name: gcp-creds
key: creds
On fait manger le fichier à Kubernetes :
kubectl apply -f gcp-providerconfig.yaml
Vérification :
kubectl get providerconfig
Déploiement de ressources avec le provider GCP officiel
Maintenant que Crossplane est installé et que le provider GCP est configuré, nous pouvons tenter de déployer des ressources sur GCP.
La liste des ressources qu’il est possible de déployer avec le provider GCP officiel est disponible ici : https://doc.crds.dev/github.com/crossplane/provider-gcp@v0.21.0
Nous allons par exemple créer un bucket, en ajoutant le fichier my-crossplane-bucket.yaml
avec le contenu suivant :
apiVersion: storage.gcp.crossplane.io/v1alpha3
kind: Bucket
metadata:
name: my-crossplane-bucket
labels:
example: "true"
annotations:
crossplane.io/external-name: crossplane-4g5f6
spec:
location: EU
storageClass: MULTI_REGIONAL
providerConfigRef:
name: gcp
deletionPolicy: Delete
Vérification de l’état de la ressource my-crossplane-bucket
:
kubectl get bucket my-crossplane-bucket
NAME READY SYNCED STORAGE_CLASS LOCATION AGE
my-crossplane-bucket True True MULTI_REGIONAL EU 15m
Elle semble correctement synchronisée (SYNCED = True) et disponible (READY = True).
Vérifions côté GCP que le bucket est bien présent :
gcloud alpha storage ls
gs://crossplane-4g5f6/
Déploiement de ressources avec le provider GCP Jet
Le nombre de type de ressources actuellement déployable avec le provider GCP officiel (v0.21.0) est plutôt faible : 28. En effet l’ajout de nouveaux types de ressources est particulièrement couteux en temps de développement. Ceci est assez problématique, et on ne peut par exemple à ce jour même pas déployer une instance de VM avec ce provider !
Pour palier ce problème, la société Upbound travaille sur le projet Tarrajet. L’idée derrière Terrajet est de s’appuyer sur le travail énorme effectué ces dernières années par les équipes qui développent la solution Terraform pour générer automatiquement les CRDs Crossplane pour toutes les ressources que Terraform est capable de gérer.
Note du 09 janvier 2023 : attention Terrajet est désormais déprécié au profit de Upjet.
La page suivante présente la liste des providers “Jet” disponibles :
https://github.com/crossplane/crossplane/blob/master/docs/concepts/providers.md
En installant le provider GCP Jet, on passe de 28 types de ressources utilisables à 438 ! La liste est disponible là :
https://doc.crds.dev/github.com/crossplane-contrib/provider-jet-gcp@v0.2.0-preview
Installation des CRDs pour GCP Jet
./kubectl-crossplane install provider crossplane/provider-jet-gcp:v0.1.0
Vérification de l’installation :
kubectl get configuration
kubectl get pkg
Configuration du provider GCP Jet
Le nouveau fichier gcp-jet-providerconfig.yaml
va nous permettre de déclarer le nouveau provider Jet :
apiVersion: gcp.jet.crossplane.io/v1alpha1
kind: ProviderConfig
metadata:
name: gcp-jet
spec:
projectID: <my-project-id>
credentials:
source: Secret
secretRef:
namespace: crossplane-system
name: gcp-creds
key: creds
On référence le même secret que pour le provider GCP classique.
On fait manger le fichier à Kubernetes :
kubectl apply -f gcp-jet-providerconfig.yaml
Déploiement d’une instance de VM
Essayons maintenant de déployer une instance de VM…
La spécification de l’objet instance est disponible ici :
https://doc.crds.dev/github.com/crossplane-contrib/provider-jet-gcp/compute.gcp.jet.crossplane.io/Instance/v1alpha2@v0.2.0-preview
Créons le fichier my-crossplane-instance.yaml
avec le contenu suivant :
apiVersion: compute.gcp.jet.crossplane.io/v1alpha1
kind: Instance
metadata:
name: my-crossplane-instance
spec:
providerConfigRef:
name: gcp-jet
forProvider:
machineType: e2-small
zone: europe-west1-b
labels:
managed-by: crossplane
networkInterface:
- network: default
bootDisk:
- initializeParams:
- image: debian-cloud/debian-10
Vérification de l’état de la resource my-crossplane-instance
:
kubectl get instances
NAME READY SYNCED EXTERNAL-NAME AGE
my-crossplane-instance True True my-crossplane-instance 8m6s
Vérification de la présence de l’instance sur GCP :
gcloud compute instances list
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS
my-crossplane-instance europe-west1-b e2-small 10.132.0.36 RUNNING
Pour détruire l’instance, rien de plus simple, il suffit de supprimer la ressource my-crossplane-instance
du cluster Kubernetes :
$ kubectl delete -f my-crossplane-instance.yaml
instance.compute.gcp.jet.crossplane.io "my-crossplane-instance" deleted
Sur cet autre exemple, nous allons pousser deux clés ssh et un script d’init sur la VM à créer. Nous allons aussi lui associer une adresse IP publique. Modifions le fichier my-crossplane-instance.yaml
avec le contenu suivant :
apiVersion: compute.gcp.jet.crossplane.io/v1alpha1
kind: Instance
metadata:
name: my-crossplane-instance
spec:
providerConfigRef:
name: gcp-jet
forProvider:
machineType: e2-small
zone: europe-west1-b
labels:
managed-by: crossplane
networkInterface:
- network: default
accessConfig:
- {}
bootDisk:
- initializeParams:
- image: debian-cloud/debian-10
metadata:
ssh-keys: |-
user1:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAEAQDJ6TgFfAPfgQR8X9E6i user1
user2:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCmSIZRMBampHYZ7y9mV user2
# La taille maximum pour metadataStartupScript est 256 Ko
metadataStartupScript: |
echo 'test' > /test.txt
echo $(date) >> /test.txt
Créons la ressource :
kubectl apply -f my-crossplane-instance.yaml
instance.compute.gcp.jet.crossplane.io/my-crossplane-instance created
kubectl get instances
NAME READY SYNCED EXTERNAL-NAME AGE
my-crossplane-instance True True my-crossplane-instance 15m
Et vérifions que le script a bien été exécuté au lancement de la VM :
ssh user1@IP-PUBLIQUE
sudo cat /test.txt
test
Thu May 5 15:34:37 UTC 2022
Composition
Crossplane permet d’aller encore bien plus loin avec la notion de Composition, à découvrir dans la vidéo ci-dessous…