A sample Kubernetes controller written in Java with spring-boot. Most controllers are written in golang but the purpose of this repo is to show that it is possible to write a Kubernetes controller in Java as well. Writing a controller in Java instead of golang enables Java developers to learn about Kubernetes internals quicker since the language is not something they have to learn at the same time.
This controller defines and handles the lifecycle of Mysql
type custom resources. The custom resource is defined by a CRD registered with the cluster. This controller relies on built-in Kubernetes resources to compose a Mysql
resource. It creates the following child resources:
- A
Secret
which contains the login credentials to connect to the MySql instance. - A
StatefulSet
which runs a single Pod MySql instance. The pod is assigned cpu, memory, and disk resources according to theMysql
resource spec. - A
Service
which fronts the MySql pod with a consistent name. This way the clients connect to the stable name provided by the service instead of figuring out which pod to connect to directly.
In addition to creating the above child resources, the controller also makes sure that these resources continue to exist and are ready to use. The controller also keeps the status
section of Mysql
resource updated with the current state of the child resources.
Please visit: Kubernetes Controller Pattern Example with Java and MySQL. There is a lot more detailed information about what goes into creating a controller with Java, what concepts/classes (Client Machinery) are involved from the Kubernetes Client for Java library, and how to run the controller inside the cluster.
- Running Kubernetes cluster - a single node minikube instance will do
kubectl
and/ork9s
installed- Access to the cluster via
kubectl
- JDK 17
To run the sample, we need to register the CRD with cluster and then run this application so that it acts as the controller for these CRD objects. Apply the CRD so the cluster understands the Mysql
types of resources:
kubectl apply -f k8s/crd.yaml
./gradlew clean bootrun
First, we need to bundle the code into an OCI image. The following script assumes we want to run in a local minikube instance. It builds the image using the docker daemon running inside minikube. Run it to create an image called (fnjoin.com/mysql-controller-java:1.0
):
k8s/create-java-image.sh
Create the mysql-controller
namespace, where the controller will run:
kubectl apply -f k8s/namespace.yaml
Setup RBAC so the controller has permissions to run inside the cluster:
kubectl apply -f k8s/rbac.yaml
Deploy the controller as a Deployment
resource:
kubectl apply -f k8s/deployment-java.yaml
Once, the controller is running, we can test its functionality.
Apply one of the sample resource YAMLs to create a Mysql
instance with 256MB of storage, 128MB of memory, and 1/4 of a CPU:
kubectl apply -f k8s/sample1.yaml
This creates a db-1
resource. Verify it by running:
kubectl get mysqls
Which will return a list like the following:
NAME STORAGE MEMORY CPU READY
db-1 256Mi 128Mi 250m false
Notice the READY
column, it is an attribute set by the controller to indicate if the Mysql
resource is ready to be used or not.
If you want to see the details of the db-1
object, running kubectl get mysql db-1 -o yaml
will return something like:
apiVersion: fnjoin.com/v1
kind: Mysql
metadata:
creationTimestamp: "2022-11-01T21:41:32Z"
generation: 3
name: db-1
namespace: default
resourceVersion: "97078"
uid: 52a2abef-bfe8-433c-a2bf-37a40c6f70e4
spec:
cpu: 250m
memory: 128Mi
storage: 256Mi
status:
conditions:
- lastTransitionTime: "2022-11-01T17:41:31.957460-04:00"
status: AVAILABLE
type: Service
- lastTransitionTime: "2022-11-01T17:41:31.957475-04:00"
status: AVAILABLE
type: Secret
- lastTransitionTime: "2022-11-01T17:41:38.431363-04:00"
status: CREATING
type: StatefulSet
ready: false
The status
section is reserved for controllers like ours to update so that it informs us of inner details of what is happening with the resource. Notice that StatefulSet condition is in the status of CREATING and hence the MySql resource itself is not ready.
After some time, the StatefulSet created by this controller for db-1
will become ready and the controller will detect that and update this status.
The controller pays attention to all namespaces. If you create the Mysql
resource in any other namespace, the child resources will be created in that namespace.
For a bit of chaos testing, if you delete the corresponding StatefulSet
object, notice how the controller re-creates that child resource and how it keeps the status
object updated.
Also, notice what happens when you delete a Mysql
resource. You can do that by running kubectl delete mysql db-1
. Spoiler alert! the child resources will get cleaned up as well.
Don't use this in production !!! It is missing useful features like ability to configure read-replicas, automated backups, and other day-2 kind of concerns. There are much better alternatives available - like the MySQL Operator for Kubernetes from the MySQL team or VMware Tanzu SQL with MySQL for Kubernetes