BlogTechnology and KnowHow

Crossplane - The New Kid in Town

Symphony logo
Symphony
June 20th, 2022

Are you passionate about Kubernetes as an open-source project? If the answer is yes, let me ask you what Kubernetes really is for you?

I assume that in your head, you are already saying the definition by the book - "it's an open-source container orchestration system for automating software deployment, scaling, and management.” If that is all that you think Kubernetes are, you've come to the right place as I try to convince you to think about it in a different way.

On the other hand, if you’ve never heard about Kubernetes, it's good to start by understanding what it's really all about and is it worth the hype.

Introduction

To begin with this huge topic, I will assume that many of you know the story of how Google operated containers a long time before they announced Kubernetes as an open-source project. If you aren't familiar with this topic, this is an excellent article to catch up on - Borg, Omega, and Kubernetes.

But, I will not go further on how Kubernetes became one of the main platforms for development. Instead, I'll jump into the main topic here, and that is my favorite, Crossplane! In this article, I would like to give my perspective on what Crossplane is all about and why you should start using it inside your organizations.

What is Crossplane?

In many online blogs, webinars, and podcasts, you will receive information that Crossplane is represented as a universal control plane.

As a Kubernetes add-on, it introduces a new way of building multiple custom blocks that enable your organization to provision, compose, and consume infrastructure utilizing the Kubernetes API.

This allows for the organization’s powerful separation of concern between different personas, meaning that each team member interacts with Crossplane at an appropriate level of abstraction.

Crossplane Terms

To dive deeper into this tool, I’ll provide some main terms you should be familiar with before we go further into the topic.

Firstly, each type of the Kubernetes resource following a PascalCase naming convention in which the first letter of each word is a compound word, is capitalized. For example,DeamonSet or PersistentVolumeClaim.

Crossplane follows this same convention as it runs on the Kubernetes itself. Note that Crossplane uses "X" as shorthand for "Crossplane" and/or "Composite". This is because Kubernetes has similar concepts - Custom Resources (CRs) and Custom Resource Definitions (CRDs), so when we talk about Crossplane, we use Crossplane Resource Definitions (XRDs) to avoid confusion.

Composition

Composition is a Crossplane feature that allows teams to define their opinionated platform APIs.

Composition refers to the key Crossplane API type that configures how Crossplane should compose those resources into a higher level of "compose resource.”

If we make a parallel with Terraform’s most known tool, we can think of a Composition as a Terraform module. There, we use HCL code to describe how it will take input variables and use them to create custom resources in some of the chosen cloud APIs.

Composite Resource

Composite resource or XR is an API type defined for using Crossplane. This resource is arbitrary - dictated by the concept of how the author wishes to expose this as an API, for example, a "SymphonyDB". A common convention is for types to start with X - e.g., "XSymphonyDB".

I mentioned that Crossplane is a tool that allows teams to define their own opinionated platform APIs. Those APIs are made up of composite resources, so when you're interacting with an API that your platform team has defined, you're interacting with composite resources.

We can think of it as the interface to a Composition. It provides the inputs composition uses to compose resources into a higher-level concept.

In Terraform, this is similar as tfvars file that supplies values for the variables that Terraform module uses to create resources in some cloud API.

Composite Resource Claim

A Composite Resource Claim, XRC, or just “a claim,” is also an API type defined using Crossplane. Each type of claim corresponds to a type of composite resource, and the pair have nearly identical schemes. As for the composite resources, the type of claim is arbitrary.

Let's assume that the platform team created XR and they are ready to offer it to the application teams they support. How do they do that? Well, that's achievable over the Claims, which act as an interface for the composite resource for the application teams.

Another approach Crossplane defines is that we can think of a claim as the public (app team) facing part of the opinionated platform API, while composite resources are the private (platform team) facing part.

The common convention for the claim is to be of the same type as its corresponding composite resource but without the "X" prefix. For example,  "SymphonyDB" would be the claim, and "XSymphonyDB" would be the corresponding type of composite resource. This allows application teams to use this composition without any knowledge of how to implement it, in this case, a database on targeted platform details.

Claims map to the same concepts as described above under the composite resource heading, i.e., tfvars files. Imagine that some tfvars files were only accessible to the platform team while others were offered to application teams; that’s the difference between a composite resource and a claim.

Composite Resource Definition

A “Composite Resource Definition” or “XRD” is the API type used to define new composite resources and claims types. Types of composite resources and claims exist because they were defined into existence by an XRD. The XRD configures Crossplane with support for the composite resources and claims that comprise a platform API.

They’re like the variable blocks in a Terraform module that define which variables exist, whether those variables are strings or integers, whether they’re required or optional, etc.

This is the topic that was the most challenging for me. At first, I thought that XRDs were the same as XRs, and they are definitely not! To bring this concept closer, we all know how Kubernetes CRDs and CRs work, right?

If you're familiar with this, you will easily grasp the  Crossplane tool and be ready to understand what to do next. For other folks struggling to understand it,  I'll briefly go through how native Kubernetes works.

CRDs allow us to define new CRs under our cluster. Defining this object will create a new custom resource with the name and scheme we specify. Kubernetes API serves and handles storage for our custom resource; one thing to mention is that the name of the CRDs object must be a valid DNS subdomain name.

If you want to know more about how this native scenario works, you can read more in these articles-  Extending the Kubernetes API and Use Custom Resources.

Now, let us go back into Crossplane. We mentioned the XRDs and XRs have the same flow as in the Kubernetes Server API.

As we will later create this for the Crossplane, I'll show you what it looks like in native Kubernetes,  as simple as possible:

apiVersion: apiextensions.k8s.io/v1

kind: CustomResourceDefinition

metadata:

  name: appconfigs.stable.example.com

spec:

  group: stable.example.com

  versions:

    - name: v1

      served: true

      storage: true

      schema:

        openAPIV3Schema:

          type: object

          properties:

            spec:

              type: object

              properties:

                image:

                  type: string

                replicas:

                  type: integer

  scope: Namespaced

  names:

    plural: appconfigs

    singular: appconfig

    kind: AppConfig

    shortNames:

    - ac

So let's dive deeper into what we created:

  • The first two lines define what the API version is, and we want to create a custom resource definition
  • The metadata field helps us define the name of the resource; in our case, it is appconfigs (plural)
  • Spec group helps us define what the group name will be
  • Spec version helps us determine the version. We are calling it v1, and we want this version to be our storage version
  • As you can see, we can define a version of our CRD, and only one version can be a storage version at a time, so keep that in mind. We then made sure that this CRD was namespaced and not cluster-wide. This allows us to create the CRD for either just a specific namespace or for the whole cluster
  • Next, we defined what the singular and plural name of our CRD will be
  • Lastly, we defined the kind name and the short name

Now, we can apply this CRD to our cluster; for example this kubectl create --filename appconfig-crd.yml:

The new namespaced RESTful API endpoint for our CRD will be found at:  /apis/stable.example.com/v1/namespaces/*/appconfigs/...

At this time, we have placed our CRD at the cluster side. To use this CRD we need to create a new manifest file using the kind that we created above. Let's assume that this is our manifest file e.g. appconfig-kind.yml

apiVersion: stable.example.com/v1

kind: AppConfig

metadata:

  name: demo-appconfig

spec:

  image: my-awesome-appconfig-image

  replicas: 1

Now we can apply this to our cluster and use it like kubectl create --filename appconfig-kind.yml

To see what is going on, we can run kubectl get ac --output yaml, and see detailed information on what we just created. Notice how we use the short name (ac) we defined in our CRD.

So to summarize, CRD is a way to extend Kubernetes, allowing us to create a custom resource of our choice and make it declarative with a custom controller’s help.

Managed resource

Managed resource represents a resource in an external system i.e., resources that Crossplane manages. Those resources enable platform teams to compose into higher-level XRs, creating an opinionated platform API.

So far, we have introduced a couple of terms used in the Crossplane ecosystem: composed resource, managed resource, and external Resource. To explain how they correlate, we will use our “SymphonyDB” as our managed resource. It represents within the Crossplane API, e.g., our AWS RDS instance.

Comparing this with an external resource, we compare Crossplane's representation of the “SymphonyDB” in the Kubernetes API and what Crossplane needs to orchestrate, like RDS instance in AWS’s API. It’s like Terraform resource blocks.

Package

We can extend Crossplane with support for new kinds of composite resources and claims or new kinds of managed resources. There are two types of packages: configurations and providers.

Configuration

Let’s assume we have chosen AWS for our targeted provider, where we created conceptually related groups of XRDs and compositions with custom configurations. What happens if we want to reuse this same setup?

In this case, we can create a configuration package for our opinionated platform API that Crossplane exposes. Next time, we need to re-use this same configuration package on the new cluster.

Provider

As with any other tool, to use cloud APIs, we must provide programmatic keys to communicate with that control plane endpoint. In that way,  we have access to controllers for managed resources; for example, the AWS provider installs support for AWS managed resources. Providers are directly analogous to Terraform providers.

Final Thoughts

For now, I believe we have created a good foundation for the next part of the Crossplane sessions, where we will use one of the providers and bring up the infrastructure from Kubernetes. Stay tuned!