What is Crossplane?
You can think of Crossplane as being a framework for building your own cloud native control plane or platform. You can do it declarative so you don’t have to write any code to make this happen.
But what is a Control Plane?
Google Cloud is a very good example of a control plane. They have been using control planes for years. If you ask for a cloud resource, in their backend services they have a control plane running to do all of the orchestration for the machines, storage, etc to provision and dynamically give you the services and resources you requested. Kubernetes is another example of a control plane. It orchestrates pods, applications, containers and so on.
Crossplane will help you build your own control plane and also allows you to put your own opinion into that control plane. Anything with an API could be managed by Crossplane. You can even order a Domino’s Pizza using Crossplane. Don’t believe it? Check it out 🍕
Before we get our hands dirty we need to learn a concept that will help us understand how Crossplane works.
A Managed Resource represents external objects in Kubernetes. On Google Cloud we have lots of things that we could use like Kubernetes Clusters (GKE), Databases (Cloud SQL), VPCs, PubSub and so on. We can represent those objects in Kubernetes YAML style. For example, if we want to represent a Google Cloud Storage Bucket using Crossplane we will do something like:
# Note that this will be the actual bucket name so it has to be globally unique/available.
We are describing what infrastructure resource from Google Cloud we want to create (a Bucket) and delegating Kubernetes to manage the state of that resource for us.
This gives us many advantages over traditional IaaC tools like Terraform. Nic Cope told us about one of the advantages in his article Crossplane vs Terraform. He states:
Terraform relies on a monolithic state file to map desired configuration to actual, running infrastructure. A lock must be held on this state file while configuration is being applied, and applying a Terraform configuration is a blocking process that can take minutes to complete. During this time no other entity — no other engineer — can apply changes to the configuration.
Collaboration scales in Crossplane because the Crossplane Resource Model (XRM) promotes loose coupling and eventual consistency. In Crossplane every piece of infrastructure is an API endpoint that supports create, read, update, and delete operations. Crossplane does not need to calculate a graph of dependencies to make a change, so you can easily operate on a single database, even if you manage your entire production environment with Crossplane.
This is just one of many advantages we can have using Crossplane. Read Nic’s article to find more about it. There are more concepts about Crossplane but this is not the scope of this article. In the future we are going to cover other concepts so stay tuned!!!
Enough talking lets get to the action.
I’m supposing you already have a local Kubernetes cluster in your machine with Helm installed. I’m using Minikube but you can user whatever you want (kind, k3d, etc).
Install Crossplane in your local cluster using Helm:
kubectl create namespace crossplane-systemhelm repo add crossplane-stable https://charts.crossplane.io/stablehelm repo updatehelm install crossplane --namespace crossplane-system crossplane-stable/crossplane
Check Crossplane Status to see if everything is ok:
helm list -n crossplane-system
Note that we are installing Crossplane in our local Minikube cluster. We need Crossplane running in an already created Kubernetes cluster in order to use Crossplane to create our GKE cluster. That’s a kind of a chicken and egg problem. How do we solve this? In a later post I will describe how to use Crossplane to manage the same cluster it is running on.
Creating a Service Account:
You will need to create a Service Account in order to make Crossplane communicate with your GKE cluster (the GKE cluster where you will CREATE your Cloud Resources):
The Service Account must be created with the following Roles:
Computer Network Admin
Kubernetes Engine Admin
Service Account User
You will also need to create a new JSON key for this newly created Service Account:
After you created the JSON key, download it to your computer.
Creating a Provider Secret:
Lets create a Provider Secret from the JSON Key you just downloaded from GCP:
cat > authentication.yaml <<EOF
credentials: $(base64 crossplane-credentials.json | tr -d "\\n")
P.S — I renamed the downloaded JSON key to crossplane-credentials.json
Apply the newly created authentication.yaml file:
kubectl apply -f authentication.yaml
Installing GCP Crossplane Provider:
Install the Crossplane CLI to extend kubectl functionality to build, push and install Crossplane packages:
curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | sh
Install the GCP Crossplane provider using the command below:
kubectl crossplane install provider crossplane/provider-gcp:v0.21.0
To see the latest GCP provider version go to: https://doc.crds.dev/github.com/crossplane/provider-gcp
Check to see if the provider is operational:
kubectl get providersNAME INSTALLED HEALTHY PACKAGE AGE crossplane-provider-gcp True True crossplane/provider-gcp:v0.21.0 41s
Note that INSTALLED and HEALTHY sections are True, so you are good to go.
Before creating any resources, we need to create and configure a GCP cloud ProviderConfig resource in Crossplane, which stores the cloud account information in it. All the requests from Crossplane to GCP will use the credentials attached to this ProviderConfig resource.
In this step we are configuring the Crossplane GCP provider to tell it where to get the authentication information we created on the step above.
cat > provider-config.yaml <<EOF
kubectl apply -f provider-config.yaml
Before building our GKE cluster we first need to define which network and subnet the cluster will use. Lets create a network.yaml file:
description: 'This is a network built by crossplane'
# make sure this matches the network name you defined in Network
kubectl apply -f network.yaml
Check to see if the network and subnet were created:
$ kubectl get network NAME READY SYNCED network True True$ kubectl get subnetwork NAME READY SYNCED subnetwork True True
READY and SYNCED sections are True. You can check your Google Cloud console and see that the network and subnet were created.
Now we have all the elements to create our GKE cluster. Create a gke-cluster.yaml file with the following content:
--- # API Reference: https://doc.crds.dev/github.com/crossplane/provider-gcp/container.gcp.crossplane.io/Clusterfirstname.lastname@example.org apiVersion: container.gcp.crossplane.io/v1beta2 kind: Cluster metadata: name: gke-crossplane-cluster spec: forProvider: initialClusterVersion: latest network: "projects/YOUR_PROJECT_ID/global/networks/network" subnetwork: "projects/YOUR_PROJECT_ID/regions/europe-west1/subnetworks/subnet" ipAllocationPolicy: useIpAliases: true defaultMaxPodsConstraint: maxPodsPerNode: 110 # By default, GKE allows up to 110 Pods per node on Standard clusters addonsConfig: cloudRunConfig: disabled: true loadBalancerType: LOAD_BALANCER_TYPE_UNSPECIFIED dnsCacheConfig: enabled: false gcePersistentDiskCsiDriverConfig: enabled: true horizontalPodAutoscaling: disabled: false httpLoadBalancing: disabled: false kubernetesDashboard: disabled: true networkPolicyConfig: disabled: false location: europe-west1 binaryAuthorization: enabled: false legacyAbac: enabled: false masterAuth: clientCertificateConfig: issueClientCertificate: false monitoringService: monitoring.googleapis.com/kubernetes providerConfigRef: name: crossplane-provider-gcp writeConnectionSecretToRef: name: gke-crossplane-cluster namespace: default ---# API Reference: https://doc.crds.dev/github.com/crossplane/provider-gcp/container.gcp.crossplane.io/NodePoolemail@example.com apiVersion: container.gcp.crossplane.io/v1beta1 kind: NodePool metadata: name: standard-pool spec: forProvider: autoscaling: autoprovisioned: false enabled: true minNodeCount: 1 maxNodeCount: 4 cluster: projects/YOUR_PROJECT_ID/locations/europe-west1/clusters/gke-crossplane-cluster config: serviceAccount: YOUR_SERVICE_ACCOUNT@YOUR_PROJECT_ID.iam.gserviceaccount.com diskSizeGb: 100 diskType: pd-ssd imageType: cos_containerd labels: team: platform cluster_name: gke-crossplane-cluster created_by: crossplane machineType: n1-standard-2 oauthScopes: - "https://www.googleapis.com/auth/devstorage.read_only" # is required for communicating with gcr.io - "https://www.googleapis.com/auth/logging.write" - "https://www.googleapis.com/auth/monitoring" - "https://www.googleapis.com/auth/servicecontrol" - "https://www.googleapis.com/auth/service.management.readonly" - "https://www.googleapis.com/auth/trace.append" - "https://www.googleapis.com/auth/compute" # is required for mounting persistent storage on your nodes. initialNodeCount: 1 locations: - europe-west1-c - europe-west1-d management: autoRepair: true autoUpgrade: true upgradeSettings: maxSurge: 1 maxUnavailable: 0 providerConfigRef: name: crossplane-provider-gcp
kubectl apply -f gke-cluster.yaml
This will take a while to be in a ready state. It took me around 20 minutes to create the Cluster. The time varies a lot. It could be more, it could be less than that.
Check to see if the cluster is running:
$ kubectl get cluster
NAME READY SYNCED STATE ENDPOINT LOCATION AGE
gke-crossplane-cluster True True RUNNING 100.111.7.99 europe-west1 25m
STATE is RUNNING so the cluster was created successfully.
In this tutorial I created the simplest GKE cluster possible using Crossplane. There are other ways of creating a Kubernetes cluster and other cloud resources using Compositions for example. But this is for another time. For now I’ll leave you with this.
I’m excited to see where Crossplane adoption will get. Infrastructure world these days changes very fast but I see potential in Crossplane to be the defacto tool to manage Infrastructure in a modern way. Judging by the buzz in the last Kubecon Europe 2022, Crossplane has a bright future. New providers are being built and use-cases are becoming more popular. If I’m right or wrong only time will tell.
Thanks for reading!!!