Build and deploy multiarch Docker images like a pro

Dominik Deschner
medialesson
Published in
4 min readMar 30, 2022

Containerizing applications with engines like Docker is a fast and convenient way to distribute and consume applications, that come with batteries included and are ready to run without setting up dependencies and installing. Especially edge enabled IoT use cases tend to be a predestined scope where containers can shine, since they not only require modular and scalable runtime platforms but also need to deploy diverse and independent pieces of software.

Photo by Ian Taylor on Unsplash

Even though Docker provides us with a high level of abstraction and isolation there are certain limitations when it comes to interoperability. Docker adds a layer of abstraction by jailing the container with the user space binaries provided by the image in charge which contains all required dependencies for the containerized application to run. These binaries are compiled for a specific target platform which implies that a container can only be executed on the platform for what it was built.

During the development of IoT scenarios it can be beneficial to have different environments where your solution is deployed, e.g., a demo environment in the cloud for easy discussion with your stakeholders, on your Raspberry PI for integration testing on an embedded device and in the end your solution should maybe run on an ARM64 SoC. That means we need to build our containers for all required platforms. In this article I’d like to guide you through this process.

Manifests

Docker provides us with a convenient way to consume images without explicitly specifying the targeted platform. So called manifests help us to wrap images built for different platforms with a common name. E.g. in a manifest named docker-demo/hello-world:latest we could include docker-demo/hello-world:amd64 and docker-demo/hello-world:armv7. That would allow us to run docker-demo/hello-world:latest on a Raspberry PI and our desktop environment.

Running ``docker manifest inspect -v debian:11`` will give us better insights about how manifests work. We can see that the manifest supports at least amd64 and armv5. The Ref property contains the reference to the image that was built for the corresponding platform. E.g., you could do

docker run docker.io/library/debian:11@sha256:039f72a400b48c272c6348f0a3f749509b18e611901a21379abc7eb6edd53392

when you are running on an amd64 platform. Now that we gained superficial understanding on how Docker manifests work we want to create our own manifest linking images for different platforms.

Docker manifest inspect displays the contents of a manifest
Docker manifest inspect allows us to see what is contained in a manifest

Targeting multiple platforms

In order to create a manifest that can run on different platforms we first need to build concrete images for the targeted platforms and then tie them up in a manifest. There are several ways to achieve this but I want to demonstrate the most straightforward solution: using the buildx cli extension. Buildx is a Docker official cli plugin that abstracts lots of the technical details of building multiarch manifests.

If you do not use Docker Desktop for Windows or Mac you first have to install the tool. Then we are able to open a terminal and navigate to the Dockerfile we want to build.

First we need to create our own builder instance where we create our images:

docker buildx create --driver-opt network=host --use

You can omit drive-opt-network=host if you do not plan to push the manifest to your local Docker registry. After that we proceed and install some tools needed to emulate different platforms:

docker run --rm --privileged tonistiigi/binfmt --install all

Now finally we can make buildx build our images:

docker buildx build -t localhost:5000/hello-medialesson:demo --push . --platform=linux/arm64 --platform=linux/amd64 --platform=linux/arm/v7

Now let’s see whether it worked out.

docker buildx imagetools inspect localhost:5000/hello-medialesson:demo

The output should display something like shown below which clearly states that we managed to build our image for arm64, amd64 and armv7. So that means you can pull and run that image on all available Raspberry Pis and your desktop machine or in the cloud natively.

Inspecting our freshly built manifest

But make sure to build your image from a base image that also supports the platforms you are targeting in your build. Buildx will eventually complete sucessfully and create a manifest containing images for the different platforms. But they just won’t run on the target platform since the binary format does not match. So always inspect the your image base and make sure that it supports your target platforms.

Conclusion

Nowadays it is really easy to build crossplatform Docker images without caring for the technical heavy lifting behind the scenes. So buildx makes our lives easier and adds little overhead to the build process. In scenarios where the slightest chance exists that an image should run on another platform I go straight with Docker buildx and create cross platform Docker images.

--

--