Writing a Kubernetes Operator in Java: Part 2

July 2, 2019 by Fabian Staeber

Share this blog post on:

Part 2: Getting Started with the Quarkus Kubernetes Client Extension

This is the second Blog post in our series on writing a Kubernetes operator in Java. The first post gave a general overview of what an operator is. In this post, we show how to set up the a Quarkus application that interacts with the Kubernetes API server. In the third post we will add typical operator functionality, like watching a custom resource.

Initializing a Quarkus Project

Quarkus provides Maven archetypes for initializing projects. In order to create our project, we simply take the command from the official Quarkus getting started guide:

This command generates a hello world project. Running ./mvnw package will create an executable JAR file, which will run hello world service on http://localhost:8080/hello.

Creating a Native Executable

In addition to the default profile, the generated pom.xml defines an optional native profile that can be used to create a native executable:

./mvnw package -Pnative -Dnative-image.docker-build=true

The native profile uses GraalVM to compile Java to a native binary. If you don’t have GraalVM installed locally, you can use the -Dnative-image.docker-build=true flag as shown above. This makes maven download a GraalVM Docker image and use it to compile the project. That way, you don’t need to have GraalVM installed.

As a result, an executable ./target/operator-example-1.0-SNAPSHOT-runner is created. If you run it, it opens a hello world REST service on http://localhost:8080/hello.

Adding the Kubernetes Client Extension

GraalVM can be a bit tricky to use, because it is not 100% compatible with Java. If you add a random Maven dependency to a GraalVM project, there might be unexpected errors, both at compile time and at runtime.

Quarkus leverages this by providing extensions. Quarkus extensions are like Maven dependencies, but they are guaranteed to work in native mode. We will use the Fabric8 Kubernetes client to talk to the Kubernetes API server, but we will not add it as a regular Maven dependency, but as a Quarkus extension.

First, list all available extensions to make sure that quarkus-kubernetes-client is available:

The interesting part is: In order to learn the namespace we are running in, we read the magic file /var/run/secrets/kubernetes.io/serviceaccount/namespace. This works well if the operator runs within a Kubernetes cluster, because Kubernetes makes sure that this file exists in our container. However, if you intend to run the operator outside of a cluster, you should implement an alternative mechanism here.

The example we create in this Blog post will list all Pods and print the list of Pods to stdout. In order to print this list on startup, we put it in a method that @Observes the CDI StartupEvent.

Creating a Docker Image

First of all, we must use -DskipTests to compile the code above, because the tests will initialize all CDI beans, and our beans will try to get the namespace and list pods, which will fail outside the cluster.

./mvnw package -Pnative -DskipTests -Dnative-image.docker-build=true

This should create the executable file ./target/operator-example-1.0-SNAPSHOT-runner.

The next step is to create a Docker image for running this executable. We need to modify the generated ./src/main/docker/Dockerfile.native file to make that work.

The Kubernetes client talks to the Kubernetes API server via HTTPS. In order to do that, it needs a library called libsunec.so, implementing elliptic curve cryptography. This library is missing in the default ubi-minimal Docker image referenced in ./src/main/docker/Dockerfile.native.

libsunec.so is provided as part of GraalVM. After compiling the application with the Maven command above, you should already find a Docker image containing GraalVM in your local repository. The GraalVM image name is quay.io/quarkus/centos-quarkus-native-image. You can simply copy the library out of this docker image:

The demo lists all pods in the same namespace on startup. So it’s good to have some pods running to see something. For a quick test, just apply the replica set example at the top of the [previous Blog post]((https://www.instana.com/blog/writing-a-kubernetes-operator-in-java-part-1).

Once you have some Pods running, you are ready to deploy the operator. Create a file operator-example.deployment.yaml like this:

Summary

This Blog post showed how to get a Quarkus application using the Kubernetes client extension up and running. As an example interaction with the Kubernetes API server, we listed all Pods and printed the list of Pods to stdout.

The next post shows how to extend this example to add some real operator functionality, like watching a custom resource.

Start your FREE TRIAL today!

Full Stack Visibility in 5 minutes! Instana makes it easy to manage the performance of your applications.

About Instana: As the leading provider of Automatic Application Performance Monitoring (APM) solutions for microservices, Instana has developed the automatic monitoring and AI-based analysis DevOps needs to manage the performance of modern applications. Instana is the only APM solution that automatically discovers, maps and visualizes microservice applications without continuous additional engineering. Customers using Instana achieve operational excellence and deliver better software faster. Visit https://instana.com to learn more.