Kubernetes Custom Resource Definition

DevOps Archaeologist
4 min readJun 18, 2021

This scenario takes you through the essentials of defining new Kubernetes Custom Resource Definitions (CRDs). You will extend Kubernetes by creating new resources beyond the standard ones you normally get with any Kubernetes cluster. Once you understand CRDs, you will learn how to further extend Kubernetes using the powerful Operator pattern.

In this scenario you will learn how to:

  • Create a CRD and define custom attributes
  • Discover how your CRD becomes a new resource
  • Add, inspect, and remove instances of a custom resource

Kubernetes Extensibility: Custom Resource Definitions

a. verify your Kubernetes cluster is up and running for this LAB and the Helm package manager is installed

b. Kubernetes Dashboard :You can administer your cluster with the kubectl CLI tool or use the visual Kubernetes Dashboard. Use this script to access the protected Dashboard: token.sh

Create Simple CRD YAML

Let’s begin by inspecting a small example YAML file that declares a simple Thermometer: ccat thermometer-crd-minimal.yaml

In this CRD definition, the Kind is CustomResourceDefinition and the CRD is scoped to namespaces. We also provide the plural and short alias names for the same resource. Later we will define features that we can add to this specification. In its current form, it’s the most simplistic.

In this scenario we are using Kubernetes v1.18, so in the CRD definition we use apiVersion: apiextensions.k8s.io/v1. If you are using Kubernetes v1.16 or newer, you can use v1, which has a slightly improved CRD format.

Before we give this declaration to your cluster, let’s see what is currently on the cluster. There should be no preexisting CRDs: kubectl get crds

Now, submit the Thermometer CRD to Kubernetes:kubectl apply -f thermometer-crd-minimal.yaml

Notice the apply command was used instead of the create because we will be applying additional upgrades to the CRD in the subsequent steps. Kubernetes is now aware of this new resource type: kubectl get crds

Thermometer resources are now listed as a cluster api-resource:

kubectl api-resources --api-group='d2iq.com' -o wide

The resource REST management is also added as /apis/d2iq/ to the Kubernetes API:

kubectl get --raw / | jq . | grep -C3 d2iq

The REST API URL reveals the Thermometer resource:
kubectl get -v=9 --raw /apis/d2iq.com/v1beta1/thermometers/ | jq

Notice that in the last command, we added a verbosity request -v=9. With it set to level 9 (the highest) we get a bit more insight into how the kubectl command is obtaining the resource information.

The command also pipes the output to jq to pretty-print the JSON with syntax highlights.
The CRD definition just defines the resource type and while Kubernetes recognizes the type, there are no instances of the Thermometer resource type:

kubectl get trms

In the next step, let’s add a Thermometer based on this type.

***********************************************************************

Apply Simple Resource

Now that Kubernetes knows about this resource type, let’s add an actual resource. Create a YAML file to declare a Thermometer:

cat <<EOF > thermometer.yaml apiVersion: d2iq.com/v1beta1 kind: Thermometer metadata: name: simple-thermometer namespace: default EOF

The YAML file has been created. There is no data in it, just a name and namespace:

ccat thermometer.yaml

The CRD in the previous step instructed Kubernetes on what the resource kind: Thermometer means. Because of the CRD, this Thermometer declaration can be applied to your Kubernetes cluster:

kubectl apply -f thermometer.yaml

Now you have a new Thermometer resource. This is not a new Pod, Container, or Service; instead it is just a representation of a Thermometer:

kubectl get trm -A

The -A switch means find the specified resource across All namespaces.

With this, you see how to define and create custom resources. However, you can see this Thermometer type is lacking details that make a Thermometer unique, such as a geographic location, a digital metric, and its preferred units. We will expand on this in the next steps but before continuing, let’s remove this current definition.

Remove the resource:

kubectl delete trm -n default --all

Now, the resource is gone:

kubectl get trm -A

  • ************************************************************************

Apply Resource with Spec

In your current directory, there is a YAML file for a Thermometer for Stockholm Sweden:

ccat stockholm-thermometer.yaml

Notice the YAML has the name and namespace (as the location) and units defined. Create the namespace:

kubectl create namespace sweden

Apply the new resource declaration:

kubectl apply -f stockholm-thermometer.yaml --validate=false

Get the resource information:

kubectl get trm -A

This is as expected and consistent with the previous example. However, this Thermometer YAML has extra data declared:

unit: Celsius
example: 'not printed'

We had to pass in the --validate=false option just to get the controller to accept a YAML with the unknown data in it. The CRD controller will aggressively prune any unknown data that is not defined in the schema. Even though the YAML was not validated, the controller still pruned the extra data. This pruning behavior is welcomed because we want to prevent unknown data to lessen accidental or nefarious data getting into our cluster. We'll get into better schema validation in a moment. You can verify the unit and example data was pruned by inspecting the resource:

kubectl get trm stockholm -n sweden -o json | jq

Wouldn’t it be nice if the displayed output from the get command included the additional information defined in the resource details? In the next step, you can define the extra columns then improve the schema.

--

--