Spring Boot 2.6.3 ARM64 Image
Spring Boot on Raspberry Pi 4b 8gb
The purpose of this article is to create a Spring Boot (2.6.3) native image for arm64. In my case, the Raspberry Pi Zero 2W. The Raspberry Pi Zero 2W only has 512mb of SDRAM. Because of the small footprint of the Zero 2W
I want to be as efficient as possible.
We will build a very simple Spring Boot app again, but add Spring Native
to compile a native executable.
A bunch of this information is repeated from the previous article. Image creation happens on the Raspberry Pi 4. The resulting image can be deployed to a Raspberry Pi Zero 2 W.
Install ARM64, RaspiOS Bullseye, on Raspberry Pi 4 with 8gb
For this example I'm using RaspiOS Bullseye. I still recommend using the Raspberry Pi Imager for the install.
Update, upgrade and install
sudo apt update
sudo apt upgrade -y
sudo apt install zip
Install docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
#you might need to logout and login to get the group assignment
docker run hello-world
Install SDKMAN
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk version
Install a JDK
Install the latest version of the JDK.
sdk list java
sdk install java 22.0.0.2.r17-grl
java -version
Create simple Spring Boot application
Use the Spring Initializer web API to create a simple example.
The parameters below:
- name the artifact
- choose Java 17
- include the "web","actuator" and "spring native" dependencies
- use latest version of Spring Boot by default
mkdir demo
cd demo
curl https://start.spring.io/starter.tgz -d groupId=dev.dashaun -d artifactId=spring-pi -d name=spring-pi -d packageName=dev.dashaun.spring-pi -d dependencies=web,actuator,native -d javaVersion=17 | tar -xzf -
Two changes
Starters requiring special build configuration.
Swap out the tomcat version:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.tomcat.experimental</groupId>
<artifactId>tomcat-embed-programmatic</artifactId>
<version>${tomcat.version}</version>
</dependency>
Exclude io.micrometer:micrometer-core when metrics are not used for optimized footprint.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<exclusions>
<exclusion>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</exclusion>
</exclusions>
</dependency>
Milestone reached
At this point I can generate an ARM64 native
executable using the native build tools.
Install the native-image extensions for the JDK:
gu install native-image
Build the application with Maven
./mvnw -Pnative -DskipTests package
I didn't panic when I saw the warnings during compilation, because I had read the documenation.
From the Spring Native Documentation
During the native compilation, you will see a lot of WARNING: Could not register reflection metadata messages. They are expected and will be removed in a future version, see #502 for more details.
There will be a target/spring-pi
executable, 63mb in my example.
Copy that to your Raspberry Pi Zero 2 W
and it works as expected!
Keep going
How am I supposed to handle that native executable? Where do I put it? Do I make it a Github release package? Do people use Github releases in their CI/CD?
"The container image is the new artifact."
- Cora Iberkleid
Getting Unexpected Help
I was on a path to create my own buildpack for ARM64, but I was going the long way.
Daniel Mikusa created a better way.
I followed his repository and created my own versions of the cloud native buildpacks. I pushed those images to my public docker repo.
Building an Image
The default builder <builder>paketobuildpacks/builder:tiny</builder>
doesn't have arm64 images yet.
So edit pom.xml
to use <builder>dashaun/native-builder:focal-arm64</builder>
instead.
Now you can create a native arm64 image using the spring boot plugin:
./mvnw -Pnative spring-boot:build-image -DskipTests
When that is complete, you can run the image:
docker run -p 8080:8080 spring-pi:0.0.1-SNAPSHOT
Our applicaiton is now running on port 8080
Summary
Building arm64 images on a Raspberry Pi 4 is not fast. Being able to run arm64 native images on a Raspberry Pi Zero 2 W has me super excited. I've made the cloud native buildpack images available via Docker Hub. I've also created an example repository on Github.
For me, this opens the door for a software supply chain, that creates native arm64 images for my Spring Boot projects. This also makes kubernetes on arm64 devices, as small as the Raspberry Pi Zero 2 W, much more interesting!