Docker has become my favorite virtualization technique. It provides a high level of abstraction with clear interfaces.
Docker is cool and portable, so creating your own Docker images is tempting. The user can get at your Docker image in two ways:
- Distributing
Dockerfile
s is the smallest thing to distribute. It may come in just a few lines of code how to build the image. However, this method comes at a disadvantage: The demands on the build environment are high. - Distributing Docker Images solves this, and Docker Hub and other platforms help you distribute your images. But have you tried building them for an architecture other than your native CPU architecture? This seems extremely complicated and not well documented for anyone interested in just getting it running.
Creating Multi-Arch Docker Images
So here is a simple recipe:
- Make sure your host can execute binaries for all kinds of architectures:
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
This uses a Docker image to tell Linux that it can run 29 additional CPU architectures. Of course, this comes at a cost: Emulation using QEMU. This means, things will run much slower. But hey, they will run at all!
This is the precondition for building Docker images for a different architecture. - Install
buildx
, the Docker build extension:
Download thebuildx
for your CPU from https://github.com/docker/buildx/releases/latest and save it as~/.docker/cli-plugins/docker-buildx
. - Create a multi-architecture builder:
docker buildx create --name docker-multiarch && docker buildx inspect --builder docker-multiarch --bootstrap
- Build your multi-architecture Docker Image!
docker buildx build --builder docker-multiarch --platform linux/amd64,linux/386,linux/arm64,linux/arm/v6 <your-docker-dir>
This gives you Docker images for the named --platform
s. Of course, your selection might differ, but my use case, the Zeitgitter timestamping server, I believe that Intel/AMD servers and Raspberry Pis will be the main deployment platforms.
That’s not all, folks!
However, having the images built inside the docker-multiarch container is not going all the way. You need to make it available, either locally, or in a Docker repository. If you want to do the latter, add --push -t <repository-name>
to the command line. But now, that’s really all, folks!
Automating the process: Makefile
The following excerpt from a Makefile
shows how to automate this: [Updated 2023-10-10: Support pre-packaged buildx as well]
# Modify these according to your needs
PLATFORMS = linux/amd64,linux/arm64,linux/arm/v6
TAG = zeitgitter/zeitgitter
DOCKERDIR = zeitgitter
# This probably should remain as is
BUILDXDETECT1 = ${HOME}/.docker/cli-plugins/docker-buildx
BUILDXDETECT2 = /usr/libexec/docker/cli-plugins/docker-buildx
.PHONY: qemu buildx docker-multiarch-builder
docker-multiarch: qemu buildx docker-multiarch-builder
docker buildx build --pull --push --platform ${PLATFORMS} -t ${TAG} ${DOCKERDIR}
qemu: /proc/sys/fs/binfmt_misc/qemu-m68k
/proc/sys/fs/binfmt_misc/qemu-m68k:
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
buildx:
@if [ ! -x ${BUILDXDETECT1} -a ! -x ${BUILDXDETECT2} ]; then \
echo '*** `docker buildx` missing. See `https://github.com/docker/buildx#installing`'; \
exit 1; \
fi
docker-multiarch-builder: qemu buildx
if ! docker buildx ls | grep -w docker-multiarch > /dev/null; then docker buildx create --name docker-multiarch && docker buildx inspect --builder docker-multiarch --bootstrap; fi
Yes, the process of downloading and installing buildx
could also be automated, but it isn’t as easily reversible, so I leave it to the user. The entire Makefile can be found here.
Happy Multi-Architecture Coding!
The fine print
I learnt a lot from these writeups:
- Docker themselves explain in their Documentation how to create Multi-Arch Images for the Mac. At first glance, this did not seem like it would be that helpful, but indeed, this was the key to putting it all together, from what I had been reading before.
- Adrian Mouat has a great writeup in the Docker Blog: Multi-Platform Docker Builds. I learnt a lot there, but it was missing the information that you do not need to deal with Manifests yourselves, but that
buildx
handles this transparently for you. Thanks, buildx folks! - Christy Norman explains Manifests in detail here. But you probably don’t need to deal with Manifests yourselves anymore.
- A more “manual” QEMU-based build procedure is described by Balena in “Building ARM containers on any x86 machine, even DockerHub”. Hey, we’ve gone a long way these past three years!
If you would like to have more insight into what happens or how to go on even further, you will find a lot of information in the above articles.
I also borrowed from the original Docker image and have seen a similar design to what I made from it, but fail to remember where I found it. If anyone has a clue, please remind me, so I can credit the idea.