Compile a Spring application natively with GraalVM
With the release this week of Spring Native Beta in version 0.9.0, it is interesting to take stock of the compilation of Spring
applications into native executables using GraalVM
and its native mode. -image
.
The execution of an application in native code has, indeed, many advantages compared to that in Bytecode in a JVM:
- The start is instantaneous
- The performance is optimal from the start
- Memory consumption is greatly reduced
The version of Spring Native is, however, in Beta which means that not all Spring components are still functional in native mode. Let’s see how it works in detail.
- Basic system requirements
- Generation of the application skeleton
- Adding a Web Controller
- Compilation in native code
- Conclusion
Basic system requirements
First, you will need to install GraalVM and then its native code compiler native-image
:
For MacOS and Linux, it is recommended to install its JDKs with SDKMAN. Nothing complicated: refer to doc. official or find a previous article on this tool Install Java with SDKMAN
For our friends on Windows, refer directly to the Installation on Windows Platforms
Generation of the application skeleton
The arrival of the Beta version implies that Spring Native is now supported by Spring Initializr, a web interface that allows you to compose your Spring application and then generate its skeleton.
Let’s use it to define our demo app:
- Fill in the project metadata
- Select the
Spring Native [Experimental]
dependency to benefit from native compilation - Add the
Spring Web
dependency as part of this demo - Download the generated code by clicking on the
Generate
button
Spring Native Modules
You will find, in the POM, the list of Spring modules configured as Maven dependencies:
- The
Spring Native
dependency and its version:
- The
Spring Boot Maven
plugin and its configuration to run the build of a native image in a Buildpacks :
- The
AOT Maven
plugin which is used to configure Spring for itsAhead-Of-Time
compilation as well as to generate code for the configuration and the classpath of the application:
Remarks
Dependencies not supported
In case you select a Spring dependency not yet supported in native mode, the HELP.md
file will contain a warning:
Supported dependencies
- In the case of dependencies supported by Spring, the initializr will configure all the plugins necessary for the build and execution of the Spring application to work out-of-the-box!
In the example of Spring Data JPA
, Maven will be configured so that the Hibernate classes are compiled when the application is built and not during its runtime as is the case for a JVM:
All of this is very reassuring! I had previously tested version 0.7.1
of Spring Native (named spring-graalvm-native at the time) and there were a lot of manual changes to be made.
Posted goal of the team in charge of Spring Native
- Provide configuration automatically so that there is no need to modify the Java code, whether the application is running in native mode or in a JVM.
- Make the unit tests run in the same way in a native image or in a JVM.
- Further reduce the size of the native image generated in the next version 0.10 of Spring Native.
Adding a Web Controller
Unzip the file generated by
Spring Initializr
and open the directory with your preferred IDE.Create a new Controller at the root of your project’s package with the code below:
Compilation in native code
There are two ways to compile a Spring application to native code:
- By using the Buildpack Spring Boot integrated in Spring and which will produce a container lightweight containing the native code of the application
- Using the Maven plugin native-image-maven-plugin which will produce a native executable
Note
The Maven configuration generated by Spring Initializr chooses Buildpacks:
- We will therefore only discuss this aspect in this article.
- We will see the native build using the Maven native-image plugin which requires significant POM modifications, in a future article.
Using the Spring Boot Buildpack
This procedure obtains a Docker container that contains the application compiled in native code. It is lightweight and can be deployed directly into a container orchestrator.
Prerequisites
Docker must be installed in order to be able to launch the Buildpack Spring Boot. It’s a container that contains everything you need to build a Spring application in native code.
- You can install Docker from Docker Installation
- For MacOS, it is recommended to allocate at least 8GB of memory to Docker
- For Windows, you must activate Docker WSL 2 Backend to have better their performances
Compiling in native mode with Buildpacks
- The native application can be compiled by running the following command:
This command will create, locally, a Linux container to compile the native application from the GraalVM native-image
compiler.
- Let’s look at the images present, in the local Docker registry and which have just been implemented in this build:
We can see that this process produces 3 Docker images:
- paketobuildpacks/run:tiny-cnb: The
distroless
bionic + glibc + openssl + CA certs basedrunner
to run an application in native code. It is the basic container used to encapsulate an application in native code. - paketobuildpacks/builder:tiny: The
builder
based on an ubuntudistroless
stack: bionic + openssl + CA certs + compilers + shell utilities. It is a Buildpack used to compile most applications in Go and Java applications in native code with GraalVM. - demo_spring_native:0.0.1-SNAPSHOT: The native code application encapsulated in a basic
distroless
runner.
To go further
- The images from the Buildpack date from 1980, January 1, 1980 exactly! This is quite intended and the explanation can be found here: Time Travel with Pack
- The Distroless stacks are minimalist images, developed by Google and which improve security and container size by reducing the area of attacks and the number of components they integrate.
- The notion of Runner and Builder in the Buildpacks .
Running the application
- To start the application from the Buildpack, type the following command:
- Test its operation with:
It works! Magnificent!!
Buildpacks characteristics
- The compilation lasts 3 min (with Docker images and Maven artifacts locally)
- The application starts in 0.06 s
- The Docker image containing the Spring application and the OS, is 82 MB in size
Conclusion
- The Spring Native 0.9.0 version allowed us to easily compile a Spring application in native mode.
- As expected, the benefits of native mode are instant startup and greatly reduced container size.
Interesting points, this generates new uses:
- High Availability management can be done with a single instance, the start of a second being instantaneous.
- instant start also allows a web application to be serverless, without needing to be redeveloped.
- With Knative (a Kubernetes redesign that starts serverless containers), GraalVM Native is a very well suited solution.
Spring Native will eventually be integrated into Spring Boot 3 and Spring Framework 6, the goal being to specify only in the Maven or Graddle build, the expected target (native or other). The remaining work consists of optimizing the size of the native code generated, taking into account more Spring APIs and improving the execution of the tests in the native image (JUnit 5, …)
To be followed closely then!
Cheers …