Build a Kubernetes Operator in 10 minutes with Operator SDK

It's fairly straightforward to create and deploy a custom Operator and custom Kubernetes types using the Operator SDK. Here's how.
137 readers like this.
Ships at sea on the web

In Kubernetes, objects are analogous to a job or a completed task in the real world. You can use them to define common tasks, store them in a version control system, and apply them with kubectl apply. Kubernetes ensures that this triggers everything necessary to bring your declarative description to life by creating the depending resources (like pods) to run your software. Kubernetes contains a number of built-in object types that can be created with this workflow, like Deployments and Services.

With Operators, Kubernetes allows cluster maintainers or software providers to define their own Kubernetes object types, called custom resource definitions (CRDs). These objects can be handled by the Kubernetes API, just like built-in object types. Inside the Operator code, authors can define how to act on those custom objects.

The Operator user can use kubectl apply to create an object of this custom type, which is called a custom resource (CR).

There are many different uses for CRs, from deploying a few pods to more complex applications. Imagine running kubectl -f postgres.yml and getting a high-availability PostgreSQL cluster ready for use.

This article will use a Kubernetes Operator to deploy a simple application consisting of one config map and one pod. In the CR, you can specify a Markdown string that will be transformed into an HTML5-based presentation. (After all, who doesn't want to prepare the slides for their next talk using kubectl edit presentation my-presentation, as you'll do in this tutorial?)

If you're not already familiar with running a Kubernetes cluster, read Getting started with Minikube: Kubernetes on your laptop to make it easier to follow along with this how-to.

Install the Operator SDK

Before I begin, I'll assume you are already logged into the Kubernetes cluster you want to use. You must also have Golang 1.13 or later.

Install the latest version of Operator SDK for your computer from the Operator SDK releases page on GitHub, then make it executable and put it into your PATH as operator-sdk:

$ wget https://github.com/operator-framework/operator-sdk/releases/download/v0.15.2/operator-sdk-v0.15.2-x86_64-linux-gnu
$ sudo mv operator-sdk-v0.15.2-x86_64-linux-gnu /usr/local/bin/operator-sdk
$ sudo chmod +x /usr/local/bin/operator-sdk

Bootstrap a new Kubernetes Operator

Now you're ready to get started with the Operator development! The operator-sdk binary can be used to generate the boilerplate code common to many different Operators. This enables every Operator author to focus on developing their own logic that differentiates it from other Operators, instead of reinventing the Operator logic over and over again. To generate the boilerplate code, run the following command in a folder where you want to create the Operator. Operator SDK will generate a folder with the name of the Operator you specify:

$ cd ~/operators
$ operator-sdk new presentation-example-operator --type go --repo github.com/NautiluX/presentation-example-operator

Point the repo argument to the repository you want to use for your Go module. This command will download some dependencies, create the folder presentation-example-operator, and create a basic project setup. Next, generate some Go code to represent your custom resource definition (the "API" of your Operator):

$ cd presentation-example-operator
$ operator-sdk add api --kind Presentation --api-version presentation.example.com/v1alpha1

This command specifies that the CRD will be called Presentation and creates the file pkg/apis/presentation/v1alpha1/presentation_types.go, which you can modify to specify the input parameter of your CRD. For this example application, you need only one parameter, called Markdown:

...
type PresentationSpec struct {
	// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
	// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
	// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html

	// This field can be used to define your presentation in markdown. Use --- to separate slides.
	Markdown string `json:"markdown,omitempty"`
}
...

Note that the only things different here are a comment for the field and the declaration of the Markdown field. The rest of the file is generated by Operator SDK and can be adjusted to your needs.

Now, generate the actual CRDs from this definition and a bit of Go code. To do so, run the following commands:

$ operator-sdk generate crds
$ operator-sdk generate k8s

In deploy/crds/presentation.example.com_presentations_crd.yaml, you will find the CRD, which you can install on your cluster using the following command:

$ kubectl apply -f deploy/crds/presentation.example.com_presentations_crd.yaml

The Operator will run a so-called reconcile-loop, which ensures a piece of code is executed every time a CR from an Operator's CRDs is created. This piece of code lives in a controller. Every CRD the Operator wants to operate on will have a corresponding controller in the operator. In this case, you only need one controller, as you only have a single CRD. It's common, though, to have multiple CRDs and multiple controllers packaged in a single Operator. To create a controller for your newly created CRD, run the following command:

$ operator-sdk add controller --kind Presentation --api-version presentation.example.com/v1alpha1

Now you can add your own logic to the function Reconcile in the generated file pkg/controller/add_presentation.go. This function will be called every time a Presentation CR is created, changed, or deleted. Whenever it returns an error, the same request will run another round through that function.

After it is generated, it contains example code that creates a pod, which you can adjust to fit your needs. You can find the full implementation of the called functions in the Git repository of this example Operator. This code shows the workflow in the Reconcile function:

...
	configMapChanged, err := r.ensureLatestConfigMap(instance)
	if err != nil {
		return reconcile.Result{}, err
	}
	err = r.ensureLatestPod(instance, configMapChanged)
	if err != nil {
		return reconcile.Result{}, err
	}
	return reconcile.Result{}, nil
...

First, it ensures a ConfigMap with the current Markdown exists. Then, it ensures a pod is created with the latest version of the ConfigMap mounted.

Iterate quickly: Run the Operator outside the cluster

The Operator can be deployed to the cluster, just like any other application. However, for quick iterations during development, it is handy to run it on your local machine. That way, you can see the output right away and can stop it by simply pressing Ctrl+C. To start the Operator, run the following command:

$ operator-sdk run --local

Now you can create your first presentation from a separate terminal and watch the Operator do its work:

$ kubectl apply -f <(echo "
apiVersion: presentation.example.com/v1alpha1
kind: Presentation
metadata:
  name: example-presentation
spec:
  markdown: |
    # My First Presentation
    ---
    ## Test slide

    * Test
    ---
")

This will spin up a pod and create a config map, as you defined it in the Go code above:

$ kubectl get pods
NAME                       READY   STATUS    RESTARTS   AGE
example-presentation-pod   1/1     Running   0          103s

$ kubectl get configmaps
NAME                          DATA   AGE
example-presentation-config   1      104s

To access this little application and see your slides, you need to port-forward port 80 from inside the pod. In a real application, you could create a service resource from the Operator code:

$ kubectl port-forward example-presentation-pod 8080:80

Now you can see your presentation by navigating your browser to http://localhost:8080. Whenever you update the Markdown (for example, using kubectl edit presentation example-presentation), the page will be updated after the pod has been redeployed (and you will need to restart port-forwarding again).

Deploy the Operator

To deploy the Operator, you need to create a container image that can be accessed by your Kubernetes cluster. Luckily, Operator SDK contains tooling to create this image. After building, you can push the image with the Docker CLI:

$ operator-sdk build manueldewald/presentation-example-operator
$ docker push manueldewald/presentation-example-operator

Now, you need to adjust the deployment config that was previously generated by the Operator SDK in deploy/operator.yaml:

$ sed -i 's|REPLACE_IMAGE|manueldewald/presentation-example-operator|g' deploy/operator.yaml

Finally, you can deploy the Operator using kubectl apply -f deploy/operator.yml, and it will start running inside the cluster. Now you can try to change your presentation or deploy a new one.

Conclusion

It's fairly straightforward to create and deploy a custom Operator and custom Kubernetes types using the Operator SDK.

If you like the idea of creating slides using Markdown, take a look at Remark.js, which was used in this example Operator. You don't need to run a Kubernetes cluster to use it; opening a simple HTML file will be enough. But if you want to present from your Kubernetes cluster for any reason, now you know how to do it.

What to read next
Tags
User profile image.
Manuel completed his studies in applied computer science with a Master's degree in 2013 in Heidelberg, Germany and started working as a software developer shortly thereafter. He is interested in working with and combining all kinds of technology to build new cool things, striving to make lives (including his own) easier.

4 Comments

Quite complicated the implementation but will definitely try it sometime in the coming week. Thanks for sharing :)

I totally agree

Hey Manuel, Thanks for such a nice presentation on operators. Can we try this operator on the OpenShift environment also?

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.