Guide to Creating and Containerizing Native Images
In this article, we will learn how to turn Java applications into native images and then containerize them for further deployment in the cloud.
Join the DZone community and get the full member experience.
Join For FreeNative Image technology is gaining traction among developers whose primary goal is to accelerate startup time of applications. In this article, we will learn how to turn Java applications into native images and then containerize them for further deployment in the cloud. We will use:
Spring Boot 3.0 with baked-in support for Native Image as the framework for our Java application;
Liberica Native Image Kit (NIK) as a native-image compiler;
Alpaquita Stream as a base image.
Building Native Images from Spring Boot Apps
Installing Liberica NIK
It would be best to utilize a powerful computer with several gigabytes of RAM to work with native images. Opt for a cloud service provided by Amazon or a workstation so as not to overload the laptop. We will be using Linux bash commands further on because bash is a perfect way of accessing the code remotely. macOS commands are similar. As for Windows, you can use any alternative, for instance, bash included in the Git package for Windows.
Download Liberica Native Image Kit for your system. Choose a Full version for our purposes.
Unpack tar.gz with:
tar -xzvf ./bellsoft-liberica.tar.gz
Now, put the compiler to $PATH with:
GRAALVM_HOME=/home/user/opt/bellsoft-liberica export PATH=$GRAALVM_HOME/bin:$PATH
Check that Liberica NIK is installed:
java -version openjdk version "17.0.5" 2022-10-18 LTS
OpenJDK Runtime Environment GraalVM 22.3.0 (build 17.0.5+8-LTS)
OpenJDK 64-Bit Server VM GraalVM 22.3.0 (build 17.0.5+8-LTS, mixed mode, sharing)
native-image --version
GraalVM 22.3.0 Java 17 CE (Java Version 17.0.5+8-LTS)
If you get the error "java: No such file or directory" on Linux, you installed the binary for Alpine Linux, not Linux. Check the binary carefully.
Creating a Spring Boot Project
The easiest way to create a new Spring Boot project is to generate one with Spring Initializr. Select Java 17, Maven, JAR, and Spring SNAPSHOT-version (3.0.5 at the time of writing this article), then fill in the fields for project metadata. We don’t need any dependencies.
Add the following code to you main class:System.out.println("Hello from Native Image!");
Spring has a separate plugin for native compilation, which utilizes multiple context dependent parameters under the hood. Let’s add the required configuration to our pom.xml file:
<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Let’s build the project with the following command:
./mvnw clean package -Pnative
The resulting native image is in the target directory.
Write a Dockerfile
We need to write a Dockerfile to generate a Docker image container. Put the following file into the application folder:
FROM bellsoft/alpaquita-linux-base:stream-musl
COPY target/native-image-demo .
CMD ["./native-image-demo"]
Where we:
Create an image with Alpaquita Linux base image (the native image doesn’t need a JVM to execute);
Copy the app into the new image;
Run the program inside the container.
We can also skip the step with Liberica NIK installation and we build a native image straight in a container, which is useful when the development and deployment architectures are different. For that purpose, create another folder and put there your application and the following Dockerfile:
FROM bellsoft/liberica-native-image-kit-container:jdk-17-nik-22.3-stream-musl as builder
WORKDIR /home/myapp
ADD native-image-demo /home/myapp/native-image-demo
RUN cd native-image-demo && ./mvnw clean package -Pnative
FROM bellsoft/alpaquita-linux-base:stream-musl
WORKDIR /home/myapp
COPY --from=builder /home/myapp/native-image-demo/target/native-image-demo .
CMD ["./native-image-demo"]
Where we:
Specify the base image for Native Image generation;
Point to the directory where the image will execute inside Docker;
Copy the program to the directory;
Build a native image;
Create another image with Alpaquita Linux base image (the native image doesn’t need a JVM to execute);
Specify the executable directory;
Copy the app into the new image;
Run the program inside the container.
Build a Native Image Container
To generate a native image and containerize it, run:
docker build .
Note that if you use Apple M1, you may experience troubles with building a native image inside a container.
Check that the image was create with the following command:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 8ebc2a97ef8e 18 seconds ago 45.2MB
Tag the newly created image:
docker tag 8ebc2a97ef8e nik-example
Now you can run the image with:
docker run -it --rm 8ebc2a97ef8e
Hello from Native Image!
Conclusion
Native image containerization is as simple as creating Docker container images of standard Java apps. Much trickier is to migrate a Java application to Native Image.
We used a simple program that didn’t require any manual configuration. But dynamic Java features (Reflection, JNI, Serialization, etc.) are not supported by GraalVM, so you have to make the native-image tool aware of them.
Published at DZone with permission of Dmitry Chuyko. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments