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.

dmikusa-pivotal/paketo-arm64

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!