Multi-Platform Docker Images
I recently replaced my 16” i7 MacBook Pro with the 14” M1 Pro model. It’s instantly become one of my favorite laptops I’ve ever owned (the 2nd being my iBook G4 from almost 20 years ago). I vaguely knew I’d have to do some different things for a development environment on an arm64 architecture, the main issue being running Docker containers.
Docker supports Docker Desktop for Apple Silicon, but if you rely on Docker images that only have a linux/amd64 (i.e. x86) build you’ll quickly notice that everything runs significantly slower than you’re used to. Being able to build and publish multi-platform images allows running Docker containers on M1 Macs without any emulation required.
You’d be surprised how many public images on Docker Hub don’t have a multi-platform build, so being able to build your own is necessary if you don’t want to suffer the performance penalty imposed by x86 emulation.
The instructions below are a distilled verison of the multi-platform documentation provided by Docker.
Setup Docker buildx
If you intend to publish docker images you’ll need to setup buildx, which allows the docker engine to build multi-platform images.
> docker buildx create --name multi-platform --driver docker-container --bootstrap --use
> docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
multi-platform * docker-container
multi-platform0 unix:///var/run/docker.sock running v0.10.5 linux/arm64, linux/amd64, linux/amd64/v2, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
default docker
default default running 20.10.20 linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
desktop-linux docker
desktop-linux desktop-linux running 20.10.20 linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
Updating your Dockerfile
You’ll also need to update any Dockerfile that installs software without a package manager (i.e. without using apt or yum). If you look at the Dockerfile for popular images on Docker Hub you’ll notice the same pattern, which is to use a bash case statement to set the current architecture for the build.
A simple example for building a Go image:
arch="$(dpkg --print-architecture)"
case "$arch" in
'amd64')
url="https://dl.google.com/go/go1.19.3.linux-amd64.tar.gz";
sha256="74b9640724fd4e6bb0ed2a1bc44ae813a03f1e72a4c76253e2d5c015494430ba";
;;
'arm64')
url="https://dl.google.com/go/go1.19.3.linux-arm64.tar.gz";
sha256="99de2fe112a52ab748fb175edea64b313a0c8d51d6157dba683a6be163fd5eab";
;;
*) echo >&2 "error: unsupported architecture: $arch"; exit 1 ;;
esac
Create Multi-Platform Images
Finally use buildx to build and publish multi-platform images. You can inspect the published image afterwards and see the support architectures in the manifest for the image.
docker buildx build \
--platform=linux/amd64,linux/arm64 \
-t my-image:latest \
--push \
.
docker buildx imagetools inspect my-image:latest
The end result is a docker image that you can use on your Mac locally, as well as for deployment on Linux hosts using x86 processors.