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.
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.
|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.
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.
|Manages the application, resources, volumes, properties, environment variables, replicas, etc., to be deployed.
|Endpoint providing access to the application port of the
Pods hosting an application. The
Service performs load balancing on incoming requests to
|Virtual space isolating
|Manages external access to the
|Store nonconfidential data in key-value pairs.
|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.
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.
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 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
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.
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.
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:
- Read Quarkus for Spring Developers to learn about what challenges led to Quarkus and see side-by-side examples of familiar Spring concepts, constructs, and conventions.
- Learn why you should choose Quarkus over Spring for microservices development.
- Check out why organizations believe the developer experience is just as important as the product itself.
- Explore Quarkus quick starts in the Developer Sandbox for Red Hat OpenShift, which offers a free and ready-made environment for experimenting with containerized applications.
- Try free 15-minute interactive learning scenarios.
- Get started with Quarkus on your own.
- Learn about Quarkus's Spring compatibility features.
- In some instances, there might not be any code changes needed to run a Spring application on Quarkus.
- Get hands-on converting a Spring Boot application to Quarkus with little-to-no code changes.
- Attend a Quarkus World Tour event when it comes to a city near you. You can even request a private stop.