Buildpacks for Spring Boot
Transcript:
Hi friends, Buildpacks. Let you containerize your application without writing a Docker file. In this video, I'll show you how to use them with a Spring Boot app and fine tune them to get smaller, more efficient and secure container images. Buildpacks are a set of scripts that take your source code. And turn it into a ready to use container image automatically. No docker files, no manual dependency management, but also no sorcery. Pure engineering. Let's break it down.
When you set Buildpacks loose on your code, they go through two main phases, detect and build. During the tech phase, Buildpacks, inspect the source code. If they recognize the language or framework, they step in to start the build process. During the build phase, Buildpacks, patch dependencies, compile your code if needed, and set everything up for production. You get a clean, secure, and optimized container image ready to run on any cloud. To make this happen, buildpacks, use so-called builders. Think of them as pre-configured toolboxes. A builder includes a set of Buildpacks to analyze and build the application, a stack, a build image, and the run image in the lifecycle manager that assembles everything into a final container image. It means that one builder can handle different applications automatically. So no more fiddling with Docker files. Just push your code and let the Buildpacks do their magic.
Getting a production ready container image for Java is just a matter of one command. You can use a pack tool or a Maven or a Gradle plugin. I bet you're curious what's going on in between the source code and the ready container image. So let's look under the hoods of a build process with Buildpacks. When you build a container image, you get a very detailed lock of the build process. So let's look at that Here. We can see that we are building the image called newer Watch the, uh. Builder that is used is Noble Tower Tiny. We are pulling the necessary image for the platform. So there's the analyzing stage. We can see that the image was not found, so it's going to be built from scratch. And then there is the detect face. You can see that six of 26 Buildpacks are participating in building Our image. Among them are CA certificates, both of labora, which is used by default in Spring Boot Buildpacks. There's also sift for creating an as bomb and some Buildpacks for building a Spring Boot application. There are several build configurations like embedded certificates and so on. You can also configure the JDK to get the more optimal result or. some additional tooling that you need for the image. We're going to talk about it a little bit later. We can see that the builder uses, um, pulse of Liberica JRE 24 and we are also using a build pack for sift to create an SBO software build off materials. There's a build configuration for Spring Boot, so we can also tweak these options and as you can see, we are creating a layer jar by default.
A little side note, a layer jar is a very, very cool feature of Spring Boot that lets you build a jar file with several layers. Which are ordered in the final container image from the least used one to the most least one. The next time when you rebuild your image, the changed layers are going to be changed and the others pull from cache. In this case, the build process going to be faster, and here it is, the final process of building the image and adding necessary layers. And now we get the image for. Our demo application, neur Watch. One powerful feature of Buildpacks is calculating the memory limits for your container automatically. For this purpose, Buildpacks use the so-called chow memory calculator algorithm. Some other JVM options are also set by default, so let's run our container image and see which options were enabled. Let's run our container image. We can see that Buildpacks have calculated the JVM memory settings based on available memory. Here are these options. maximum direct memory size maximum hip size, meta space size, and so on. Also Java native memory tracking is enabled by default, so when you stop your container image, you will get a log on the native memory usage. You can disable this functionality. I'll show you later how to do that. Also, some Java options were set automatically. For instance, Java security properties exits on out of memory error. Again, memory settings. Unlock diagnostic VM options. if you will want to use JFR print native memory tracking statistics. And that's it. Let's stop the container. Here you can see the native memory tracking stats, the total. Native memory usage and a more detailed report.
I already mentioned that Buildpacks create a software bill of materials by default and place it into the container image. A software bill of materials is basically a list of all dependencies used to build and run your application with their versions and available. Vulnerabilities, if any. An SBO helps you track known CVS in your project and, um, manage them. So let's see how we can extract the SBO from our container image. The easiest way to do that is to use the pack tool. So install it and run pack SBO down. Download the name of your container image and the output directory. Great. We have to wait for a little bit. And here it is, our SBO layers directory. With all the information on the dependencies here, it's broken down into different packages. So there is the SBOs for Spring Boot Buildpacks for certificates for Well Softly America. You can check them out. The Buildpacks are in the sift format. Also, there are SBOs in CDX and SPDX formats for the launcher. You can adjust. The command and make the tool, for instance provide a cumulative report on the dependencies. The image that we have built for our Spring Boot application uses jamy-tiny as a builder, and Liberica JDK as the Java time. This image takes the up 339 megabytes, and it starts in about five seconds to be more precise in 4.9 seconds. The default settings are quite optimal, but in the case, you need something else. You can actually tune the Buildpacks to achieve more optimal result for you.
Let's see what you can do with build pack. First of all, you can configure the JVM. You can choose the JVM type JDK or JRE by default, the JRE is used, but if you need JDK, you can use that. You can also choose the JVM version 17, 21. You can select the application server or even tune the Java options like garbage collection. You can also choose another JDK distribution by default, Liberica JDK is used, but you can select from the list of available J dks if you want. Just check that this Java run time provides the necessary JVM type and version. You can also choose another builder. As I said, by default Ubuntu Jammy tiny is used. It doesn't contain a shell, so you might want to have a shell in your final image.
In this case, you can choose Ubuntu jammy base or another builder available as a buildpack. Well, actually there are quite many Buildpacks available. There is the New Relic Build Pack, the Datadog Build Pack. The Open Telemetry Built Pack, the J Profiler Built Pack, and so many more. There are also alpaca built packs. They are based on Libc, GDK or JRE and Alpaquita Linux with MUSL or Glip C Library. Alpaquita Linux is a lightweight Linux distribution, so your container image will be smaller than with the default builder, but as a ponce, it contains a shot. One important point to consider. You can tune the JVM settings, but you can't tune the Linux settings. So if you need to add additional packages for Linux, this is not going to be so easy to do, but good news is that you can actually build your custom build pack. Drop me a comment if you want to see how to do that.
What if reducing container image size is critical to you? In this case, you can use JLink. It is a JVM tool that helps you to cut out the custom JRE with only the modules used by your application when it is running. In the real life, you'll have to first analyze the modules that your application uses with the G Depths tool, and then list all these modules in an Docker file to cut out the JRE. But with Buildpacks, this whole process is just a matter of one teeny tiny option in the plugin configuration. So just set bp JVMJ Link enabled to true built the container image with the default command. And while our image now takes 220 megabytes, so more than 100 megabytes smaller, what if reducing the startup time of your application is more critical to you than reducing the image size? In this case, you can enable CDS and Spring AOT. Just add two options, say in the plugin configuration VP Spring AOT enabled, and BP JVM CDS enabled also in the plugin add the execution goal called Process AOT. Adding AOT processing is not necessary, but is actually very beneficial, so it helps to reduce the startup time even more when you use CDS. But even without the CDS, it helps to reduce the startup time. So run the default command, and now our image starts in less than two seconds. So one and a half to be precise. That's a great result.
Of course, the image takes a little bit more now, but that's because the CDS archive takes additional space. You can also generate a native image using buildpacks. Simply add the gra VM built tools Maven or gravel plugin, and run the default command, but with the native profile, the process of building a native image. Take some time. So if you're doing that with me, go ahead and grab a coffee, hit subscribe button, like this video if you like it so far, or drop me a comment. We can see that the image size almost hasn't changed as compared to the one built with the default settings, but now the application starts in half a second. You can also enable debugging or profiling with Buildpacks. I'll show you how to enable profiling with A JFR and GMX. You can do that by configuring the plugin settings or at one time when you start your container. In both cases, we need to add a couple of options to the plugin. We can do that with the PPE Append Java Tool Options Add, unlock diagnostic VM options and debug non-safe points to make profiling more precise. If you want to start profiling when you start the container, that's it. Build the container image and then start it with JFR enabled. So BPL, JFR enabled true. You can also set here the JFR argument with the BPL, JFR arcs. For instance, the file name, the duration of the recording, and so on. Also, you can enable JFR in the plugin. So let's add one more option to the ones we have already specified, and that is start flight recording here. You can also specify the JFR arguments if necessary.
After that, you can start your container the usual way. And the recording will be started. Enabling JM Mix enables you to monitor your application performance in real time using channel emission control. Again, you can enable it when you start the container. Just specify BPL JM Mix enabled set through, and BPL jamming, export. If you're running your application on local host, you can now simply go to Java Mission Control dashboard. Select your application and watch how it performs. However, what if you are not running your application on local host, which is a more realistic situation? In this case, we need to specify the server host name, but we can't simply do that by adding this one option to the plugin, we'll have to enable Fully in the plugin and specify all the necessary options. All these options that are enabled by default when we run BPLG MX enabled. So let's do that.
First at BP EA Pan J tools, options to the plugin if you haven't done that yet. And now we need to specify six options for JMX sound management, JM remote server post name, authenticate true or false, SSL true or false report. And RMI port, it has to have the same value as the Jamie Simon port, build a container image, run it with the default command, and now you can observe your application in real time regardless of where you are running it. And one more thing that I wanted to show you is how to disable native memory tracking. If you need to, that's actually pretty simple. Just specify one option when starting the container. That is VPE Java and MT enabled set to falls, and that's it. In this video, we'll learn how to use and optimize Buildpacks to build efficient Spring Boot containers. Now it's your turn Test. This tweaks on your application. And don't forget to like and subscribe until next time.