DEV Community

DaShaun
DaShaun

Posted on • Originally published at dashaun.com on

First attempt at a multi-arch buildpack for Spring Boot 3

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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"
                }
            ]
        }
    }
]

Enter fullscreen mode Exit fullscreen mode

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 -

Enter fullscreen mode Exit fullscreen mode

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 

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

build on AMD64 or ARM64

The magical moment! You can do this from AMD64 or ARM64.

./mvnw -Pnative,dashaun spring-boot:build-image

Enter fullscreen mode Exit fullscreen mode

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'

Enter fullscreen mode Exit fullscreen mode

The | jq.[0].Architecture can be replaced with | grep Architecture if you don’t have jq 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

Enter fullscreen mode Exit fullscreen mode

Links

Thanks

Its working

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.

Top comments (0)