Cloud-Native Java: Mastering Quarkus Kubernetes Extension

Article

Quarkus Kubernetes extension provides a declarative approach to generating and deploying Kubernetes resources for your Java applications. It automatically generates manifests, handles configuration, and simplifies the entire Kubernetes deployment workflow, making it ideal for building cloud-native Java applications.


What is Quarkus Kubernetes Extension?

The Quarkus Kubernetes extension is a set of tools that automatically generates Kubernetes manifests (YAML files) from your Quarkus application code and configuration. It follows the "compile-time" philosophy of Quarkus, generating optimal Kubernetes resources during build time.

Key Benefits for Java Developers:

  • Automatic Manifest Generation: No manual YAML writing required
  • Configuration as Code: Kubernetes configuration in application.properties
  • Health Checks: Automatic liveness/readiness probe configuration
  • Service Discovery: Automatic service and ingress generation
  • Security: Built-in security context and RBAC generation
  • Multi-Platform Support: Kubernetes, OpenShift, Knative

Setup and Dependencies

1. Maven Configuration

<!-- pom.xml --> <project> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>quarkus-kubernetes-app</artifactId> <version>1.0.0</version> <properties> <quarkus.platform.version>3.6.0</quarkus.platform.version> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> <dependencies> <!-- Quarkus Core --> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-arc</artifactId> </dependency> <!-- RESTEasy Reactive for HTTP --> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive</artifactId> </dependency> <!-- Kubernetes Extension --> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-kubernetes</artifactId> </dependency> <!-- Health Checks --> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-smallrye-health</artifactId> </dependency> <!-- Metrics --> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-micrometer-registry-prometheus</artifactId> </dependency> <!-- Configurations --> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-kubernetes-config</artifactId> </dependency> <!-- Service Binding --> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-kubernetes-service-binding</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-bom</artifactId> <version>${quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>

2. Gradle Configuration

// build.gradle plugins { id 'java' id 'io.quarkus' } repositories { mavenCentral() } dependencies { implementation 'io.quarkus:quarkus-arc' implementation 'io.quarkus:quarkus-resteasy-reactive' implementation 'io.quarkus:quarkus-kubernetes' implementation 'io.quarkus:quarkus-smallrye-health' implementation 'io.quarkus:quarkus-micrometer-registry-prometheus' implementation 'io.quarkus:quarkus-kubernetes-config' implementation 'io.quarkus:quarkus-kubernetes-service-binding' } quarkus { setOutputDirectory("$projectDir/build/classes/java/main") }

Basic Configuration

1. Application Properties Configuration

# application.properties # Application Configuration quarkus.application.name=my-quarkus-app quarkus.application.version=1.0.0 # Kubernetes Configuration quarkus.kubernetes.deployment-target=kubernetes quarkus.kubernetes.namespace=my-namespace # Container Configuration quarkus.kubernetes.image-pull-policy=IfNotPresent quarkus.kubernetes.image-registry=docker.io quarkus.kubernetes.image-group=my-organization quarkus.container-image.build=true # Service Configuration quarkus.kubernetes.service-type=ClusterIP quarkus.kubernetes.ingress.expose=true quarkus.kubernetes.ingress.host=my-app.example.com # Health Checks quarkus.kubernetes.liveness-probe.path=/q/health/live quarkus.kubernetes.readiness-probe.path=/q/health/ready quarkus.kubernetes.liveness-probe.initial-delay=30 quarkus.kubernetes.readiness-probe.initial-delay=5 # Resource Configuration quarkus.kubernetes.resources.requests.memory=256Mi quarkus.kubernetes.resources.requests.cpu=250m quarkus.kubernetes.resources.limits.memory=512Mi quarkus.kubernetes.resources.limits.cpu=500m # JVM Configuration quarkus.kubernetes.env.vars.JAVA_OPTS=-Xmx256m -Xms128m quarkus.kubernetes.env.vars.QUARKUS_PROFILE=production # Replica Configuration quarkus.kubernetes.replicas=3

2. Basic Quarkus Application

package com.example.quarkus; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; @Path("/api") public class GreetingResource { @GET @Path("/hello") @Produces(MediaType.APPLICATION_JSON) public String hello() { return "{\"message\": \"Hello from Quarkus Kubernetes!\"}"; } }

3. Health Check Endpoints

package com.example.quarkus.health; import org.eclipse.microprofile.health.HealthCheck; import org.eclipse.microprofile.health.HealthCheckResponse; import org.eclipse.microprofile.health.Liveness; import org.eclipse.microprofile.health.Readiness; import jakarta.enterprise.context.ApplicationScoped; @Liveness @ApplicationScoped public class LivenessHealthCheck implements HealthCheck { @Override public HealthCheckResponse call() { return HealthCheckResponse.up("Quarkus Kubernetes App - Liveness"); } } @Readiness @ApplicationScoped public class ReadinessHealthCheck implements HealthCheck { @Override public HealthCheckResponse call() { return HealthCheckResponse.up("Quarkus Kubernetes App - Readiness"); } }

Advanced Kubernetes Configuration

1. Complete Kubernetes Configuration

# src/main/resources/application.properties # ===== BASIC APPLICATION CONFIG ===== quarkus.application.name=order-service quarkus.application.version=2.1.0 # ===== KUBERNETES DEPLOYMENT CONFIG ===== quarkus.kubernetes.deploy=true quarkus.kubernetes.deployment-target=kubernetes quarkus.kubernetes.namespace=production # ===== CONTAINER IMAGE CONFIG ===== quarkus.container-image.build=true quarkus.container-image.push=true quarkus.container-image.registry=ghcr.io quarkus.container-image.group=my-organization quarkus.container-image.name=order-service quarkus.container-image.tag=latest quarkus.container-image.username=${CONTAINER_REGISTRY_USERNAME} quarkus.container-image.password=${CONTAINER_REGISTRY_PASSWORD} # ===== DEPLOYMENT CONFIG ===== quarkus.kubernetes.replicas=3 quarkus.kubernetes.part-of=order-management-system quarkus.kubernetes.management.port=9000 # ===== SERVICE CONFIG ===== quarkus.kubernetes.service-type=ClusterIP quarkus.kubernetes.service.ports.http=80 quarkus.kubernetes.service.ports.target-http=8080 # ===== INGRESS CONFIG ===== quarkus.kubernetes.ingress.expose=true quarkus.kubernetes.ingress.host=orders.mycompany.com quarkus.kubernetes.ingress.class-name=nginx quarkus.kubernetes.ingress.annotations.kubernetes\\.io/ingress\\.class=nginx quarkus.kubernetes.ingress.annotations.nginx\\.ingress\\.kubernetes\\.io/rewrite-target=/ # ===== HEALTH CHECK CONFIG ===== quarkus.kubernetes.liveness-probe.http-action-path=/q/health/live quarkus.kubernetes.liveness-probe.initial-delay=30 quarkus.kubernetes.liveness-probe.period=10 quarkus.kubernetes.liveness-probe.failure-threshold=3 quarkus.kubernetes.liveness-probe.success-threshold=1 quarkus.kubernetes.readiness-probe.http-action-path=/q/health/ready quarkus.kubernetes.readiness-probe.initial-delay=5 quarkus.kubernetes.readiness-probe.period=5 quarkus.kubernetes.readiness-probe.failure-threshold=3 quarkus.kubernetes.startup-probe.http-action-path=/q/health/started quarkus.kubernetes.startup-probe.initial-delay=0 quarkus.kubernetes.startup-probe.period=5 quarkus.kubernetes.startup-probe.failure-threshold=30 # ===== RESOURCE CONFIG ===== quarkus.kubernetes.resources.requests.memory=512Mi quarkus.kubernetes.resources.requests.cpu=500m quarkus.kubernetes.resources.limits.memory=1Gi quarkus.kubernetes.resources.limits.cpu=1000m # ===== ENVIRONMENT VARIABLES ===== quarkus.kubernetes.env.vars.JAVA_OPTS=-Xmx512m -Xms256m -XX:+UseG1GC quarkus.kubernetes.env.vars.QUARKUS_PROFILE=production quarkus.kubernetes.env.vars.LOG_LEVEL=INFO quarkus.kubernetes.env.vars.DATABASE_URL=postgresql://db-host:5432/orders # ===== SECURITY CONTEXT ===== quarkus.kubernetes.security-context.run-as-user=1001 quarkus.kubernetes.security-context.run-as-non-root=true # ===== LABELS AND ANNOTATIONS ===== quarkus.kubernetes.labels.app=order-service quarkus.kubernetes.labels.version=2.1.0 quarkus.kubernetes.labels.team=backend-team quarkus.kubernetes.annotations.owner=java-team quarkus.kubernetes.annotations.description="Order Management Microservice" # ===== PDB CONFIG ===== quarkus.kubernetes.pdb.min-available=2 # ===== SERVICE ACCOUNT ===== quarkus.kubernetes.service-account-name=order-service-account

2. Multi-Container Pod Configuration

# Multi-container application.properties quarkus.kubernetes.sidecars.logging-agent.image=fluent/fluent-bit:2.1 quarkus.kubernetes.sidecars.logging-agent.env-vars.FLUSH_INTERVAL=1 quarkus.kubernetes.sidecars.logging-agent.ports.http.port=2020 quarkus.kubernetes.init-containers.db-migration.image=postgres:15 quarkus.kubernetes.init-containers.db-migration.commands[0]=/bin/sh quarkus.kubernetes.init-containers.db-migration.commands[1]=-c quarkus.kubernetes.init-containers.db-migration.commands[2]=until pg_isready -h $DB_HOST; do sleep 5; done

3. ConfigMap and Secret Configuration

# ConfigMap and Secrets configuration quarkus.kubernetes.config-maps.app-config.enabled=true quarkus.kubernetes.config-maps.app-config.mount-path=/app/config quarkus.kubernetes.secrets.db-secret.enabled=true quarkus.kubernetes.secrets.db-secret.mount-path=/app/secrets # Environment variables from ConfigMap quarkus.kubernetes.env.configmaps=app-config quarkus.kubernetes.env.secrets=db-secret # Specific fields from ConfigMap quarkus.kubernetes.env.mapping.DB_HOST.from-configmap=app-config quarkus.kubernetes.env.mapping.DB_HOST.with-key=database-host quarkus.kubernetes.env.mapping.DB_PASSWORD.from-secret=db-secret quarkus.kubernetes.env.mapping.DB_PASSWORD.with-key=password

Generated Manifests Examples

1. Generated Deployment Manifest

# Generated kubernetes.yml apiVersion: apps/v1 kind: Deployment metadata: name: order-service namespace: production labels: app.kubernetes.io/name: order-service app.kubernetes.io/version: 2.1.0 app: order-service version: 2.1.0 team: backend-team annotations: owner: java-team description: Order Management Microservice spec: replicas: 3 selector: matchLabels: app.kubernetes.io/name: order-service app.kubernetes.io/version: 2.1.0 template: metadata: labels: app.kubernetes.io/name: order-service app.kubernetes.io/version: 2.1.0 app: order-service version: 2.1.0 spec: serviceAccountName: order-service-account securityContext: runAsUser: 1001 runAsNonRoot: true containers: - name: order-service image: ghcr.io/my-organization/order-service:latest ports: - containerPort: 8080 name: http - containerPort: 9000 name: management env: - name: JAVA_OPTS value: -Xmx512m -Xms256m -XX:+UseG1GC - name: QUARKUS_PROFILE value: production - name: LOG_LEVEL value: INFO - name: DATABASE_URL value: postgresql://db-host:5432/orders resources: requests: memory: 512Mi cpu: 500m limits: memory: 1Gi cpu: 1000m livenessProbe: httpGet: path: /q/health/live port: 8080 initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 3 successThreshold: 1 readinessProbe: httpGet: path: /q/health/ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 startupProbe: httpGet: path: /q/health/started port: 8080 initialDelaySeconds: 0 periodSeconds: 5 failureThreshold: 30 imagePullPolicy: IfNotPresent

2. Generated Service Manifest

apiVersion: v1 kind: Service metadata: name: order-service namespace: production labels: app.kubernetes.io/name: order-service app.kubernetes.io/version: 2.1.0 spec: ports: - name: http port: 80 targetPort: 8080 protocol: TCP selector: app.kubernetes.io/name: order-service app.kubernetes.io/version: 2.1.0 type: ClusterIP

3. Generated Ingress Manifest

apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: order-service namespace: production annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: orders.mycompany.com http: paths: - path: / pathType: Prefix backend: service: name: order-service port: name: http

Advanced Features

1. Custom Resource Definitions

# Custom Resource configuration quarkus.kubernetes.custom-resources.custom-crd.enabled=true quarkus.kubernetes.custom-resources.custom-crd.target-kubernetes=1.25

2. Horizontal Pod Autoscaler

# HPA Configuration quarkus.kubernetes.hpa.enabled=true quarkus.kubernetes.hpa.min-replicas=3 quarkus.kubernetes.hpa.max-replicas=10 quarkus.kubernetes.hpa.metrics.cpu.target-average-utilization=70 quarkus.kubernetes.hpa.metrics.memory.target-average-utilization=80

3. Service Binding

# Service Binding configuration quarkus.kubernetes-service-binding.enabled=true quarkus.kubernetes-service-binding.services.database.api-version=postgresql.cnpg.io/v1 quarkus.kubernetes-service-binding.services.database.kind=Cluster quarkus.kubernetes-service-binding.services.database.name=postgres-cluster

Multi-Environment Configuration

1. Development Profile

# src/main/resources/application-dev.properties quarkus.kubernetes.deploy=false quarkus.kubernetes.replicas=1 quarkus.kubernetes.resources.requests.memory=256Mi quarkus.kubernetes.resources.requests.cpu=100m quarkus.kubernetes.resources.limits.memory=512Mi quarkus.kubernetes.resources.limits.cpu=250m quarkus.kubernetes.env.vars.QUARKUS_PROFILE=dev quarkus.kubernetes.env.vars.LOG_LEVEL=DEBUG quarkus.kubernetes.ingress.expose=false

2. Production Profile

# src/main/resources/application-prod.properties quarkus.kubernetes.deploy=true quarkus.kubernetes.replicas=3 quarkus.kubernetes.resources.requests.memory=512Mi quarkus.kubernetes.resources.requests.cpu=500m quarkus.kubernetes.resources.limits.memory=1Gi quarkus.kubernetes.resources.limits.cpu=1000m quarkus.kubernetes.env.vars.QUARKUS_PROFILE=prod quarkus.kubernetes.env.vars.LOG_LEVEL=INFO quarkus.kubernetes.ingress.expose=true quarkus.kubernetes.ingress.host=api.mycompany.com quarkus.kubernetes.liveness-probe.initial-delay=45 quarkus.kubernetes.security-context.run-as-non-root=true

3. OpenShift Configuration

# OpenShift specific configuration quarkus.kubernetes.deployment-target=openshift quarkus.kubernetes.ingress.expose=true quarkus.openshift.route.host=my-app.openshift.example.com quarkus.openshift.annotations.openshift.io/display-name=My Quarkus App

Build and Deployment

1. Maven Build Commands

# Build application and generate Kubernetes manifests mvn clean package # Build with container image mvn clean package -Dquarkus.container-image.build=true # Build and push container image mvn clean package -Dquarkus.container-image.build=true -Dquarkus.container-image.push=true # Deploy to Kubernetes mvn clean package -Dquarkus.kubernetes.deploy=true # Build for specific profile mvn clean package -Dquarkus.profile=prod # Generate manifests without building mvn quarkus:kubernetes-generate

2. Generated Files Location

target/kubernetes/ ├── kubernetes.yml # Main Kubernetes manifests ├── openshift.yml # OpenShift specific manifests ├── knative.yml # Knative serving manifests └── jib-container # Container build files

3. Custom Build Script

#!/bin/bash # build-and-deploy.sh set -e ENVIRONMENT=${1:-dev} IMAGE_TAG=${2:-latest} echo "Building for environment: $ENVIRONMENT" echo "Using image tag: $IMAGE_TAG" # Build with specific profile mvn clean package \ -Dquarkus.profile=$ENVIRONMENT \ -Dquarkus.container-image.build=true \ -Dquarkus.container-image.tag=$IMAGE_TAG if [ "$ENVIRONMENT" == "prod" ]; then echo "Pushing image to registry..." mvn package -Dquarkus.container-image.push=true fi echo "Kubernetes manifests generated at: target/kubernetes/" # Display generated manifests echo "=== Generated Manifests ===" cat target/kubernetes/kubernetes.yml

CI/CD Integration

1. GitHub Actions Workflow

# .github/workflows/quarkus-kubernetes.yml name: Quarkus Kubernetes CI/CD on: push: branches: [ main, develop ] pull_request: branches: [ main ] env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' - name: Run tests run: mvn clean test build: needs: test runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' steps: - uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' - name: Build and generate manifests run: | mvn clean package -Dquarkus.container-image.build=true cat target/kubernetes/kubernetes.yml - name: Log in to Container Registry uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Push container image run: mvn package -Dquarkus.container-image.push=true deploy: needs: build runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 - name: Deploy to Kubernetes uses: steebchen/kubectl@v2 with: config: ${{ secrets.KUBECONFIG }} command: apply -f target/kubernetes/kubernetes.yml - name: Verify deployment uses: steebchen/kubectl@v2 with: config: ${{ secrets.KUBECONFIG }} command: rollout status deployment/order-service

2. GitLab CI Configuration

# .gitlab-ci.yml stages: - test - build - deploy variables: MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository" test: stage: test image: maven:3.9-eclipse-temurin-17 script: - mvn clean test build: stage: build image: maven:3.9-eclipse-temurin-17 script: - mvn clean package -Dquarkus.container-image.build=true - cat target/kubernetes/kubernetes.yml artifacts: paths: - target/kubernetes/ expire_in: 1 week deploy: stage: deploy image: name: bitnami/kubectl:latest entrypoint: [""] script: - kubectl apply -f target/kubernetes/kubernetes.yml - kubectl rollout status deployment/order-service only: - main

Best Practices

1. Security Configuration

# Security best practices quarkus.kubernetes.security-context.run-as-non-root=true quarkus.kubernetes.security-context.run-as-user=1001 quarkus.kubernetes.security-context.allow-privilege-escalation=false quarkus.kubernetes.security-context.capabilities.drop=ALL quarkus.kubernetes.security-context.read-only-root-filesystem=true

2. Monitoring and Observability

# Monitoring configuration quarkus.micrometer.export.prometheus.path=/metrics quarkus.kubernetes.annotations.prometheus\\.io/scrape=true quarkus.kubernetes.annotations.prometheus\\.io/port=8080 quarkus.kubernetes.annotations.prometheus\\.io/path=/metrics

3. Resource Optimization

# Resource optimization quarkus.kubernetes.resources.requests.memory=64Mi quarkus.kubernetes.resources.requests.cpu=50m quarkus.kubernetes.resources.limits.memory=128Mi quarkus.kubernetes.resources.limits.cpu=100m quarkus.kubernetes.env.vars.QUARKUS_JVM_CHECK=false quarkus.kubernetes.env.vars.JAVA_OPTS=-Xmx64m -Xms16m

Conclusion

The Quarkus Kubernetes extension provides Java developers with:

  • Zero-Configuration Kubernetes: Automatic manifest generation
  • Production-Ready Defaults: Health checks, resource limits, security
  • Multi-Environment Support: Easy configuration for dev/staging/prod
  • CI/CD Integration: Seamless integration with deployment pipelines
  • Cloud-Native Features: Service discovery, config management, scaling

By leveraging the Quarkus Kubernetes extension, Java teams can focus on business logic while Quarkus handles the complexities of Kubernetes deployment, resulting in faster development cycles and more reliable deployments.

Pyroscope Profiling in Java
Explains how to use Pyroscope for continuous profiling in Java applications, helping developers analyze CPU and memory usage patterns to improve performance and identify bottlenecks.
https://macronepal.com/blog/pyroscope-profiling-in-java/

OpenTelemetry Metrics in Java: Comprehensive Guide
Provides a complete guide to collecting and exporting metrics in Java using OpenTelemetry, including counters, histograms, gauges, and integration with monitoring tools. (MACRO NEPAL)
https://macronepal.com/blog/opentelemetry-metrics-in-java-comprehensive-guide/

OTLP Exporter in Java: Complete Guide for OpenTelemetry
Explains how to configure OTLP exporters in Java to send telemetry data such as traces, metrics, and logs to monitoring systems using HTTP or gRPC protocols. (MACRO NEPAL)
https://macronepal.com/blog/otlp-exporter-in-java-complete-guide-for-opentelemetry/

Thanos Integration in Java: Global View of Metrics
Explains how to integrate Thanos with Java monitoring systems to create a scalable global metrics view across multiple Prometheus instances.

https://macronepal.com/blog/thanos-integration-in-java-global-view-of-metrics

Time Series with InfluxDB in Java: Complete Guide (Version 2)
Explains how to manage time-series data using InfluxDB in Java applications, including storing, querying, and analyzing metrics data.

https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide-2

Time Series with InfluxDB in Java: Complete Guide
Provides an overview of integrating InfluxDB with Java for time-series data handling, including monitoring applications and managing performance metrics.

https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide

Implementing Prometheus Remote Write in Java (Version 2)
Explains how to configure Java applications to send metrics data to Prometheus-compatible systems using the remote write feature for scalable monitoring.

https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide-2

Implementing Prometheus Remote Write in Java: Complete Guide
Provides instructions for sending metrics from Java services to Prometheus servers, enabling centralized monitoring and real-time analytics.

https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide

Building a TileServer GL in Java: Vector and Raster Tile Server
Explains how to build a TileServer GL in Java for serving vector and raster map tiles, useful for geographic visualization and mapping applications.

https://macronepal.com/blog/building-a-tileserver-gl-in-java-vector-and-raster-tile-server

Indoor Mapping in Java
Explains how to create indoor mapping systems in Java, including navigation inside buildings, spatial data handling, and visualization techniques.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper