Difficulty: Beginner
Estimated Time: 30 minutes

How to design an Kubernetes/Openshift Operator using golang

In this scenario, you will understand the process of designing an operator for a demo product. Build an operator for your product can be challenging as we begin to learn about operators.

we will walk over the process to design a demo product operator. Which will give you a better understanding about implementating an operator for your product. After compeleting this scenario, you will be able to easily write an operator for your desired product or use-case.

Setup used in this scenario

About the Demo Product

I am using an out of the box Quarkus application. if you aren't familar with Quarkus or Java that's fine. You can follow allow the scenarios. Same process will be used even if you have different applications stack. Quarkus with some configurations makes easiers to generate templates i.e Openshif.yml/Kubernetes.yml. which includes DeploymentConfig, Service, ServiceAccount, Route, ImageStream, & BuildConfig etc.

quarkus application that runs on port: 8080 with a simple web page.

Image of the demo product

Dockerhub: https://hub.docker.com/repository/docker/akoserwal/demo-product

Image: akoserwa/demo-product:latest

Deploying any application on Kubernetes/Openshift will require these following templates.


ServiceAccount :

{
  "apiVersion" : "v1",
  "kind" : "ServiceAccount",
  "metadata" : {
    "annotations" : {
      "app.quarkus.io/build-timestamp" : "2020-09-17 - 12:41:03 +0000",
      "app.openshift.io/vcs-url" : "<<unknown>>"
    },
    "labels" : {
      "app.kubernetes.io/name" : "demo-product",
      "app.kubernetes.io/version" : "1.0.0",
      "app.openshift.io/runtime" : "quarkus"
    },
    "name" : "demo-product"
  }
}

Service

  "apiVersion" : "v1",
  "kind" : "Service",
  "metadata" : {
    "annotations" : {
      "app.quarkus.io/build-timestamp" : "2020-09-17 - 12:41:03 +0000",
      "app.openshift.io/vcs-url" : "<<unknown>>"
    },
    "labels" : {
      "app.kubernetes.io/name" : "demo-product",
      "app.kubernetes.io/version" : "1.0.0",
      "app.openshift.io/runtime" : "quarkus"
    },
    "name" : "demo-product"
  },
  "spec" : {
    "ports" : [ {
      "name" : "http",
      "port" : 8080,
      "targetPort" : 8080
    } ],
    "selector" : {
      "app.kubernetes.io/name" : "demo-product",
      "app.kubernetes.io/version" : "1.0.0"
    },
    "type" : "ClusterIP"
  }
}

DeployConfig

{
  "apiVersion" : "apps.openshift.io/v1",
  "kind" : "DeploymentConfig",
  "metadata" : {
    "annotations" : {
      "app.quarkus.io/build-timestamp" : "2020-09-17 - 12:41:03 +0000",
      "app.openshift.io/vcs-url" : "<<unknown>>"
    },
    "labels" : {
      "app.kubernetes.io/name" : "demo-product",
      "app.kubernetes.io/version" : "1.0.0",
      "app.openshift.io/runtime" : "quarkus"
    },
    "name" : "demo-product"
  },
  "spec" : {
    "replicas" : 1,
    "selector" : {
      "app.kubernetes.io/name" : "demo-product",
      "app.kubernetes.io/version" : "1.0.0"
    },
    "template" : {
      "metadata" : {
        "annotations" : {
          "app.quarkus.io/build-timestamp" : "2020-09-17 - 12:41:03 +0000",
          "app.openshift.io/vcs-url" : "<<unknown>>"
        },
        "labels" : {
          "app.kubernetes.io/name" : "demo-product",
          "app.kubernetes.io/version" : "1.0.0",
          "app.openshift.io/runtime" : "quarkus"
        }
      },
      "spec" : {
        "containers" : [ {
          "env" : [ {
            "name" : "KUBERNETES_NAMESPACE",
            "valueFrom" : {
              "fieldRef" : {
                "fieldPath" : "metadata.namespace"
              }
            }
          }, {
            "name" : "QUARKUS_OPTS",
            "value" : "-Dquarkus.http.host=0.0.0.0"
          }, {
            "name" : "JAVA_APP_JAR",
            "value" : "/deployments/demo-product-1.0.0-runner.jar"
          }, {
            "name" : "QUARKUS_HOME",
            "value" : "/home/quarkus/"
          } ],
          "image" : "akoserwa/demo-product:latest",
          "imagePullPolicy" : "IfNotPresent",
          "name" : "demo-product",
          "ports" : [ {
            "containerPort" : 8080,
            "name" : "http",
            "protocol" : "TCP"
          } ]
        } ],
        "serviceAccount" : "demo-product"
      }
    },
    "triggers" : [ {
      "imageChangeParams" : {
        "automatic" : true,
        "containerNames" : [ "demo-product" ],
        "from" : {
          "kind" : "ImageStreamTag",
          "name" : "demo-product:1.0.0"
        }
      },
      "type" : "ImageChange"
    } ]
  }
}

Route

{
  "apiVersion" : "route.openshift.io/v1",
  "kind" : "Route",
  "metadata" : {
    "annotations" : {
      "app.quarkus.io/build-timestamp" : "2020-09-17 - 12:41:03 +0000",
      "app.openshift.io/vcs-url" : "<<unknown>>"
    },
    "labels" : {
      "app.kubernetes.io/name" : "demo-product",
      "app.kubernetes.io/version" : "1.0.0",
      "app.openshift.io/runtime" : "quarkus"
    },
    "name" : "demo-product"
  },
  "spec" : {
    "host" : "",
    "path" : "/",
    "port" : {
      "targetPort" : 8080
    },
    "to" : {
      "kind" : "Service",
      "name" : "demo-product"
    }
  }
}


Demo Product Operator

We will create an operator for out demo product i.e quarkus application using above yaml configurations.

Steps we will follow:

  • Generate the operator using operator-sdk v1.0.0
  • Generate the API & Controller
  • Configure the options we want to provide which require custom input from the user. we will define under spec for the resouce: demo-product.
  • Convert the above DeploymentConfig template into deployment reconciler code.
  • Convert Service template into service reconciler code
  • Convert Route template into route reconciler code.
  • we will deploy our demo-product-operator in the Openshift.
  • Using the deployed operator we will create an instance of our product.

Finished: Well Done!

What did we learn

  • We learned how to create an operator using operator-sdk
  • We created a API end-point & controller
  • We learned to update the API:Spec & generated CRD
  • We learned how to add reconciler for Deployment, Service, & Route.
  • Finally of deploying the operator

This similar process can be used for the deployment of an operator for you own product

Contact Me

Abhishek Koserwal

Website: https://akoserwal.github.com Twitter: https://twitter.com/akoserwal Meduim: https://medium.com/@akoserwal

How to design a Kubernetes/Openshift Operator

Step 1 of 10

Step 1

Let's begin by creating a new operator called demo-product-operator.

Please wait until you see this message: You are ready to try it


Let's now create a new directory in our $GOPATH/src/ directory:

mkdir -p $GOPATH/src/github.com/demo-product-operator/


Navigate to the directory:

cd $GOPATH/src/github.com/demo-product-operator/


Create a new Go-based using Operator SDK project for the demo-product-operator:

operator-sdk init --domain=example.com --repo=github.com/akoserwal/demo-product-operator


Now we have generated our demo-product-operator. We need to design an API end-point for our demo product. This API end-point will be added to Kubernetes schema by our operator and we can perform operations like create, read, update, & delete on the resource defined in the Kubernetes/Openshift based on permission we grant.

Let's Generate an API & Controller for our operator

  • Version: v1
  • group: Cache
  • Kind: Demoproduct
  • Adding a controller for our Demoproduct API. Which will handle the logic to create or update the resoures.


API end-point exposed by our operator will look like: /apis/cache/v1/namespace/$NAMESPACE/demoproducts/: demoproduct

We will interact with this end-point within our controller to generate resource for our demo-product like: DeploymentConfig, Service, & Route etc.


Let's use the operator-sdk create api command to generate our API & Controller.

operator-sdk create api --group cache --version v1 --kind Demoproduct --resource=true --controller=true

We are all set to start adding logic for our demo-product.