Kubernetes Deployment

Kubernetes Extension

You need to finish the Creating containers section before going into Kubernetes section.

Deploying to Kubernetes with YAMLs

Deploying to Kubernetes is not done using kubectl create deployment approach at production, but creating and applying YAML files.

For example you can express a Deployment with the following deployment file:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: quarkus-demo-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: quarkus-demo
  template:
    metadata:
      labels:
        app: quarkus-demo
        env: dev
    spec:
      containers:
      - name: quarkus-demo
        image: quay.io/rhdevelopers/quarkus-demo:v1
        imagePullPolicy: Always
        ports:
        - containerPort: 8080

Likewise, you can register a service:

apiVersion: v1
kind: Service
metadata:
  name: the-service
spec:
  selector:
    app: quarkus-demo
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: LoadBalancer

The creation of these resources are cumbersome as in most of the services the content will be similar, to avoid having to repeat again and again the creation of the same files for all services, Quarkus comes with a Kubernetes extension.

Quarkus Kubernetes Extension

Let’s add the extension so Quarkus scaffolds automatcally Kubernetes deployment files when packaging the app.

Run the following command in the project directory:

./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-kubernetes"

With the extension added, run the following command to generate the Kubernetes resources:

./mvnw clean package -DskipTests

After running this command at the target/kubernetes directory, Quarkus places kubernetes.yaml and kubernetes.json files to deploy the application. One of the great things about the extension is the integration with container extension seen at Creating containers section, so image tag is pointing out to the image created there.

To deploy the application, we have two options, the first one, applying the resource manually:

kubectl apply -f target/kubernetes/kubernetes.yml
service/quarkus-app-workshop created
deployment.apps/quarkus-app-workshop created

Inspect the deployed pods and services:

kubectl get pods
NAME                                    READY   STATUS    RESTARTS   AGE
quarkus-app-workshop-5b45488995-kck2c   1/1     Running   0          3m5s
kubectl get services
NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
quarkus-app-workshop   ClusterIP   172.30.199.49   <none>        80/TCP    3m23s

Before showing the second way, undeploy the application:

kubectl delete all --all

The second way is at package time using the quarkus.kubernetes.deploy property. When it’s set to true, then Quarkus will create the container and push it (automatically setting quarkus.container-image.push property to true if not stated otherwise) to the repository and finally apply the generated resources.

In the terminal run the following command:

./mvnw clean package -DskipTests -Dquarkus.kubernetes.deploy=true
....
[INFO] --- quarkus-maven-plugin:2.7.5.Final:build (default) @ todo-app ---
[INFO] Checking for existing resources in: /Users/asotobu/git/kube-native-java-apps/apps/todo-app/src/main/kubernetes.
[INFO] [io.quarkus.kubernetes.deployment.KubernetesDeploy] Kubernetes API Server at 'https://api.sandbox.x8i5.p1.openshiftapps.com:6443/' successfully contacted.
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Starting (local) container image build for jar using jib.
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Base image 'registry.access.redhat.com/ubi8/openjdk-11-runtime:1.11' does not use a specific image digest - build may not be reproducible
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] LogEvent [level=INFO, message=trying docker-credential-desktop for quay.io]
[INFO] [io.quarkus.kubernetes.deployment.KubernetesDeploy] Kubernetes API Server at 'https://api.sandbox.x8i5.p1.openshiftapps.com:6443/' successfully contacted.
[INFO] [io.quarkus.kubernetes.deployment.KubernetesDeploy] Kubernetes API Server at 'https://api.sandbox.x8i5.p1.openshiftapps.com:6443/' successfully contacted.
[INFO] [io.quarkus.kubernetes.deployment.KubernetesDeploy] Kubernetes API Server at 'https://api.sandbox.x8i5.p1.openshiftapps.com:6443/' successfully contacted.
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] LogEvent [level=LIFECYCLE, message=Using credentials from Docker config (/Users/asotobu/.docker/config.json) for quay.io/rhdevelopers/quarkus-app-workshop:1.0.0-SNAPSHOT]
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Using base image with digest: sha256:0aca47bf03430b5ee5033d67ba9b38871470af00fa7d80a38c1cc0ae34270935
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Container entrypoint set to [java, -Djava.util.logging.manager=org.jboss.logmanager.LogManager, -jar, quarkus-run.jar]
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Created container image quay.io/rhdevelopers/quarkus-app-workshop:1.0.0-SNAPSHOT (sha256:da549c4785e018c8830c1d61f544372abedfd5896812f6f11eff20713adac7b4)

[INFO] [io.quarkus.kubernetes.deployment.KubernetesDeployer] Deploying to kubernetes server: https://api.sandbox.x8i5.p1.openshiftapps.com:6443/ in namespace: asotobue-dev.
[INFO] [io.quarkus.kubernetes.deployment.KubernetesDeployer] Applied: Service quarkus-app-workshop.
[INFO] [io.quarkus.kubernetes.deployment.KubernetesDeployer] Applied: Deployment quarkus-app-workshop.
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 11640ms

Application is packaged, containerized, pushed to container registry, and finally, deployed to configured cluster.

Inspect the deployed pods and services:

kubectl get pods
NAME                                    READY   STATUS    RESTARTS   AGE
quarkus-app-workshop-8547f6bbc-9gb5w   1/1     Running   0          33m
kubectl get services
NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
quarkus-app-workshop   ClusterIP   172.30.199.49   <none>        80/TCP    3m23s

Now that we have shown the second way, undeploy the application:

kubectl delete all --all

Customize auto-generated Kubernetes resources

Provide additional resources via YAML fragments

Sometimes it’s desirable to either provide additional resources, or provide custom values that will be used as a base for the generation process. Those resources can be added under src/main/kubernetes directory.

Suppose you want to change the default exposed port in the generated service from 80 (the default one) to 8080.

Create the src/main/kubernetes directory with a file named kubernetes.yml.

src/main/kubernetes/kubernetes.yml
apiVersion: v1
kind: Service
metadata:
  name: quarkus-app-workshop
spec:
  ports:
    - name: http
      port: 8080
      targetPort: 8080
  selector:
    app.kubernetes.io/name: quarkus-app-workshop
    app.kubernetes.io/version: 1.0.0-SNAPSHOT
  type: ClusterIP

Finally, regenerate the resources by executing the following command:

./mvnw clean package -DskipTests

If you inspect the target/kubernetes/kubernetes.yml content, the service port is 8080.

Overwrite generated Kubernetes resources using configurations

You can also overwrite the auto-generated Kubernetes resources by adding different configurations within application.properties:

quarkus.kubernetes.service-type=load-balancer(1)
quarkus.kubernetes.deploy=true(2)
quarkus.kubernetes.ingress.expose=true(3)
1 Generate a Kubernes Service of type LoadBalancer.
2 Deploy to Kubernetes every time the application is packaged.
3 Expose via Ingress the deployed resources.

If you inspect again the generated target/kubernetes/kubernetes.yml, you should notice that the Service type is changed and Ingress definition is present:

src/main/kubernetes/kubernetes.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    app.quarkus.io/build-timestamp: 2022-04-11 - 18:28:06 +0000
  labels:
    app.kubernetes.io/name: quarkus-app-workshop
    app.kubernetes.io/version: 1.0.0-SNAPSHOT
  name: quarkus-app-workshop
spec:
  rules:
    - http:
        paths:
          - backend:
              service:
                name: quarkus-app-workshop
                port:
                  name: http
            path: /
            pathType: Prefix