Multi-Architecutre Builds Support for Cloud Native Buildpacks — Part I

Husni Faiz
4 min readJun 7, 2023

--

Cloud Native Buildpacks

Since 2019, Linux Foundation have been offering mentorship programs to train the next generation of open source developers in collaboration with popular open source organizations. Each year registrations open for spring, summer and fall terms. This year I was lucky enough to get selected for the Linux Foundation Mentorship 2023 program for the spring term to work with the Cloud Native Buildpacks (CNB) project which is a Cloud Native Computing Foundation (CNCF) incubation project. I was mentored by Juan Bustamante (VMware), Aidan Delaney (Bloomberg) and Jerico Pena (Rapid7).

For a long time developers have been using x86 based computers and laptops. When Apple started distributing their ARM based M1 chips in their MacBooks the developers started to realize that they have to consider not only the operating systems (Linux/Windows/MacOS) of the machine but also the architecture (x86/ARM/RISC-V) of the processor when distributing software. Same is true for container images as well. But thanks to the great user experience of our modern tools you might not have noticed that when interacting with container images.

If you visit Docker hub and check an image you will see there is multiple digests listed for a single tag and each digest has an OS/ARCH information specified in front of it. For example if we check the famous hello-world image you would see something like the following.

But when you are using Docker to pull an image you would just specify the name of the image but you will not have provided your platform (OS/Architecture combination) information. By default Docker will pull the image matching the host machine’s OS and architecture. So if your x86_64(amd64) machine is running Linux then your platform is linux/amd64.

Now the question is how does the registries handle these multi-platform images. Whenever we need interoperability and standardization, we all love having “specifications”. The Open Container Initiative (OCI) specification defines Image Indexes or Manifest Lists for this purpose. Image Index is a higher level manifest which points to specific image manifests as shown above in the figure. Image Index information is stored as a JSON structure. We can use crane manifest command to inspect the manifest list.

crane manifest hello-world
{
"manifests": [
{
"digest": "sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "linux"
},
"size": 525
},
{
"digest": "sha256:084c3bdd1271adc754e2c5f6ba7046f1a2c099597dbd9643592fa8eb99981402",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v5"
},
"size": 525
},
...
...
...
{
"digest": "sha256:3522a799ae11b49426f6a60b3fcf3d249f21fbd3dc1dd346d8a49fe4f028b668",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "windows",
"os.version": "10.0.17763.4377"
},
"size": 946
}
],
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"schemaVersion": 2
}

This is our first step in supporting multi-platform in CNB, which is to create the tools required to handle Manifest Lists. So we have added a set of manifest commands to the pack-cli. We’ll explor these commands with a simple example where we need to distribute a multi-platform image.

Creating a Multi-Platform Image

Let’s distribute the following hello-world C program to amd64/linux and arm64/linux platforms.

#include <stdio.h>

#if defined(__amd64__) || defined(__x86_64__)
#define ARCH "AMD64"
#elif defined(__arm__) || defined(__aarch64__)
#define ARCH "ARM"
#else
#define ARCH "Unknown"
#endif

void main(int argc, char* argv){
#ifdef ARCH == "AMD64"
printf("Hello from AMD64\n");
#elif ARCH == "ARM"
printf("Hello from ARM\n");
#endif
}

This program just prints a string with the platform architecture and exit. Now we’ll build the images using `buildx`.

FROM alpine as build-env
RUN apk add --no-cache build-base
WORKDIR /app
COPY . .

# Compile the binaries
RUN gcc -o hello helloworld.c
FROM alpine
COPY --from=build-env /app/hello /app/hello
WORKDIR /app
CMD ["/app/hello"]
docker buildx build --platform linux/amd64,linux/arm64 -t drac98/multi-arch-gcc-test:latest --push .

Creating an Image Manifest List

pack manifest create <registry>/<org>/<repository>:<tag>
pack manifest create drac98/sample-builder:multiarch dashaun/builder-arm:tiny cnbs/sample-builder:jammy --publish
pack manifest create drac98/sample-builder:multiarch-local dashaun/builder-arm:tiny cnbs/sample-builder:jammy
pack manifest inspect drac98/sample-builder:multiarch-local
pack manifest push drac98/sample-builder:multiarch-local
pack manifest create drac98/sample-package:hello-multiarch-universe cnbs/sample-package:hello-universe cnbs/sample-package:hello-universe-windows
pack manifest inspect drac98/sample-package:hello-multiarch-universe
pack manifest annotate drac98/sample-package:hello-multiarch-universe cnbs/sample-package:hello-universe --arch amd64
pack manifest annotate drac98/sample-package:hello-multiarch-universe cnbs/sample-package:hello-universe-windows --arch amd64
pack manifest push drac98/sample-package:hello-multiarch-universe

--

--