First attempt at a multi-arch buildpack for Spring Boot 3
DaShaun
Posted on November 16, 2022
My previous builder generated some fabulous feedback, including an issue in GitHub. It also opened the door for some wonderful collaborations while I have been traveling to Barcelona and Tel Aviv.
On this journey of trying to get ARM64 support into the upstream Paketo buildpacks I have learned so much. The next step is to prove that we can create the pipelines that can deliver a multi-architecture image.
Skip to the Quick Start here
Docker manifest
In order to see the architecture of an image, you can use the docker manifest
command.
# Inspect shows the image architecture
docker manifest inspect --verbose dashaun/java-native-builder-arm64:7.37.0
Inspecting the ARM64 builder that was created in earlier article
Create a new manifest list
The process for slapping docker images together is pretty simple. Ideally you should be using the “same” image, but we are just proving a point here.
My theory is that if I provide an arm64
architecture and an amd64
architecture to the same manifest list, that it will work as expected.
# create a new manifest with a name and tag
docker manifest create dashaun/java-native-builder-multiarch:7.37.0 \
# add ARM64 builder
--amend dashaun/java-native-builder-arm64:7.37.0 \
# add Paketo tiny builder for AMD64
--amend paketobuildpacks/builder:tiny
# push the manifest to the repository
docker manifest push dashaun/java-native-builder-multiarch:7.37.0
That’s it!
inspect the manifest list - the builder
Now you can inspect the manifest and see that it has packaged ARM64 and AMD64 architectures. This capability has been available for years. It is also surprisingly simple to accomplish as you can see.
docker manifest inspect --verbose dashaun/java-native-builder-multiarch:7.37.0
[
{
"Ref": "docker.io/dashaun/java-native-builder-arm64:7.37.0",
"Descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:80771c9c2efee25020ba60e320c41cc385a333434ad2982ed7326a0a413b9ae7",
"size": 5150,
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
"SchemaV2Manifest": {
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 12587,
"digest": "sha256:8cd1ead83689b853055ac2de0cc07c5f3c9d8d087b2ac97e838551fe8e962e00"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 27195998,
"digest": "sha256:4e7e0215f4adc2c48ad9cb3b3781e21d474b477587f85682c2e2975ae91dce9d"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 4348,
"digest": "sha256:fc2e670f062f6edbb1b3be6aae88cbc0978a5273314c365bc4e99d861dfb5ff1"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 103281689,
"digest": "sha256:cba3892e6fcca4b47983803d53b777bc05476639fcfc1a2d4f384eac40363677"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 179,
"digest": "sha256:f5230b5f167cfc41fb9f96d53de0d9c5832e3b03e3a705052cf42ceada13aea9"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 197,
"digest": "sha256:357fefdf9bc907107a38600cf8d79c713346dc97370273d1aa79635d97a2f6f9"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 8870380,
"digest": "sha256:0ab7ccc1e1a42589b53d1397672009a979b296933efc276d79d3a2cdc336656c"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2498256,
"digest": "sha256:fa161ac7a0015773c3f6890d6f1824957a6e21cee5a9bbf8ddc60e4e6d30a3ed"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1806073,
"digest": "sha256:bdfdd20caee6a7044607d11d9a4f758a007ad2d61399ea5e3db22a7037ab4975"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 3273385,
"digest": "sha256:bc251569386a8e5fd513c85d9c51e76a258f0e59d0f945e185e101930012c9db"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1509592,
"digest": "sha256:caeca36964d2da6c2b88abd998281e4fd5b708cfdd1440486f6acf7bc569292e"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 3191207,
"digest": "sha256:f840d9ee444653d04a2181d1d99e4f579d01b56349cce9325c6dade9af410998"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 3273637,
"digest": "sha256:b4f56cede28073295dc750e93678876a55b9fdafd97c940b97dc9a01ae345663"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2387292,
"digest": "sha256:cebaf5f7c6e3350f5973f2c77f9ba90138d5a6bb2f1eb9e86084c8cf9f6117c6"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 4208510,
"digest": "sha256:47be0bf349071cb594970ef2d18f383b91231645ba49f27b5b39b47d9090aedc"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1818619,
"digest": "sha256:5844db3d59f2bdc13aa19a6fd09789cf4a12aa87088759840a79c10eb01f93c5"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1569855,
"digest": "sha256:89975d6b7daf9b8cf8b1a8350461e9e009b0ffc2a482becb9e1073adc3475153"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2413551,
"digest": "sha256:aca3b8db4220fa7d69bf9cbd0bebc048cfa381174929828affb820c5b6b0f505"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2480839,
"digest": "sha256:71bd4e702bfc97f1db60e6a5ac364897b2818dc2f92f1bf9d78c2935a1ae125d"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2521983,
"digest": "sha256:3e1f506f69017dbcedb2a2b022ff0d6e63d4c0cd44e32fdcf6cc9e39b2416ade"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2475996,
"digest": "sha256:f617bf8af0912672e16ed61332d6e7ad1f0ffc4fffe049da45ea670a1c441afd"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 362,
"digest": "sha256:0e3fabdd36281c4c1c946d09854510dc82ffd61476d62aaa474b3c64d8969ae3"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 151,
"digest": "sha256:9602e7831cd64261f173d4789e26f9cada67107ba1688409f7781bd737792b58"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 32,
"digest": "sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1"
}
]
}
},
{
"Ref": "docker.io/paketobuildpacks/builder:tiny",
"Descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:e5b4e4e720598e905baa793a94259ee75827949a6ba22a3d3196eb332e475dd6",
"size": 9773,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"SchemaV2Manifest": {
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 64488,
"digest": "sha256:94a3f804e111904682577130709e0cac6e0541e6b1931c1f9aa4f27326c974fc"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 29589398,
"digest": "sha256:38496a9cab3e2bb43fa8ae1d45c3ddc1d2f89328e1f3973f029949b8cb2dd5fe"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 247,
"digest": "sha256:2e7826560210e2f0d4249f9ea20651fecd45306c2482767274c8c37289c23788"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 316,
"digest": "sha256:6013ff26a0374342ff4848b3171ddbe396413d80faa2b1ee67ca9afbfa64cafc"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 106179950,
"digest": "sha256:87196f2fd40c0ee5587ff771878aad41b42c1d29628c75fff9206b5c0b77f016"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 188,
"digest": "sha256:d61afba38b8e093dfc46b8e81c141b2d7eddc0903b0fc530b3af36a46a144af7"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2744757,
"digest": "sha256:856e5ee7330534a520c3fd1d90260e9a5e99bfc182e0c2c3b18f7fea06f0bb0e"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 807,
"digest": "sha256:7eededf5887404e40dfc0d1d533fb9db3fe6b725213e4f3edda1ca1f5b1492c4"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 197,
"digest": "sha256:357fefdf9bc907107a38600cf8d79c713346dc97370273d1aa79635d97a2f6f9"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 9753557,
"digest": "sha256:d0d113e4e2bd21dbaa2f66e687ec4a23e4e83331b7845b85d0c10de1da05b08d"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2574003,
"digest": "sha256:41869a3aefd1a5bf8db003eb401d1af5ef2568c92262c3038f553d38145f36a1"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2573999,
"digest": "sha256:053ee41df41d6484ffec64fda64937a6418a1404f602487019fccddbad2958de"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 4641532,
"digest": "sha256:0fec499a25de5a35aa814bed46943a93c94d0e6785eedc679874ec3da04bc748"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2525673,
"digest": "sha256:c7b3308e13d3f30bde70d2293590aefdb95e52784b8bb840e13c587d354604e6"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 8714351,
"digest": "sha256:51918c870f73c8ceffc527e79041e08d3101beeabd0c0bc44555b6faf1188cdd"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 3197061,
"digest": "sha256:fbb9179cbbb1aa191e9ec19e77b7e6226b97ba8ac9d382822970ddcc61ce05e2"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2661004,
"digest": "sha256:38876f988da32a7ef093d5245f6923bb9b748362834345a3e8995f72ef49e168"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2547018,
"digest": "sha256:b847bb2fe3fdc6bd69cd7d690bcef5a1399310ce17bea49bbd1d5fd3641d0140"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 3291941,
"digest": "sha256:78b11fcb7ca30ba09defc8dcfc1298ea20b970430d9836b4bc7c404840b49a03"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 3255182,
"digest": "sha256:18ee4cd834fc95727859c15eaedc1f5670c4e9f3f74e3f45a108d5b9167344ab"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1576728,
"digest": "sha256:ad443f24b1ed77880e534a0c1aca0ca5a722e866b7611a85e9fd7ef3463dea08"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1511595,
"digest": "sha256:a6cd17527f03923a723839bdbe57ec200a008de6c8b35c0c4d900a675adac014"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2689335,
"digest": "sha256:cd146e648d04aaf6161526eb8be96d3bf9c8fc22e433c4e6834dc972b4f09830"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 3478686,
"digest": "sha256:bf32d3cbc5fb2d1c54c54f964b11e10d70c58f340dc17857d45db246807bcca6"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2639800,
"digest": "sha256:c505716a4281aec3fc577e417a29d23a19d1ff720f498aef49b9ed362ab53d88"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 3382413,
"digest": "sha256:17c8e16edff8afd8ef3b3b1d1d061351245287c59c6ab8846c1af94b414e1865"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 555,
"digest": "sha256:57bf259ba83920a4e2575582423c084670209a37fdb503fbbc7e10d75a5419d0"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2798929,
"digest": "sha256:e71af6bec187b3f6627f52f6f2808c6b946b5cc09b7f4a6ee006cc5557254ebc"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1678930,
"digest": "sha256:fcf29a1baad8cb3d657d685b47ab630602a5c47b333f7ed9d7cbcae68a300801"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1826608,
"digest": "sha256:fad10dc586613d133e71639507d157251d8d533d9c94c3897ed5ae6c557bcdf7"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2637303,
"digest": "sha256:bc22ffea56cce18044465269f6958bfb8ef37e182c82c6ab9f2821525c034717"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1623998,
"digest": "sha256:63fd16f5b2cc4af356f18b61281870994919fcb17f3a51f490fc09b6cd5e0cfe"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 3091878,
"digest": "sha256:71dea8ea27aab08e92be2cb1c61ba12693f8dc622575cd9087e689fdabd048fd"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 8496432,
"digest": "sha256:547d0874d2b5a13d766fc98a54725cce8b5ec50af624d5c21b971ca0e668d7e0"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2644647,
"digest": "sha256:27bd211db33f03f1ad558807c8312a3bf537e154b591f7067a7eab8d9364f10b"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 5318,
"digest": "sha256:3455451f8d3fd909057c5f94d9c34b5618fa6f62db51ed540cd7931c146a5fd6"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 3472177,
"digest": "sha256:a77b05080df7a983ee586813839673e2dfdb852c87589ddf0900517bc928b779"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 8659224,
"digest": "sha256:6a58fb7cc75d73d08a6d85d995ad767e5693c4afd8b4f94f89f61018a3925c67"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 5136,
"digest": "sha256:437d9ac90cf3a6df605fbc9b691455242c3f4d7a3ba89b683daa5885314f1909"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 4385434,
"digest": "sha256:52814f0ca0c9d85074d817b558ccb3583cdb394d1ec02cf3e45e860b24ef3f7b"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1838119,
"digest": "sha256:7e8facf10ccafea1b9688a4a4ba424d36f92e3e4cea1e913577bb45eb5bdc5f9"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2464280,
"digest": "sha256:b8531cb5032999225be8589ab8bec5ea275fffb47353863c29d1afcdf7966104"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 3468583,
"digest": "sha256:55e985d488da8faeaa9772b7cfb3073d458fa626c89a390b292ab879be30f157"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 214,
"digest": "sha256:7d37e76963e86dbc76a9cbd3a6afa21c127bb790f77415b608da57daa9019f10"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 187,
"digest": "sha256:3aaaa9c943efae7533b9a28a28a676354866cad58208bcd7988b055c754653f0"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 32,
"digest": "sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1"
}
]
}
}
]
validation
Create a Spring Boot 3 application with web
and actuator
which is enough for a simple test.
# Create a Spring Boot 3 application
curl https://start.spring.io/starter.tgz -d dependencies=web,actuator -d javaVersion=17 -d bootVersion=3.0.0-SNAPSHOT -d type=maven-project | tar -xzf -
I’m using SNAPSHOT, because YOLO, but RC2 is already available!
Add a profile to the pom.xml, that will use the multi-arch
builder that we created above.
# Remove the last line of the pom.xml file (on Mac OS X)
sed -i '' -e '$ d' pom.xml
# Add the new profile to the end of the pom.xml
echo "
<profiles>
<profile>
<id>dashaun</id>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>dashaun/java-native-builder-multiarch:7.37.0</builder>
</image>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>" >> pom.xml
Now your project will use the multi-architecture builder that created.
optional
To watch, and be amazed, by the correct images being pulled down to your machine, run these commands to remove any unused images from your machine.
docker kill $(docker ps -q)
docker system prune -a --volumes
build on AMD64 or ARM64
The magical moment! You can do this from AMD64 or ARM64.
./mvnw -Pnative,dashaun spring-boot:build-image
You should have an image created. One layer of that image, is a statically linked binary for your architecture.
inspect the new image.
# find the local image
docker images | grep demo
# Look at the architecture for the image
docker inspect demo:0.0.1-SNAPSHOT | jq '.[0].Architecture'
The
| jq.[0].Architecture
can be replaced with| grep Architecture
if you don’t havejq
installed
run and test the image
Run the OCI image with docker
to start up the server. It should start quickly!
# Forward the port, run in the background, but see the startup time
docker run -p 8080:8080 demo:0.0.1-SNAPSHOT
# Check the endpoint to validate
http :8080/actuator/health
Links
- GitHub dashaun/java-native-builder-multiarch
- Docker dashaun/java-native-builder-multiarch
- dashaun/paketo-arm64
- Related Post
Thanks
Finally
I want to hear from you! All of my social links are here at dashaun.com.
Issues and feedback can be left in the GitHub dashaun/java-native-builder-multiarch repository.
Posted on November 16, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.