miércoles, enero 30, 2019

DRY with Kubernetes Operator Framework


An Operator is a method of packaging, deploying and managing a Kubernetes application. To make it, you use Kubernetes API to generate and deploy the application. You can think about a way of extending Kubernetes resources to be custom to your needs. This enables us to not have to repeat the same resource configuration every time but just the things that are different.

An example of an operator might be the deployment of service of your microservice architecture. For your service, you need to create a deployment file where you specify a lot of parameters such as the container, the deployment information, environment variables to configure the service, liveness and readiness probes, ... Then when you need to release a new version of this service, you just take this deployment file, you specify everything again but with one small change (the version part of docker image is updated). So everything is exactly the same except one field (the version part) and you apply the resource to Kubernetes cluster. Why you need to repeat everything every time you want to release a new version when the only important thing is the version number?

An Operator let you fix this. You specify all common part as a custom resource (write once) and then for every new version of the service you only need to create a new resource of kind custom resource, with only the uncommon part set, in our example the version number.

In a big simplification (I repeat a big simplification) you can think about Operators as a way to create a template with some dynamic values that are set in creation time. The biggest difference with a template is that the common content ("template") is created programmatically by the operator so you've got the freedom to change the resources dynamically.

Apart from that, with an Operator, you use Kubernetes API to decide when and how to deploy each of the resources.

Let's start with a really simple example which might help you understand how powerful is Operators and why you should start using them.

Suppose that I have one simple service which prints to console a message. This message is set in the command line section. So the resource file to deploy this service might look like:

As you can see it is fairly simple, but what's happen if now you want to deploy a new version of the service which instead of printing "Hello Alex", it prints "Hello Soto"? Well, you just need to create a new file which is exactly the same but changing the command line part. But instead of doing this, let's create an operator where you only need to specify the message to print, and the release of the service "just happens".

What you need:

To create an operator, for this guide you need:
Installing and booting up Minishift:

Minishift installation instructions can be found at (https://docs.okd.io/latest/minishift/getting-started/installing.html). After installation just run to prepare the cluster:

Creating the operator:

The first thing to do is prepare the layout for the Operator. Since we are going to create the Operator in Go, you need to create it in your GOPATH directory:

Then we need to add a new custom resource definition to this project, which will be responsible for defining how our custom resources look like:

We are generating a custom resource definition for a custom type called Hello.

Then we need to define the parameters that you want to set to the custom resource. These are the parts that you want to be different in every deployment. Let's say the version number or the message to print.

So let's define the spec and the status object.

Open pkg/apis/hello/v1alpha1/hello_types.go and add next fields:

You define in HelloSpec struct a field called Message that will contain the message you want to be printed when the container is started.

Then you need to run next command to update the generated code: operator-sdk generate k8s

Last part regarding code is to generate a controller which will be responsible for watching and reconciling our Hello resource. So run next command:

operator-sdk add controller --api-version=hello.lordofthejars.com/v1alpha1 --kind=Hello

The important file created after running this command is at pkg/controller/hello/hello_controller.go and in the next snippet you can see the important bits for our example:

Reconcile method reads the state of the cluster for our Hello object and makes any changes based on the sate and what is in the spec object.

The next important piece is the method newPodForCR which is a custom method that generates programmatically the resource that we want to use. You can think about it as the template, where you define the schema, and you use the Hello kind to fill the empty spaces. Notice that in this method there is the cr variable which is used to get the values from the custom resource. Let's modify this method to adapt to our requirements.

Installing the Operator:

Then you need to install the custom resource to the cluster, build the Operator Docker image and push it to Docker registry:

After that, you need to update the operator's definition to use the created image. Open deploy/operator.yaml and change REPLACE_IMAGE tag to lordofthejars/hello-operator:v0.0.1

And finally, we just need to create all Operator resources into the cluster:

If you run kubectl get pods now, you'll see the Operator deployed in the cluster (hello-operator-6d5559b65f-5zjg2   1/1       Running   0          25s).

Now that we have everything in place, it is time to see it in action. Create next file:

This is our resource where we are only specifying the message to be printed.

And finally, run oc/kubectl apply -f deploy/crds/hello_v1alpha1_hello_cr.yaml

Then you can check the log message by running oc/kubectl logs example-hello-pod

To remove the resource, you just need to do as usually oc/kubectl delete -f deploy/crds/hello_v1alpha1_hello_cr.yaml 

Now just update the hello_v1alpha1_hello_cr.yaml file to another message and apply the resource again. See the logs and boom the new message is printed.

So notice that now we are not doing a copy-paste anymore, we just create a file with the configurable parts, and that's all, everything else is managed by the operator.


This is a really simple example, but you get the idea of how powerful is Operators and how they can simplify the way you deploy applications on Kubernetes.

We keep learning,

I don't know why you're not fair, I give you my love, but you don't care, So what is right and what is wrong?, Gimme a sign (What is Love - Haddaway)

Music: https://www.youtube.com/watch?v=HEXWRTEbj1I
Follow me at https://twitter.com/alexsotob