Quick Notes 📚
- Controllers are control loops that watch the state of your cluster & update the actual state of the cluster, examples below are core controllers managed by the Kubernetes controller manager in the control plane:
- Node controller: Responsible for noticing and responding when nodes go down.
- Job controller: Watches for Job objects that represent one-off tasks, then creates Pods to run those tasks to completion.
- ServiceAccount controller: Create default ServiceAccounts for new namespaces.
- Resources: are endpoints in the Kubernetes API.
- Custom resources: are extensions of the Kubernetes API that is not necessarily available in the default Kubernetes installation.
- Operators are application-specific controllers.
An operator is a type of controller that extends Kubernetes’ functionality to manage custom resources (CRDs - Custom Resource Definitions) specific to a particular application or workload. Unlike built-in controllers in Kubernetes, which handle standard resources like Pods, Deployments, and Services, operators are tailored to manage higher-level abstractions created by users for their applications.
While Golang is the dominant language in the Kubernetes ecosystem, there are other popular operators and frameworks for building operators in languages other than Go:
- Kopf : is a Python framework for building Kubernetes operators.
- Java Operator SDK: The Java Operator SDK is a framework for building Kubernetes operators using the Java programming language.
- NodeJS Kubernetes operator framework: The Operator Framework for building Kubernetes operators using JavaScript or TypeScript.
- kube-rs: is a Rust library for interacting with Kubernetes. It provides bindings to the Kubernetes API and can be used to build custom operators in Rust.
- dotnet-operator-sdk: The dotnet Kubernetes Operator SDK.
Operator’s Logic
In this post, we will walk through the process of building a simple operator using Kopf. The operator we’ll create is an alert operator that monitors the states of pods and generates alerts for pods that are not running. We will also include log trails to assist application developers in debugging.
Note:
Please note that this tutorial focuses on the core functionality of the operator. Although it can be extended to integrate with Slack webhooks to send alerts to specific channels, we will keep the tutorial simple and solely focus on the basic implementation.
By the end of this tutorial, you will have a solid understanding of how to build a basic operator using Kopf and leverage its capabilities to enhance your Kubernetes-based applications. Let’s get started!
Environment Setup
1 | minikube start --cpus 4 --memory 4096 |
Custom Resource Definition
Let us define a CRD (custom resource definition) for our alerting object:
1 | ## alert.yaml |
1 | kubectl apply -f alert.yaml |
The yaml above is a CustomResourceDefinition (CRD) resource in Kubernetes. Let’s break down the different sections:
- apiVersion:
apiextensions.k8s.io/v1
specifies the API version of the CustomResourceDefinition resource. - metadata: Contains metadata information about the CustomResourceDefinition resource.
- spec: Defines the specifications for the CustomResourceDefinition.
- scope:
Namespaced
, Specifies that the custom resource defined by this CRD will be namespaced, meaning it will be limited to a specific namespace. - group:
kopf.dev
: Specifies the group to which this custom resource belongs. - kind:
Orchestr
Specifies the singular name of the custom resource. In this case, the singular form is"orchestr".
- plural: orchestrs: Specifies the plural name of the custom resource. In this case, the plural form is
"orchestrs".
- openAPIV3Schema: Specifies the OpenAPI v3 schema for the custom resource.
x-kubernetes-preserve-unknown-fields
: Indicates that any unknown fields encountered in the custom resource should be preserved when reading or writing the resource.
Custom Resource Object
1 | #obj.yaml |
1 | kubectl apply -f obj.yaml |
Get a list of the existing objects of this kind with one of the commands:
1 | kubectl get Orchestr |
Output:
1 | apiVersion: kopf.dev/v1 |
Writing the Operator Logic
The following 3 core cause-handlers are available on Kopf, they keep track of the following
- When are object is created
- When an object is deleted
- When an object is updated
1 | import kopf |
For this post, we will just focus on the @kopf.on.create
kopf.on.create
is a decorator or function provided by the Kopf framework for defining handlers that are triggered when a new resource of a specific kind is created in Kubernetes.
1 | #main.py |
The code you provided is a Python script that uses the Kopf framework to build a Kubernetes operator. Let’s go through the code and explain its functionality:
- The script starts by importing necessary modules:
kubernetes
for interacting with the Kubernetes clusterkopf
for building the operatorApiException
from kubernetes.client.rest for handling API exceptions.
The @kopf.on.create('orchestrs')
decorator indicates that the following function is a handler for the creation of custom resources of kind'orchestrs'
. When a new resource of this kind is created, the create_fn function will be triggered.- Inside the
create_fn
function, it retrieves the value of thealerts.checkRunningPods
field from the custom resource’s spec. If this field is not set, it raises a kopf.PermanentError with an error message indicating that an alert must be set. - The
check_pod_status
function is called to check the status of all pods in the cluster. It retrieves the list of pods using the Kubernetes client and iterates over each pod to check if its status is not “Running”. If a pod is found with a status other than “Running”, it collects information about the pod, such as its name, namespace, status, and any issues related to its containers. These problematic pods are stored in the problematic_pods list. - The
get_pod_logs
function retrieves the logs for a specific pod in a given namespace. It uses the Kubernetes client to read the logs for the specified pod. If an ApiException occurs while retrieving the logs, it raises an exception with an appropriate error message.
Building an image for the Operator
1 | FROM python:3.7 |
1 | docker image build -t lindaindicina/alert-operator:latest . |
RBAC Rules
Operators often need RBAC (Role-Based Access Control) to perform various tasks within a Kubernetes cluster.
1 | #rbac.yaml |
1 | kubectl apply -f rbac.yaml |
Deploying the Operator
1 | #deploy..yaml |
1 | kubectl get pods |
Conclusion
The example discussed in this article serves as a simple introduction to using Kopf. It aims to provide beginners with valuable insights and a starting point for utilizing Kopf in their Kubernetes projects.
Hello đź‘‹, If you enjoyed this article, please consider subscribing to my email newsletter. Subscribe đź“