Idea logo

The IntelliJ IDEA Blog

The Capable & Ergonomic Java IDE by JetBrains

Community Features IntelliJ IDEA Java Videos Webinars

Live Stream Summary: Spring Boot Loves K8s

On April 21, 2021, we had the pleasure of hosting the live stream ‘Spring Boot Loves K8s’ by Madhura Bhave, one of the awesome software engineers on the Spring Boot Team.

The code in this live stream can be accessed using this link.

Session details

In this live stream, Madhura demonstrated features introduced in Spring Boot 2.3 and later versions that improved developing and deploying Spring Boot applications to Kubernetes. She covered four main improvements: building efficient container images, liveness and readiness probes, graceful shutdown and loading ConfigMaps and Secrets.

To demonstrate these improvements, Madhura used a simple Spring Boot text editor application that accepts Markdown text then allows the user to preview it in HTML as well as perform some spell checking on it. She used IntelliJ IDEA for developing her application as well as starting and stopping her application services. You can see each step she took during the presentation reflected by the application’s GitHub repository commits.

Building efficient container images

Spring Boot 2.3 and later versions introduced features to build more efficient container images through Dockerfiles as well as Cloud Native buildpacks.

Dockerfiles

Madhura started by building a docker image for her application using a traditional Dockerfile:

FROM openjdk:8-jre-alpine
WORKDIR application
ARG JAR_FILE=scribe/target/scribe-*.jar
COPY ${JAR_FILE} application.jar
ENTRYPOINT ["java","-jar","application.jar"]

She used a tool called dive to take a closer look at the docker image’s layers and pointed out that the application layer is quite large: 23MB. While the traditional Dockerfile works, it isn’t very efficient because there is one single layer for the application which include both the application code and dependencies which mean that each time we make a code change and rebuild our images, we not only rebuild our application code but also the dependencies even though they often don’t change – wasting time and disk space.

The way to improve the previous Dockerfile is by splitting the application into multiple layers and extracting out the dependencies into its own layer – putting the least frequently changed layers at the bottom. You can do this by using a builder which allows you to separate the dockerfile into multiple phases.

Madhura went through multiple iterations of the Dockerfile introducing incremental improvements. The final Dockerfile looks like this:

FROM openjdk:8-jdk-alpine AS builder
WORKDIR source
ARG JAR_FILE=scribe/target/scribe-*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract

FROM openjdk:8-jre-alpine
WORKDIR application
COPY --from=builder source/dependencies/ ./
COPY --from=builder source/spring-boot-loader/ ./
COPY --from=builder source/snapshot-dependencies/ ./
COPY --from=builder source/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

The final Dockerfile takes advantage of the builder pattern to separate the application layer. It also runs the jar using the JarLauncher which takes into account the classpath index file. In addition, it uses the new -Djarmode=layertools option introduced in Spring Boot 2.3. She also adds a custom layers.xml file.

After making all the improvements to the Dockerfile, Madhura rebuilt the image and we see that the new image has a much smaller application layer with a size of 102KB! Now, whenever we make a change in the application code, the layer being rebuilt is much smaller.

View this section in the video

Cloud Native Buildpacks

The other improvement for building more efficient container images is through Cloud Native Buildpacks which allow you to convert your application code into a container image without using Dockerfiles. With Cloud Native Buildpacks, developers don’t have to worry about writing or maintaining Dockerfiles. Instead, you can use a single mvn spring-boot:build-image command if you’re using maven or gradle bootBuildImage if you’re using Gradle.

Madhura starts her demo by removing the Dockerfile for the application. Then, she shows us how easy it is to build her image by running mvn spring-boot:build-image -pl scribe. The command calls out to the cloud buildpacks to build the image which takes care of pulling the JDK for you. They also support custom layers with layers.idx. In addition, they do extra work to optimize the image layers. Another advantage of using buildpacks are security patches. For example, if your JDK has a CVE, the buildpack will pull the patched version automatically instead of you having to manually update your Dockerfile.

She also demos how the build packs will pick up and take care of application changes by switching the application from using Java 8 to Java 11.

View this section in the video

Kubernetes probes

Kubernetes probes are a way for the application to communicate its status and signal when something goes wrong. The liveness probe signals that an application is broken and needs a restart. The readiness probe signals an application is ready to serve traffic. Spring Boot exposes Kubernetes probes via the actuator health groups: /actuator/health/liveness and /actuator/health/readiness.

Madhura adds the management.endpoint.health.probes.enabled=true property to her application.properties file and simulates different situations when liveness or readiness may be DOWN. Note that the management.endpoint.health.probes.enabled property is enabled by default when running on Kubernetes but since Madhura was running her application locally, she had to manually enable it.

She explains the difference between liveness and readiness by simulating the application relying on a dictionary service that is being slow. In that case, the application liveness is UP since the application doesn’t need a restart, however, the readiness is DOWN since the application isn’t ready to accept traffic while the dictionary service is still coming up.

She also showed how an application’s internal state is available in AvailabilityState and can be published using AvailabilityChangeEvent.

View this section in the video

Graceful Shutdown

When graceful shutdown is enabled, the server doesn’t accept new requests but it allows any in-flight requests to complete. First, Madhura showed us the application’s behavior when graceful shutdown is disabled. She inputs some text that takes a few seconds to process and while it is being processed, she shutdowns the markdown converter service. As a result, we see a could not render markup message indicating that the request was not allowed to finish.

Then, she enabled graceful shutdown by adding server.shutdown=graceful to the application.properties file. She tries to make the slow request again and shuts down the service while the request is being processed but this time, we see the rendered markup message because the in-flight request was allowed to complete. She points out that graceful shutdown can be especially important in cloud environments since it allows you to scale your application instances down without abrupt request interruptions.

You can configure a grace period via spring.lifecycle.timeout-per-shutdown-phase so you can limit the time an application will wait for a request to finish.

Graceful shutdown is supported on Tomcat, Jetty, Undertow and Reactor Netty.

View this section in the video

ConfigMaps and Secrets

ConfigMaps are Kubernetes objects that can be used for storing configuration data in key-value pairs. They can be made available to the pod using volume mounting. When a ConfigMap is volume mounted, the file name is the key in the ConfigMap and the content is the value.

Madhura adds the volume mounted files as a property source using spring.config.import with the file name as the property name and contents of the file as the value. Files can be imported using the configtree: prefix. If you want your application to start even if the configtree doesn’t exist, you can add it as an optional location.

She also showed us how to take advantage of Spring Boot’s multi-document support which allows you to consolidate all your properties in one file.

View this section in the video

Live Stream Summary

Madhura showed us the many ways that Spring Boot has improved the development and deployment of applications on Kubernetes. Try these features with the latest Spring Boot versions!

IntelliJ IDEA Resources

Check out this link to find more about how IntelliJ IDEA Ultimate provides a rich set of built-in developer tools and outstanding support for the Spring framework. If you are new to using Spring Boot in IntelliJ IDEA, this can help you get started. You can also refer to the IntelliJ IDEA documentation on how it supports the Spring framework.

If you’re using Kubernetes, checkout the Kubernetes in IntelliJ IDEA Documentation to see how IntelliJ IDEA can help you when using Kubernetes to deploy, scale and manage your containerized applications.

You can use this link to download IntelliJ IDEA today.

Our next live stream

On May 19 2021, we will be hosting a live stream on Make IntelliJ IDEA Your Own by Sirisha Pratha. Don’t forget to Follow us on Twitter so you don’t miss the updates.

Happy developing!

Discover more