Welcome to this exhilarating tutorial on how to deploy and use Quarkus in Kubernetes. Kubernetes is one of the open-source tools currently preferred when automating system deployments. It makes it easy to scale and manage containerized applications.
Kubernetes works by distributing workloads across the cluster and automating the container networking needs. Also, storage and persistent volumes are allocated, by doing so the desired state of container applications is continuously maintained.
Quarkus offers provides an easy way to automatically generate the Kubernetes resources based on some defaults and the user-provided configuration. This Kubernetes-native Java framework also provides an extension used to build and push container images to a registry before the application is deployed to the target. Another feature about Quarkus is that it enabled one to use the Kubernetes ConfigMap as a configuration source without mounting them on the pod.
The cool features associated with Quarkus are:
- Community and Standards: It provides a cohesive and fun-to-use full-stack framework by leveraging a growing list of over fifty best-of-breed libraries that you love and use
- Container First: It offers amazingly fast boot time, incredibly low RSS memory (not just heap size!) offering near-instant scale-up and high density memory utilization in container orchestration platforms like Kubernetes
- Unifies imperative and reactive: It allows developers to combine both the familiar imperative code and the reactive style when developing applications.
- Kube-Native: The combination of Quarkus and Kubernetes provides an ideal environment for creating scalable, fast, and lightweight applications. It highly increases the developer productivity with tooling, pre-built integrations, application services e.t.c
By following this guide to the end, you will learn how to:
- Use Quarkus Dekorate extension to automatically generate Kubernetes manifests based on the source code and configuration
- Build and push images to Docker registry with Jib extension
- Deploy an application on Kubernetes without any manually created YAML in one click
- Use Quarkus Kubernetes Config to inject configuration properties from ConfigMap
Let’s dive in!
Setup Pre-requisites
For this guide, you will require:
- Quarkus CLI
- Apache Maven 3.8.1+ (Optional)
- Access to a Kubernetes cluster
A Kubernetes cluster can be deployed with the aid of the guides below:
- Run Kubernetes on Debian with Minikube
- Deploy Kubernetes Cluster on Linux With k0s
- Install Kubernetes Cluster on Ubuntu using K3s
- Install Kubernetes Cluster on Rocky Linux 8 with Kubeadm & CRI-O
Once the cluster is running, install kubectl
curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin
Ensure that you can access the cluster.
# For k0s
export KUBECONFIG=/var/lib/k0s/pki/admin.conf
1. Install Quarkus CLI
The Quarkus CLI can be installed on Linux, macOS, and Windows (using WSL or bash compatible shell-like Cygwin or MinGW) by running the below commands:
curl -Ls https://sh.jbang.dev | bash -s - trust add https://repo1.maven.org/maven2/io/quarkus/quarkus-cli/
curl -Ls https://sh.jbang.dev | bash -s - app install --fresh --force quarkus@quarkusio
You can install it on Windows systems using the Powershell:
iex "& { $(iwr https://ps.jbang.dev) } trust add https://repo1.maven.org/maven2/io/quarkus/quarkus-cli/"
iex "& { $(iwr https://ps.jbang.dev) } app install --fresh --force quarkus@quarkusio"
Once installed, restart your shell.
The Quarkus CLI can also be installed using SDKMAN as below:
sdk install quarkus
2. Create a Project
Use the Quarkus command-line interface (CLI) to create a new project. The below command adds resteasy-reactive, Jib, and kubernetes dependencies.
quarkus create app quarkus-example --extension=resteasy-reactive,kubernetes,jib
cd quarkus-example
Sample Output:
After this, you will have several files generated, among these files is the pom.xml file bearing dependencies to the build file:
.........
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-jib</artifactId>
</dependency>
......
The good thing with Quarkus is that it generates Deployment/StatefulSet resources that it use your registry_username/test-quarkus-app:tag as the container image of the Pod. The image here is controlled by the Jib extension and can be customized using the application.properties as shown:
Open the file for editing:
vim src/main/resources/application.properties
Add the following lines replacing where required.
quarkus.container-image.group=registry_username
quarkus.container-image.name=tutorial-app
quarkus.container-image.tag=latest
quarkus.container-image.username=registry_username
quarkus.container-image.password=Your_registry -Password
If no registry has not been specified, the default, docker.io registry will be used. A detailed demonstration on specifying a registry has been captured elsewhere in this guide.
3. Build and Deploy your Application
Jib is used to build optimized images for Java applications without a Docker daemon and no need for the mastery of deep docker practices. Dekorate is a Java library that makes it simple to generate and decorate Kubernetes manifests. It generates manifests based on the annotations, source code, and configuration variables.
Now build and deploy your application using Quarkus CLI:
quarkus build -Dquarkus.container-image.push=true
Sample Output:
After the build process, you will have two files named kubernetes.json and kubernetes.yml under the target/kubernetes/ directory.
# ls target/kubernetes
kubernetes.json kubernetes.yml
Both files contain both the Kubernetes Deployment and Service. For example, the kubernetes.yml file looks like this:
# cat target/kubernetes/kubernetes.yml
---
apiVersion: v1
kind: Service
metadata:
annotations:
app.quarkus.io/build-timestamp: 2022-07-09 - 10:55:08 +0000
labels:
app.kubernetes.io/name: tutorial-app
app.kubernetes.io/version: latest
name: tutorial-app
spec:
ports:
- name: http
port: 80
targetPort: 8080
selector:
app.kubernetes.io/name: tutorial-app
app.kubernetes.io/version: latest
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
app.quarkus.io/build-timestamp: 2022-07-09 - 10:55:08 +0000
labels:
app.kubernetes.io/version: latest
app.kubernetes.io/name: tutorial-app
name: tutorial-app
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/version: latest
app.kubernetes.io/name: tutorial-app
template:
metadata:
annotations:
app.quarkus.io/build-timestamp: 2022-07-09 - 10:55:08 +0000
labels:
app.kubernetes.io/version: latest
app.kubernetes.io/name: tutorial-app
spec:
containers:
- env:
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: registry_username/tutorial-app:latest
imagePullPolicy: Always
name: tutorial-app
ports:
- containerPort: 8080
name: http
protocol: TCP
You will also have the image pushed to your registry. DockerHub for this example:
It is possible to generate a StatefulSet resource instead of the default Deployment resource via the application.properties;
quarkus.kubernetes.deployment-kind=StatefulSet
Now deploy the application to your Kubernetes cluster using any of the two manifests. For example:
kubectl apply -f target/kubernetes/kubernetes.yml
Verify if the deployment is up:
# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
tutorial-app 1/1 1 1 13s
# kubectl get pods
NAME READY STATUS RESTARTS AGE
tutorial-app-bc774dc8d-k494g 1/1 Running 0 19s
Check if the service is running:
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 31m
tutorial-app LoadBalancer 10.102.87.114 <pending> 80:30400/TCP 4m53s
Access the deployment using the provided port 30400. This can be done using a web browser with the URL http://IP_Address:30400/hello
Or from the terminal as shown:
$ curl 192.168.205.4:30400/hello
Hello from RESTEasy Reactive
This is the output of the file at src/main/java/org/acme/GreetingResource.java
4. Tuning the generated resources using application.properties
Quarkus allows one to tune the generated manifest using the application.properties file. Through this file, several configurations can be made. These include:
A. Namespace
Quarkus allows one to run the application in a chosen namespace. It omits the namespace in the generated manifest rather than enforcing it in the default namespace.
Therefore, you can run the application in the desired namespace say test using the command:
kubectl apply -f target/kubernetes/kubernetes.yml -n=test
Aside from specifying the namespace when running the Kubernetes command, you can still capture the namespace in the application.properties as shown:
quarkus.kubernetes.namespace=mynamespace
Replace mynamespace with the desired namespace for the application.
B. Defining a Docker registry
There are several other registries that can be defined. If left undefined, docker.io is used. If you want ot use another registry such as quay.io, then you need to specify it:
quarkus.container-image.registry=my.docker-registry.net
my.docker-registry.net is the registry you want to use.
C. Environment variables
There are several ways of defining variables on Kubernetes. These includes:
- key/value pairs
- import all values from a Secret or ConfigMap
- interpolate a single value identified by a given field in a Secret or ConfigMap
- interpolate a value from a field within the same resource
Environment variables from key/value pairs
To add environment variables from key/value pairs, use the below syntax:
quarkus.kubernetes.env.vars.my-env-var=foobar
This adds MY_ENV_VAR=foobar as an environment variable. my-env-var is converted to uppercase and the dashes are replaced with underscores to result in MY_ENV_VAR.
Environment variables from Secret
To add key/value pairs of Secret as environment variables, add the lines below to application.properties:
quarkus.kubernetes.env.secrets=my-secret,my-other-secret
This will result in the following in the container environment:
envFrom:
- secretRef:
name: my-secret
optional: false
- secretRef:
name: my-other-secret
optional: false
You can set the variable by extracting a value defined by keyName form the my-secret:
quarkus.kubernetes.env.mapping.foo.from-secret=my-secret
quarkus.kubernetes.env.mapping.foo.with-key=keyName
Resulting into:
- env:
- name: FOO
valueFrom:
secretKeyRef:
key: keyName
name: my-secret
optional: false
Environment variables from ConfigMap
Quarkus can be used to add key/value pairs from ConfigMap as environment variables. To achieve this, you need to add the lines below separating the ConfigMap to be used as a source by a comma. For example:
quarkus.kubernetes.env.configmaps=my-config-map,another-config-map
This will result into:
envFrom:
- configMapRef:
name: my-config-map
optional: false
- configMapRef:
name: another-config-map
optional: false
It is also possible to extract keyName field from the my-config-map by using:
quarkus.kubernetes.env.mapping.foo.from-configmap=my-configmap
quarkus.kubernetes.env.mapping.foo.with-key=keyName
This will generate a manifest with the below lines:
- env:
- name: FOO
valueFrom:
configMapRefKey:
key: keyName
name: my-configmap
optional: false
That is it!.
Books For Learning Kubernetes Administration:
Closing Thoughts
That summarizes this guide on how to deploy and use Quarkus in Kubernetes. I am sure that you are now in a position to generate the Kubernetes resources based on some defaults and the user-provided configuration using Quarkus. I hope this was valuable.
See more:
Perform security checks on Kubernetes manifests and Helm charts using Datree
Configure Pod Logging in Kubernetes using Sidecar container
How to create and use init containers in Kubernetes
Dynamic hostPath PV Creation in Kubernetes using Local Path Provisioner