Deploy Quarkus Todo List App to Kubernetes Using Eclipse JKube

Rohan Kumar
The Startup
Published in
9 min readOct 10, 2020

--

Note: This blog is a part of blog Series: Deploying Java applications onto Kubernetes using Eclipse JKube

These days almost everyone around me seems to be talking about how awesome Quarkus is in terms of startup time and memory consumption and how is it redefining Cloud Native Java Development on top of Kubernetes.

Today in this blog, we would be taking a look at how well Eclipse JKube integrates with Quarkus when it comes to bringing developer joy on top of Kubernetes.

Note: Quarkus has its own suggested way of deployment to Kubernetes. This blog post is purely from an outsider’s perspective.

Setting up Kubernetes Cluster:

We would be deploying one of the Quarkus TODO List Application onto Kubernetes using the tool. But first we need to make sure our Kubernetes Cluster is running. I am using minikube so I started it using this command:

minikube start

Once, minikube is running. We need build docker image in minikube. If you were using some other Kubernetes Cluster, you would need to build and push to some docker registry. You need to expose minikube VM’s internal docker daemon to your terminal session using the following command:

# To point your shell to minikube's docker-daemon, run:
# eval $(minikube -p minikube docker-env)

Getting Quarkus Todo List Application:

Now we’re all almost set with respect to deploying our Quarkus application onto Kubernetes. We would be using Clement Escoffier (Principal Software Engineer at Quarkus team)’s Todo List quickstart:

We’ll clone the project first:

git clone https://github.com/cescoffier/quarkus-todo-app.git

We’ll compile project afterwards(I’m skipping the test because it requires PostgreSQL instance running):

mvn clean install -DskipTests

Setting up PostgreSQL database in Kubernetes:

This application requires PostgreSQL, so we would need to deploy it into Kubernetes. You can either apply a PostgreSQL Deployment and PostgreSQL Service manually using kubectl create -f postgresql-deployment.yml or kubectl create -f postgresql-service.yml . In ideal scenario databases should be managed separately.

Eclipse JKube provides a way with which you can provide YAML manifests in src/main/jkube directory and Eclipse JKube would apply it during it’s resource apply phase. I will be providing these manifests in the resource fragment directory. Here is how it would look:

quarkus-todo-app : $ tree src/main/jkube/
src/main/jkube/
└── raw
├── postgres-configmap.yml # ConfigMap for storing DB creds
├── postgres-deployment.yml # PostgreSQL Deployment
└── postgres-service.yml # PostgreSQL Service
1 directory, 3 files

Don’t worry about the contents of these manifests. You can find them at my fork of this repository: rohankanojia/quarkus-todo-app.

Configuring Application to connect to PostgreSQL running in K8s:

Now that we have delegated database creation to Eclipse JKube, we need to configure our application to connect to database running inside Kubernetes Cluster rather than connecting locally.

In Kubernetes, when you create a Service , some environment variables related to that Service (like Host IP, Ports etc) are added to all the Pods in that specified namespace. For example, our PostgreSQL Service name is postgres , environment variable for Cluster level IP address of our Service would be added as an environment variable POSTGRES_SERVICE_HOST environment variable. You can find more about it in Kubernetes Service Docs. I would be modifying application.properties file to read this environment variable for PostgreSQL database host:

quarkus.datasource.url=jdbc:postgresql://${POSTGRES_SERVICE_HOST}/rest-crud

Deploying Application to Kubernetes:

Now that we have set up database creation and configured our application to use database running in Kubernetes. We’re all set to deploy it onto Kubernetes. We would be using Eclipse JKube’s Kubernetes Maven Plugin to build image, generate resource manifests and apply them onto Kubernetes Cluster:

quarkus-todo-app : $ mvn \
> org.eclipse.jkube:kubernetes-maven-plugin:build \
> org.eclipse.jkube:kubernetes-maven-plugin:resource \
> org.eclipse.jkube:kubernetes-maven-plugin:apply

This would build a Docker image in minikube’s docker daemon, generate an opinionated Deployment and Service and apply them to the Kubernetes Cluster. There is one problem, by default Service is of type ClusterIP which means it can’t be accessed from outside the cluster. So, I would configure Eclipse JKube to generate a NodePort Service instead:

quarkus-todo-app : $ mvn \
> org.eclipse.jkube:kubernetes-maven-plugin:build \
> org.eclipse.jkube:kubernetes-maven-plugin:resource \
> org.eclipse.jkube:kubernetes-maven-plugin:apply \
> -Djkube.enricher.jkube-service.type=NodePort

Here are logs of this run:

You can then check if your application’s Pod and Service are created and are in Running state. Your application might crash on first attempt if PostgreSQL pod is not running first, but don’t worry it would recover after restart:

quarkus-todo-app : $ docker images | grep todo
sample/todo-backend latest 7b18f278909a 26 minutes ago 540 MB
quarkus-todo-app : $ kubectl get pods
NAME READY STATUS RESTARTS AGE
postgres-c4995c67f-gcwrt 1/1 Running 0 48s
todo-backend-87b97f74b-xpqcf 1/1 Running 1 48s
quarkus-todo-app : $ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 58m
postgres NodePort 10.99.221.218 <none> 5432:31473/TCP 62s
todo-backend NodePort 10.108.60.227 <none> 8080:30043/TCP 62s

For me everything was running as expected. You can verify whether PostgreSQL Service environment variables are available in the pod with this command:

quarkus-todo-app : $ kubectl exec todo-backend-87b97f74b-xpqcf printenv | grep POSTGRES
POSTGRES_PORT=tcp://10.99.221.218:5432
POSTGRES_PORT_5432_TCP_PROTO=tcp
POSTGRES_PORT_5432_TCP_PORT=5432
POSTGRES_SERVICE_PORT=5432
POSTGRES_PORT_5432_TCP=tcp://10.99.221.218:5432
POSTGRES_PORT_5432_TCP_ADDR=10.99.221.218
POSTGRES_SERVICE_HOST=10.99.221.218

If everything is running okay, you should be able to access your application with minikube service command like this:

quarkus-todo-app : $ minikube service todo-backend
|-----------|--------------|-------------|-------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|--------------|-------------|-------------------------|
| default | todo-backend | http/8080 | http://192.1.39.7:30043 |
|-----------|--------------|-------------|-------------------------|
🎉 Opening service default/todo-backend in default browser...

This would open up your application in your default browser. Once opened, it should look like this:

Todo List Application running deployed to K8s with Eclipse JKube

And that’s it! There you have your Quarkus application deployed in Kubernetes in less than 10 seconds 😉.

Once you’re done testing it, you can cleanup creates resources using Eclipse JKube’s k8s:undeploygoal:

Undeploying your Quarkus Todo list App with Eclipse JKube

You can find repository configured with Postgres Kubernetes manifests and Eclipse JKube here at my fork:

Analyzing Memory Consumption:

Let’s try to add a /health endpoint to our application to see memory consumption of our application. I would introduce an additional model class HealthResource :

This would be consumed by HealthService for showing server health stats at /health endpoint:

Adding an additional `/health` controller

After adding these, I will compile the project again and run Eclipse JKube goals as usual(If you don’t want adding full plugin maven coordinates like me, you might want to add it to your pom.xml ):

quarkus-todo-app : $ mvn clean install...
quarkus-todo-app : $ mvn k8s:build k8s:resource k8s:apply

Once the application gets deployed, let’s check out the /health endpoint and see how much memory is being consumed by our application:

quarkus-todo-app : $ kubectl get pods
NAME READY STATUS RESTARTS AGE
postgres-c4995c67f-gcwrt 1/1 Running 0 48s
todo-backend-87b97f74b-xpqcf 1/1 Running 1 48s
quarkus-todo-app : $ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP
postgres NodePort 10.99.221.218 <none> 5432:31473/TCP
todo-backend NodePort 10.108.60.227 <none> 8080:30043/TCP
quarkus-todo-app : $ curl `minikube ip`:30937/health | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 118 100 118 0 0 39333 0 --:--:-- --:--:-- --:--:-- 39333
{
"availableProcessors": 1,
"freeMemory": "34.2 MB",
"maxMemory": "862.3 MB",
"totalMemory": "54.3 MB",
"usedMemory": "20.1 MB"
}

Cool, everything looks okay. Our application seems to be using only 20 MBof memory. Here I have totalMemory of 54.3 MB because I haven’t added any resource constraints. Let’s add some resource constraints to make memory consumption more strict.

In order to do that I will be defining resource quota inside the Deployment of our Quarkus Todo App. Right now Eclipse JKube generates an opinionated Deployment . But this can be configured easily. I would be using resource fragments approach; I will provide a small YAML fragment in my src/main/jkube directory like this:

quarkus-todo-app : $ cat src/main/jkube/deployment.yml 
spec:
template:
spec:
containers:
- resources:
limits:
memory: 20Mi
cpu: 100m
requests:
memory: 20Mi
cpu: 100m

This resource fragment would be merged with opinionated Deployment generated by Eclipse JKube during it’s resource generation phase. As you can see I have added some requests and limit constraints. I’m setting max memory to 20 MegaBytes and max CPU to 100 millicores.

I would undeploy my application created previously using mvn k8s:undeploy and I would issue Kubernetes Maven Plugin goals again:

$ mvn k8s:undeploy                     # Undeploy Application, again    
$ mvn k8s:build k8s:resource k8s:apply # Deploy to Kubernetes

You’ll notice that this time our application is crashing due to OutOfMemory issues, which means our application is taking memory more that 20 mega bytes:

quarkus-todo-app : $ kubectl get pods -w
NAME READY STATUS RESTARTS AGE
postgres-c4995c67f-7bl2s 1/1 Running 0 8s
todo-backend-b88c7dcd4-kphsz 1/1 Running 0 8s
todo-backend-b88c7dcd4-kphsz 0/1 OOMKilled 0 11s

No worries, it’s Java for you. We can get it to as small as within 20 megabytes but not smaller than this.

But that’s not all, Quarkus also offers a native mode with which you can build a executable binary using GraalVM.

Quarkus Native Image Mode:

If you check this sample, it already has a native profile. It has Quarkus Maven Plugin configured to run native-image goal. I would enable dockerBuild parameter to true. Here is how my configuration would look:

<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>native-image</goal>
</goals>
<configuration>
<enableHttpUrlHandler>true</enableHttpUrlHandler>
<dockerBuild>true</dockerBuild>
</configuration>
</execution>
</executions>
</plugin>

These is an issue with Quarkus that it’s only able to build native executable in docker mode with local docker daemon only. You would need to open another shell(your current shell might be configured to use minikube’s docker daemon instead) and compile project with native profile enabled:

$ mvn clean install -Pnative

Here are the logs of this run:

You’ll notice that this create a native executable file target/todo-backend-1.0-SNAPSHOT-runner . Let’s try to deploy native version of our application to Kubernetes with Eclipse JKube again. Before doing that, I would add a property for Eclipse JKube generator to use nativeImage mode in nativea:

<profile>
<id>
native</id>
...
<properties>

<jkube.generator.quarkus.nativeImage>
true
</jkube.generator.quarkus.nativeImage>
</properties>
</profile>

After adding the property, we can run Eclipse JKube goals as usual:

mvn k8s:build k8s:resource k8s:apply -Pnative

Here are logs of this run:

Check whether pods are running:

quarkus-todo-app : $ kubectl get pods -w
NAME READY STATUS RESTARTS AGE
postgres-c4995c67f-jmgmh 1/1 Running 0 3m55s
todo-backend-b88c7dcd4-z4b6f 1/1 Running 2 3m55s

You would notice that even after we defined a strict resource and limit constraints of 20 megabytes, our application is running fine(No OOMKilled crashes :-) ).

You can access your native mode application again with minikube service todo-backend :

quarkus-todo-app : $ minikube service todo-backend
|-----------|--------------|-------------|------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|--------------|-------------|------------------------|
| default | todo-backend | http/8080 | http://192.8.9.7:31009 |
|-----------|--------------|-------------|------------------------|
🎉 Opening service default/todo-backend in default browser...

and it runs just fine under restricted resource constraints:

Quarkus Application running in Native Mode

With this I conclude this blog. I hope someone would fine it useful and see how well Quarkus and Eclipse JKube complement each other. You can find all code related to this blog at my fork:

Come Join Us:

We value your feedback a lot so please report bugs, ask for improvements…​ Let’s build something great together!

If you are a Eclipse JKube user or just curious, don’t be shy and join our welcoming community:

--

--