Step-by-Step Guide: Creating a Kubernetes Custom Resource Definition (CRD) and Controller

Crafting Kubernetes Magic

Step-by-Step Guide: Creating a Kubernetes Custom Resource Definition (CRD) and Controller

Photo by Growtika on Unsplash


Introduction ⚙️

Kubernetes is like a toolbox full of tools, but sometimes you need a special tool that isn't there. That's where Custom Resource Definitions (CRDs) come into play. They're like making your tool to get the job done. In this guide, we're going to build our special tool – a PdfDocument resource. It might sound fancy, but it's just a way to make PDFs using Kubernetes magic.

  1. Crafting PdfDocument - Oops, an Error!

    We start by making our PdfDocument. Here's how it looks:

     kind: PdfDocument
     metadata:
       name: my-document
     spec:
       documentName: "MyDocument.pdf"
       text: "Hello, this is a sample PDF document."
    

    Exciting, right? But guess what? There's a hiccup – an error message! Turns out we missed something called apiVersion. It's like an address for our PdfDocument, and we forgot to put it in the envelope.

  2. Solving the apiVersion Puzzle

    apiVersion might sound like tech lingo, but it's just telling Kubernetes where to find our PdfDocument. Think of it like GPS for your resource. We add it, try again, and boom – success! Our PdfDocument is now part of the Kubernetes club.

We've tackled our first challenge. A tiny missing piece caused a big fuss. The next part is where the real fun begins. We're going to give life to our PdfDocument using a custom controller. It's like teaching our tool some tricks. Get ready for some real Kubernetes magic! 🪄 🍵

Note: Remember, without a controller, a custom resource remains dormant and unfulfilled.


Levelling Up PdfDocument with a Custom Controller 🚀

Step 1: Set Up the Development Environment

Ensure you have the necessary tools installed:

  • Docker

  • Minikube or Kind (for a local Kubernetes cluster)

  • Go programming language

  • kubectl CLI tool

  • kubebuilder CLI tool (for scaffolding)


Step 2: Scaffold the Custom Resource and Controller

  • Initialize your project and create the PdfDocument API:

      mkdir your-controller-repo
      cd your-controller-repo
      go mod init k8s.example.com/v1
      kubebuilder init --domain example.com --repo your-controller-repo
      kubebuilder create api --group k8s.example.com --version v1 --kind PdfDocument
    

Step 3: Update the Custom Resource Spec

In your-controller-repo go to api/v1/pdfdocument_types.go, add the desired fields to the PdfDocumentSpec struct (e.g., DocumentName and Text).

// PdfDocumentSpec defines the desired state of PdfDocument
type PdfDocumentSpec struct {
    DocumentName string `json:"documentName,omitempty"`
    Text         string `json:"text,omitempty"`
}

Step 4: Implement the Controller Logic

In your-controller-repo go to controllers/pdfdocument_controller.go, implement the reconciliation logic using Reconcile method. This logic includes creating a Job to convert the Markdown text to a PDF.

Here's a concise and straightforward breakdown of the process using two init containers:

  1. Reading and Storing Text:

    • The first init container reads the text field from PdfDocument.

    • Encodes and saves it as a markdown (.md) file on a shared volume.

  2. PDF conversion:

    • The second init container uses a pandoc image for conversion.

    • Converts markdown file to a PDF, and stores on the same shared volume.

  3. Finalizing and Copying:

    • The main container sleeps, allowing PDF creation.

    • Copy the PDF from the shared volume to your local machine.

This smart orchestration automates text-to-PDF transformation, optimizing your custom PdfDocument resource in Kubernetes. Refer to the image above.


Step 5: Create and apply the Custom Resource Definition (CRD) File

After you execute the kubebuilder create api command, the Custom Resource Definition (CRD) for your PdfDocument resource will be automatically generated and placed in the config/crd/bases directory.

Your CRD definition YAML file pdfdocuments.yaml looks like this:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: pdfdocuments.k8s.example.com
spec:
  group: k8s.example.com
  scope: Namespaced
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        # openAPIV3Schema is the schema for validating custom objects.
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                documentName:
                    type: string
                text:
                    type: string
  names:
    plural: pdfdocuments
    singular: pdfdocument
    kind: PdfDocument
    shortNames:
    - pd
    - pdf
    - pdfs

Create the CRD by applying config/crd/bases/k8s.example.com_pdfdocuments.yaml using kubectl:

kubectl apply -f config/crd/bases/k8s.example.com_pdfdocuments.yaml

Then a new namespaced RESTful API endpoint is created at:

/apis/stable.example.com/v1/namespaces/*/pdfdocuments/...

Step 6: Build and Run the Controller

Before running the controller make sure to create the CRD.

make run

Step 7: Create a PdfDocument Resource

Now we can create a PdfDocument custom resource by applying a YAML file, e.g., pdfdocument-sample.yaml:

apiVersion: k8s.example.com/v1
kind: PdfDocument
metadata:
  name: my-document
spec:
  documentName: "MyDocument.pdf"
  text: "Hello, this is a sample PDF document."

Apply the custom resource:

kubectl apply -f pdfdocument-sample.yaml

You can then manage your CronTab objects using kubectl. For example:

kubectl get pdfdocument

When you delete a CustomResourceDefinition, the server will uninstall the RESTful API endpoint and delete all custom objects stored in it.

kubectl delete -f CustomResourceDefinition.yaml
kubectl get pdfdocuments
Error from server (NotFound): Unable to list {"stable.example.com" "v1" "pdfdocuments"}: the server could not
find the requested resource (get pdfdocuments.stable.example.com)

If you later recreate the same CustomResourceDefinition, it will start out empty.


Step 8: Verify Controller Behavior

Check logs and resources to ensure the controller and custom resource are functioning as expected:

kubectl logs -l control-plane=controller-manager -n your-controller-namespace
kubectl get pdfdocuments
kubectl describe pdfdocument my-document

Step 9: Access the Generated PDF

Locate and copy the generated PDF document from the pod to your local machine:

kubectl cp <pod-name>:data/mytext.pdf .
open mytext.pdf

Get the <pod-name> using kubectl. Here /data is the shared volume where the markdown files and generated pdfs are stored.


Conclusion💡

Congratulations, you've successfully created a custom resource definition and controller to manage PdfDocument resources in Kubernetes!

Reference link: Extend the Kubernetes API with CustomResourceDefinitions

Note: This guide provides a simplified overview and commands. Consider adding error handling, validation, and more advanced features for a production-grade implementation.

See you at the next one. Cheers!😁