Learn how Kubernetes Operators work from this example
Kubernetes (K8S) has become a very popular technology for working with distributed applications that run on the internet. K8S is intended to support an enormously large number of Linux containers that can run on hundreds if not thousands of virtual machines.
But, the power that Kubernetes provides comes with a good deal of complexity. An application running under Kubernetes has a lot of parts, which in K8S parlance are called resources and controllers. A resource is an object that serves a particular purpose. A controller is a piece of technology that manages the state of objects.
Kubernetes ships with a number of standard resources such as Pods, Deployments, Services, Namespaces, Secrets and ConfigMaps to name a few. There are others. Also, you can also create special resources that are custom to your use of Kubernetes. When you create a custom resource you might also have to create a custom controller to manage that resource.
Getting all these parts to play together nicely is a daunting task even for those accustomed to working with the Kubernetes. For the beginner it can be overwhelming. You have to know a lot even to create the simplest of applications.
Fortunately the Kubernetes community has come up with a technology that eases the burden of creating and deploying applications intended to run under K8S. This technology is the Kubernetes Operator. For the modern application developer, Operators are worth knowing about. Hence, understanding what Kubernetes Operators are at high level and how to use them is the purpose of this article
Be advised that in order to get full benefit from reading this article you should have some background with Kubernetes in general and the standard Kubernetes resources in particular. If you need a brush up on the basics, you can view my video on YouTube here. Also, you can read this excellent introductory article about Kubernetes on TechTarget here.
So, assuming that all you have the basic background knowledge, the place to start is by answering the essential question, what is a Kubernetes Operator?
What’s a Kuberenetes Operator?
A Kubernetes Operator is a technology that’s intended for encapsulating, deploying and managing a Kubernetes application.
As mentioned above, a Kubernetes application is made of a number of Kubernetes resources and controllers. Before Kubernetes Operators came along, in order to create a full fledged Kubernetes application, developers and sysadmins had to manage these resources manually or by way of automation scripts particular to the need at hand. Developers and sysadmins “operated” on a Kubernetes cluster in order to get an application up and running. The work was a bit tedious and as with any human activity, prone to error.
Kubernetes Operators simplify application management by encapsulating the breadth of tasks required to create and manage a Kubernetes application into a single methodology. Instead of developers having to declare a whole bunch of Kubernetes resources to construct an application, in many cases all they need to do is work with the single custom resource that ships as part of the operator. That single resource can represent a template from which an application can be realized. You can think of the operator as a blueprint from which you’ll implement a full-fledged application within the cluster.
The way Kubernetes Operator development works is that a developer will create a reusable Kubernetes Operator, which usually includes one or many custom resources along with the custom controller required to manage them. Then the developer passes the Operator off for others to use.
Once the Operator is injected into the cluster, developers using the Operator will create a manifest file that configures an instance of an application based on a custom resource particular to the Operator. Then, developers will apply the manifest file against the cluster.
The process is very similar to how a developer creates an instance of any other resource in Kubernetes. When the resource is a standard resource such as a pod, a service or a namespace, or whether the resource is a customer resource, the configuration and invocation processes are pretty much the same.
Now that we have the theory out of the way, let’s move on to actually using a Kubernetes Operator.
How do I use a Kubernetes Operator?
Kubernetes Operators has matured to the point where there is now a public repository where a company or open source project can post their Operators. The repository is called Operator Hub and can be accessed at OperatorHub.io. As of this writing there are over a hundred companies posting operators on Operator Hub.
The benefit of OperatorHub.io is that it provides Operators the work “right out of the box.” Developers can create an application based on an operator without having to go through the complexity of crafting an Operator from scratch. Analogically it’s the difference between making a pizza from one bought in the freezer case at a grocery store as opposed to making a pizza from scratch. You can think of an Operator as the frozen pizza.
In the demonstration I am going to show you how to create a database application using a Kubernetes Operator that is stored on OperatorHub.io. In this case we’ll use the Mariadb operator. But, before I go into the specifics of using the Operator, I’ll create an instance of a standard Kubernetes resource in order to give you a sense of the similarities between a standard resource and a custom resource as published by an Operator. The standard resource I will use is a Pod.
Take a look at Listing 1 below. It’s an example of a Kubernetes manifest file for creating a pod in a Kubernetes cluster. How do we know that the manifest describes a pod? Take a look at the second line. Notice the statement, kind:Pod. The declaration tells Kubernetes to create a resource of the kind: Pod.
apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80
Listing 1: The manifest file for creating a simple Kubernetes Pod that has an nginx container
Once the manifest file for the pod is defined, we execute the kubectl apply command to create the resource in the cluster. In this case, we’ll name the manifest file mypod.yaml. Thus, to create the resource in the cluster we invoke the following at the command line:
kubectl apply -f mypod.yaml
The resource will be created and the resulting output will be like so:
We’ve used a manifest file to create a Pod. Now let’s see how the pattern applies to working with the Kubernetes Operator.
Before we move on, the thing to keep in mind is that a Pod is a standard Kubernetes resource. Thus, we don’t have to inject it into the cluster because it’s already there by default. But, when it comes to an Operator, the Kubernetes cluster has no implicit understanding of the custom resource that’s part of the Operator. The custom resource needs to be injected into the cluster. Creating the particular custom resource is part of the work that goes with working with a Kubernetes Operator.
Let’s move on.
As mentioned above, we’re going to demonstrate how to work with a Mariadb Operator that’s available on OperatorHub.io. The Operator is available at https://operatorhub.io/operator/mariadb-operator-app. (See Figure 1.)
Installing the Operator is a two step process. First we install the Operator Lifecycle Manager (OLM) which is a tool that extends Kubernetes in order to support installing, managing and upgrading Operators running on a particular cluster. To install the OLM, execute the following command in the terminal you’re using to work with your particular Kubernetes cluster.
curl -sL https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.19.1/install.sh | bash -s v0.19.1
Next, we’re going to inject the actual Mariadb operator in the cluster by executing the following command:
kubectl create -f https://operatorhub.io/install/mariadb-operator-app.yaml
Notice in the above that the yaml file that describes the Operator is hosted on OperatorHub.io. This makes sense because OperatorHub is the central repository for the Mariadb operator.
Upon creating the operator you’ll see the following output:
Having executed the installation command shown above, the operator is now ready to go. But at this point it’s important to understand that there is no actual instance of Mariadb running. We’ve only created the custom resource and other related artifacts that are required for the MariaDB operator. We have not created the Mariadb application. In order to create the Mariadb application we need to create a manifest file such as the one shown below in Listing 2. In this case, we’ll name the manifest file, mymariadb.yaml.
apiVersion: mariadb.persistentsys/v1alpha1 kind: MariaDB metadata: name: mariadb spec: database: test-db username: db-user password: db-user rootpwd: password size: 1 image: 'mariadb/server:10.3' dataStoragePath: /mnt/data dataStorageSize: 1Gi
Listing 2: A manifest file for creating a running instance of Mariadb using an operator installed in the Kubernetes cluster.
Take a look at the second line in Listing 2, above. Notice the statement, kind:MariaDB. Look familiar? If you refer back to Listing 1, you’ll remember that the entry that defined the resource in the manifest file for the pod, was kind: Pod. In this case, in terms of the Mariadb operator, the resource is defined as, kind: MariaDB.
In order to get the application associated with the Operator up and running we execute the following command:
kubectl apply -f mymariadb.yaml
You’ll get the following output:
- mariadb.persistentsys is the one of the custom resources that’s part of the MariaDB Operator.
- mariadb is the name of the particular resource instance as defined in the manifest file.
As you can see, we implemented an instance of the Mariadb resource in the same way we implemented the Pod resource. The invocation pattern is identical.
Of course, the specifics of the manifest files are different. The Pod needs to know the Linux container image to use as well as the port on which the container runs internally. The MariaDB Operator declares other attributes such as the username, password, and rootpwd, to name a few.
The important thing to understand is that the Mariadb operator has an associated custom resource thatr represents a running instance of the Mariadb database within the Kubernetes cluster. That custom resource is in many ways a resource similar to the other resources that are standard within a Kubernetes cluster, at least in terms of using a manifest file to declare instances. Also, because the Mariadb operator can be conceptualized as a template upon which to base any number of application instances, I can create distinctly different instances of the Mariadb database running in the same Kubernetes cluster. The benefit that a Mariadb operator provides is that it alleviates the burden that would be incurred if I had to create a Mariadb database by hand.
There’s a lot of work that goes into creating a Mariadb application without the aid of an Operator, more than simply spinning up an instance of a Mariadb container. There’s storage, network, storage, security as well as a number of other settings that might need to be configured. Also, I’d need to create a Kubernetes service to expose the Pods running Mariadb. Fortunately, all this work goes away when using an Operator. The benefit is apparent.
Putting It All Together
Kubernetes Operators are an important step in the evolution of the Kubernetes ecosystem. They make it so that any application can be encapsulated for reuse and then deployed on demand within a Kubernetes cluster. Also, Operators make maintenance and upgrade activities a lot easier than the manually provisioned counterpart.
But, as with anything that has to do with Kubenetes, Operators are complex. It takes time to learn how to use them and much more time to learn how to make them. But, given that they alleviate a lot of the burden that goes with creating applications that run under Kubernetes, the time required to master Operators is an investment that will yield significant returns in the long run.