An Introduction to Operators — Part 1

Imagine it’s a warm day. You arrive home and set the thermostat to 18 degrees Celsius. Your air-conditioning unit kicks in and starts adjusting the temperature to reach the desired temperature. If you examine what you just did, you set the desired state in terms of specifying a temperature. The air-conditioning unit then observed that a state change was required and it’s controller started taking the necessary actions to bring down the temperature to the desired state.

At it’s core, kubernetes operates in a very similar way. Let’s have a look at the key components in kubernetes:

Control Plane: The Control Plane in kubernetes is a collection of components that keep your kubernetes cluster up and running. It’s responsible for managing the objects that you deploy, changing the state of these resources based on API calls and persisting the state for recoverability.

API Server: The entry point for users to interact with the kubernetes cluster is via APIs. These APIs are processed by the API server, who in turn coordinates with the other components to query or change the state of objects.

etcd: etcd is the key-value datastore that is used by the control plane to store the metadata, configuration and state (current and desired) of the cluster.

controller manager: There are two controller managers in kubernetes. The kube-controller-manager is a daemon process that runs the controllers (control loops) necessary to maintain the cluster in a consistent state. The cloud-controller-manager runs controllers specific to the cloud provider/hyperscaler on which your cluster is running.

scheduler: The scheduler assigns newly created pods to nodes.

kubelet: The kubelet is an agent that runs on worker nodes and manages containers running on pods.

There are additional components that are not described here e.g. kube-proxy, container runtime, etc. as they are not relevant to the discussion on Operators. Detailed information on kubernetes components can be found here

kubectl: kubectl is the command line interface provided by kubernetes to call kubernetes APIs. kubectl works in two modes — imperative and declarative. Imperative commands specify how to perform an operation on the cluster. e.g. kubectl create / delete / replace. Declarative commands specify the desired state of the cluster and kubernetes decides how to achieve the desired state. e.g. kubectl apply -f config.yaml

Now that we’ve looked at the core kubernetes components, let’s look at operators. The term operator comes from the CoreOS blog by Brandon Philips published in 2016. The core tenet in the blog states that an operator is software that contains the “application’s operational domain knowledge” programmed into it.

An operator extends the kubernetes platform natively and consists of a custom controller and custom resource definitions. A custom resource definition is a way of adding new resources types to a kubernetes cluster. By default, kubernetes understands several resource types — nodes, pods, services, deployments, replicasets, etc. A special resource type — customresourcedefinitions (CRDs) allow you to extend the kubernetes APIs by creating your own resource type. The concept of resource comes from REST APIs and essentially refers to an object of a particular type that has its own data and a set of methods that can operate on it. The kubectl CLI, when used in imperative mode, typically has the syntax of

kubectl <method> <object> <parameters>

Let’s take an example of creating a custom resource- crontab. We create a yaml file- resourcedefinition.yaml with the following content

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
# name must match the spec fields below,
# and be in the form: <plural>.<group>

name: crontabs.stable.example.com
spec:
# group name to use for REST API: /apis/<group>/<version>

group: stable.example.com
# list of versions supported by this CustomResourceDefinition
versions:
- name: v1
# Each version can be enabled/disabled by Served flag.
served: true
# One and only one version must be
# marked as the storage version.
storage: true
# either Namespaced or Cluster
scope: Namespaced
names:
# plural name to be used in the URL:
# /apis/<group>/<version>/<plural>

plural: crontabs
# singular name to be used as an alias on
# the CLI and for display
singular: crontab
# kind is normally the CamelCased singular type.
# Your resource manifests use this.
kind: CronTab
# shortNames allow shorter string to
# match your resource on the CLI
shortNames: - ct

We apply the custom resource definition

kubectl apply -f resourcedefinition.yaml

This creates the custom resource definition in kubernetes. Kubernetes now knows how to create a crontab object. You can get the custom resource definition using the following command

kubectl describe crd crontabs.stable.example.com

Let’s create a crontab object called prod-crontab. First, we create prod-crontab.yaml with the following content

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
name: prod-crontab
spec:
cronSpec: "* * * * */5"
image: prod-crontab-image

Then we apply it to our cluster using

kubectl apply -f prod-crontab.yaml

At this point we have an object prod-crontab of type crontab created.

If the intent of using a custom resource is to just store configuration information that is used by other application components, then we can stop here. The object is created and the information related to the object is available via the kubernetes API. What we’ve accomplished is to create a custom resource type, create custom objects and provide access to the object through an extension of the kubernetes API.

For us to implement operators, we will see how to create a custom controller for the crontab resource type in the next post.

Child of the pure unclouded brow, and dreaming eyes of wonder! Ever seeking, never satisfied, always learning, teaching, mentoring, coaching.