Featured image for Quarkus for Spring Developers

Want to learn more about developing applications with Quarkus? Download our free e-book Quarkus for Spring Developers, which helps Java developers familiar with Spring make a quick and easy transition.

Microservice applications designed today are often deployed on a platform such as Kubernetes. This platform can orchestrate the deployment and management of containerized microservices. Microservices development calls for sophisticated patterns, such as health checks, fault tolerance, load balancing, distributed tracing, and remote debugging and development. Because of this, it is essential to adopt technologies and frameworks that support these patterns while also providing a great developer experience.

This article will discuss some of these patterns and showcase why Quarkus is ideal for Kubernetes-native Java applications.

Challenges of Java in containers

Some challenges of Java in the cloud, container, and Kubernetes world stem from Java's tendency to consume lots of memory and to suffer slower startup times than other languages and runtimes.

The immutable nature of containers forces rethinking the design and maintenance of Java applications. Highly reconfigurable and long-running applications with large heap sizes are no longer necessary. Moreover, essential parts of an application, such as the database driver or logging framework, are known in advance. Therefore it is unnecessary to use frameworks that rely heavily on runtime bindings or Java reflection. Instead, application boot times can be accelerated and runtime footprints reduced by performing as much work at build time as possible.

Many Java-based frameworks embrace the dynamic flexibility Java provides. A lot happens in the first few seconds of Java application startup. For example, a Spring application can alter its runtime behavior by changing a few configuration properties. The dynamic runtime binding allowed by Java enables this adaptability. These frameworks need to rethink their underlying architectures in order to fit into today's architectures.

This is the situation Spring finds itself in today. By contast, Quarkus was designed with these characteristics in mind. Quarkus offers near-instant scale-up and high-density utilization in container orchestration platforms such as Kubernetes. You can run many more application instances within the same hardware resources. From the beginning, Quarkus was designed around container-first and Kubernetes-native philosophies, optimizing for low memory usage and fast startup times. Quarkus design principles reduce the size and, ultimately, the memory footprint of the application running on the JVM while also enabling Quarkus to be “natively native.” Quarkus's design accounted for native compilation from the onset.

Preparing applications for Kubernetes

Preparing an application to run as a container within Kubernetes requires numerous steps that aren't required for local development and deployment. It also requires tools, concepts, and configurations that might be new to developers.

Building a container image

Deploying an application on Kubernetes requires packaging an application's binary and resources as a container image. Quarkus contains out-of-the-box extensions for building container images, as described in Table 1.

Table 1: Quarkus extensions for building container images.
Extension Description
Quarkus Jib extension Powered by Google Jib for performing container image builds. The major benefit of using Jib with Quarkus is that all the dependencies are cached in a different layer than the application. This caching makes rebuilding images fast and small when pushing the image to a repository. Additionally, this extension provides the ability to create a container image and push it to a registry without any dedicated client-side tooling or a running daemon process.
Quarkus Docker extension Uses the Docker API to build a container image from one of the Dockerfiles in the src/main/docker directory. Code Quarkus can generate examples. The resulting image is hosted in the local Docker image repository.
Quarkus S2I extension Uses Red Hat OpenShift's Source-to-Image (S2I) build process to build a container image using a binary build. The Quarkus build process will build the application binary and use OpenShift to build the container image from the binary. The resulting image is hosted within the OpenShift container registry.

Building a container image is as simple as running ./mvnw package -Dquarkus.container-image.build=true once one of these extensions is added to an application. Pushing a container image to a configured registry is also as simple as adding the -Dquarkus.container-image.push=true flag to the command. There are also many configuration options available for each of these extensions.

Note: These flags can also be added to the src/main/resources/application.properties file rather than specified on the command line.

Kubernetes manifests

Once a container image is created, deploying an application on Kubernetes requires the creation and deployment of manifests. A Kubernetes manifest is a resource file described using YAML or JSON format. Many types of resources can be deployed, some of which are described in Table 2.

Table 2: Common Kubernetes manifest types.
Type Description
Deployment Manages the application, resources, volumes, properties, environment variables, replicas, etc., to be deployed.
Service Endpoint providing access to the application port of the Pods hosting an application. The Service performs load balancing on incoming requests to Pods.
Namespace Virtual space isolating Pods.

Ingress (Kubernetes)

Route (OpenShift)

Manages external access to the Service.
ConfigMap Store nonconfidential data in key-value pairs.
Secret Store sensitive data, such as a password, token, or key.

The Quarkus Kubernetes extension (or the Quarkus OpenShift extension, if using Red Hat OpenShift Container Platform) frees developers from manually creating the required Kubernetes manifests by automatically creating them during the build process. The generated resources can be customized further using well-defined properties. This type of developer experience doesn't exist in Spring Boot directly. You need to integrate the Dekorate Spring Boot library into your project manually.

Want to run your application serverless? The Kubernetes (or OpenShift) extension can also generate Knative resources, enabling you to deploy your application in a serverless fashion, whether it's on the JVM or a native image.

Creating these manifests is as simple as running ./mvnw clean package once one of these extensions is added to an application. All the manifests in both YAML and JSON format will be located in the /target/kubernetes directory. There are also many configuration options available for each of these extensions. Deploying an application to Kubernetes directly is just as easy as running ./mvnw clean package -Dquarkus.kubernetes.deploy=true.

Developing applications on Kubernetes

The development process is faster and more efficient with Quarkus live coding. Quarkus automatically detects changes within a project and transparently recompiles and redeploys the changes, making them visible typically in under a second. You can continuously write code and have the underlying platform seamlessly incorporate the changes into the running application.

In many instances, it might not be feasible, or even possible, to run necessary dependent or downstream services on a local developer workstation. There are limited options in such instances without Quarkus. One option is to continuously rebuild, redeploy, and retest the application each time a change is made. Another option involves lots of tedious port mapping and forwarding between the local workstation and the remote environment.

Quarkus Remote Dev Mode solves this challenge by extending live coding to push changes to a remote Quarkus instance, as shown in Figure 1. Quarkus Remote Dev is built into the Quarkus platform and does not require any IDE tooling or plug-ins. This capability greatly reduces the inner feedback loop while alleviating the "works on my machine" problem. It also allows for quick and easy prototyping of new features and capabilities.

Figure 1: Quarkus Remote Dev Mode.
Figure 1: Quarkus Remote Dev Mode.

Operating applications on Kubernetes

The composition of distributed applications communicating with each other introduces its own set of challenges. New and modern applications need to be designed around scalable distributed patterns to deal with these challenges. Users today expect near-instant response times and 100% uptime. The proliferation of distributed applications communicating to fulfill a user's request goes directly against these expectations.

Luckily, a set of patterns in the microservices world have evolved, helping to deal with some of these challenges.

Health Check

Kubernetes has built-in probes for determining whether or not a Pod is alive and ready to serve incoming requests. This is known as the Health Check pattern, which is is supported in Quarkus using the SmallRye Health extension. The SmallRye Health extension is an implementation of the MicroProfile Health specification. Spring Boot applications can use the Kubernetes probes included in the Spring Boot Actuator starter; however, the Spring Boot Actuator only provides the endpoints and doesn't generate the necessary configuration or the required Kubernetes manifests. They will need to be created manually.

Configuration management

Configuration management refers to the management of properties and resources needed to configure an application. This information is usually injected into a container at runtime rather than included in the image at build time. The Kubernetes Deployment resource contains information on how this injection would take place.

In Quarkus, the configuration can be injected in several ways, including system properties, environment variables, and an application.properties file (or application.yml file, if using the Quarkus YAML extension). The Quarkus Kubernetes extension can be configured to generate this information when generating the Kubernetes manifests. Additionally, the Quarkus Kubernetes Config extension can be used to read Kubernetes ConfigMaps and Secrets directly as configuration sources without mounting them into the Pod running the application.

Both Quarkus and Spring can use the Spring Cloud Config Server (SCCS) for centralized configuration management. The Quarkus Spring Cloud Config Client extension can read configuration properties from SCCS at application startup.

Metrics

Both Quarkus and Spring use the Micrometer metrics library for collecting metrics. Micrometer provides a vendor-neutral interface for registering dimensional metrics and metric types, such as counters, gauges, timers, and distribution summaries. These core types are an abstraction layer that can be adapted and delegated to specific implementations, such as Prometheus.

The Quarkus Micrometer extension automatically times all HTTP server requests. Other Quarkus extensions also automatically add their own metrics collections. Applications can add their own custom metrics as well. Quarkus will automatically export the metrics on the /q/metrics endpoint. In Spring, Micrometer is supported by the Spring Boot Actuator starter if the Micrometer libraries are included on the application's classpath.

Distributed tracing

A trace tracks the progress of a single request as it passes through services. Distributed tracing is a form of tracing that traverses process, network, and security boundaries. Each unit of work is called a span. A trace is a tree of spans. Think of a distributed trace like a Java stack trace, capturing every component of a system that a request flows through, while also tracking the amount of time spent in and between each component.

All Quarkus REST endpoints are automatically traced when using the Quarkus OpenTracing extension. The extension also allows custom traces to be added to an application. Additional instrumentation can be added to trace all JDBC, Kafka, and MongoDB interactions. Samplers can then be configured to export some percentage of traces to a Jaeger collector.

The community in general is moving towards the OpenTelemetry observability framework. OpenTelemetry will most likely supersede OpenTracing at some point in the future. Quarkus has an extension for OpenTelemetry. Spring support is still incubating as part of the Spring Cloud Sleuth OTel project.

Where to learn more

There are many free resources available for learning about and getting started with Quarkus. Why wait for the future? Since its inception in 2019 and continuing today and into the future, Quarkus has provided a familiar and innovative framework for Java developers, supporting capabilities developers need and want today.

Check out these available resources:

Last updated: December 27, 2023