Multi-Architecture Spring OCI from anywhere with Paketo
Learn how to build multi-architecture Spring Boot 3.4.0 OCI images for both AMD64 and ARM64 using Paketo buildpacks.
Multi-Architecture Spring OCI from anywhere with Paketo
My favorite feature of Spring Boot 3.4.0 is the adoption of multi-architecture buildpacks. This feature empowers you to construct native images and traditional JVM-based applications for both AMD64 and ARM64, from a unified build process. The fact that OCI images will also be smaller because of the new default buildpack is a bonus.
Emotionally Invested
I've been building native images for ARM64 since 2021, because I'm a Raspberry Pi enthusiast and passionate about Spring Boot. Since that time, I've created dozens of solutions, to put production-ready, enterprise-grade, Spring Boot on Raspberry Pi ARM64 devices with only 512MB of RAM. I no longer have to support my own solution.
Getting Started
Prerequisites:
- At least Java 17
- Docker
Docker Desktop Configuration
Docker Desktop comes with built-in QEMU support for multi-architecture builds. However, if you need to install QEMU manually, you have two options:
Option 1: Using tonistiigi/binfmt
# Install QEMU support for all architectures
docker run --privileged --rm tonistiigi/binfmt --install all
This command was found here: docs.docker.com/build/building/multi-platform
Option 2: Using multiarch/qemu-user-static
# Install and configure QEMU
docker run --privileged --rm multiarch/qemu-user-static --reset -p yes
More information can be found here: github.com/multiarch/qemu-user-static
Spring Boot 3.4.0 application
First, let's create a new Spring Boot application:
mkdir mydemo
cd mydemo
curl https://start.spring.io/starter.tgz -d dependencies=web,actuator -d type=maven-project | tar -xvzf -
You could also use https://start.spring.io
Create a multi-architecture OCI Image
Here's the step-by-step process to build and publish multi-architecture images:
# Build ARM64 image
./mvnw spring-boot:build-image -Dspring-boot.build-image.imagePlatform=linux/arm64 -Dspring-boot.build-image.imageName=dashaun/blog-3-4-0:arm64
docker push dashaun/blog-3-4-0:arm64
# Build AMD64 image
./mvnw spring-boot:build-image -Dspring-boot.build-image.imagePlatform=linux/amd64 -Dspring-boot.build-image.imageName=dashaun/blog-3-4-0:amd64
docker push dashaun/blog-3-4-0:amd64
# Create and push multi-architecture manifest
docker manifest create dashaun/blog-3-4-0:multiarch --amend dashaun/blog-3-4-0:arm64 --amend dashaun/blog-3-4-0:amd64
docker manifest push dashaun/blog-3-4-0:multiarch
Inspect
Inspect the multi-architecture manifest:
docker manifest inspect dashaun/blog-3-4-0:multiarch
Inspect the manifest, results below.
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 2407,
"digest": "sha256:96d95eed9308f86a8055156913ed8710c25917bdf0389bd6d5cf2ab0a79c77fc",
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 2406,
"digest": "sha256:4c42eebe412f56a03d840d2660b9defde3820ce1c5e510ffba0067be0ae11c8e",
"platform": {
"architecture": "arm64",
"os": "linux"
}
}
]
}
The manifest shows both platforms
linux/amd64
andlinux\arm64
Looking at the Docker Hub tags Digest
sections, notice that the sha
values for the arm64
and amd64
tags match the multiarch
tag's values.
The new default buildpack, builder-jammy-java-tiny also results in much smaller images. Both of the images we created here are under 275mb. Switching to Spring Boot 3.3.6 and the previous default buildpack paketobuildpacks/builder-jammy-base results in a ~350mb image.
Building Native Images
The process for building native images is similar, with the addition of GraalVM support:
mkdir my-native-demo
cd my-native-demo
curl https://start.spring.io/starter.tgz -d dependencies=web,actuator,native -d type=maven-project | tar -xvzf -
You could also use https://start.spring.io
Building Multi-Architecture Native Images
When the buildpack see's the native-maven-plugin
in the project, it creates native images with GraalVM. Our steps won't change, we will just use different tags.
# Build ARM64 native image
./mvnw spring-boot:build-image -Dspring-boot.build-image.imagePlatform=linux/arm64 -Dspring-boot.build-image.imageName=dashaun/blog-3-4-0:native-arm64
docker push dashaun/blog-3-4-0:native-arm64
#Build AMD64 native image
./mvnw spring-boot:build-image -Dspring-boot.build-image.imagePlatform=linux/amd64 -Dspring-boot.build-image.imageName=dashaun/blog-3-4-0:native-amd64
docker push dashaun/blog-3-4-0:native-amd64
# Create and push multi-architecture manifest
docker manifest create dashaun/blog-3-4-0:native-multiarch --amend dashaun/blog-3-4-0:native-arm64 --amend dashaun/blog-3-4-0:native-amd64
docker manifest push dashaun/blog-3-4-0:native-multiarch
Inspect Native
docker manifest inspect dashaun/blog-3-4-0:native-multiarch
Inspect the manifest, results below.
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 2407,
"digest": "sha256:70311015a28ff9ebbaa0ef0552e91bd85d2220716b69a8444ecf18ae542fa3f1",
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 2406,
"digest": "sha256:4b9243d946a61f5fd8c9762f9e8a5ef107ba5a51a3679faa54b5a3cd96e4147c",
"platform": {
"architecture": "arm64",
"os": "linux"
}
}
]
}
The manifest shows both platforms
linux/amd64
andlinux/arm64
Take a look at these image sizes!
dashaun/blog-3-3-6 latest 0e61fed5cf4a 44 years ago 348MB
dashaun/blog-3-4-0 amd64 6ce61c588eba 44 years ago 268MB
dashaun/blog-3-4-0 native-amd64 9f63e381e26c 44 years ago 117MB
dashaun/blog-3-4-0 native-arm64 7a9b08bf964a 44 years ago 111MB
dashaun/blog-3-4-0 arm64 0d07d6f15c86 44 years ago 259MB
I did this experiment on MacOS with ARM64 (M3 chip), but these exact same steps can be used on either ARM64 or AMD64 machines.
Conclusion
Spring Boot 3.4.0's multi-architecture support with Paketo buildpacks revolutionizes how we build and deploy applications across different architectures. Whether you're targeting cloud platforms or edge devices like Raspberry Pi, you can now build and deploy with confidence.
The combination of:
- Multi-architecture support
- Smaller image sizes
- Native image capabilities
- Simplified build process
Makes Spring Boot 3.4.0 a compelling choice for modern cloud-native and edge-native applications.