How to do multiplatform builds with docker
Why did I write this stuff?
Setting up docker builds for multiple platforms or architectures is not very complicated, but at least I found the instructions on the Internet scarce, lacking and somehow confusing.
If you want to build multiarch/multiplatform container images with docker on your local machine, with buildx and qemu and so forth, this document describes a fast track to accomplish it. I’ll describe all steps a bit, but to be honest I don’t know much more about those steps either at this point.
My instructions are applicable for Ubuntu 22.04, but should work if slightly adjusted for other distros as well. I’ll be building for amd64 and arm64.
Set up a docker ubuntu repository and install packages
There are ubuntu specific docker setup instructions, but I’ll copy & paste the relevant bits here anyway.
First remove any possibly installed OS docker packages to avoid conflicts:
$ for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done
Add the docker apt repository and gpg key:
$ sudo apt-get update
$ sudo apt-get install ca-certificates curl gnupg
$ sudo install -m 0755 -d /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ sudo chmod a+r /etc/apt/keyrings/docker.gpg
$ echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Then install docker packages:
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Enable buildkit in docker daemon
Create or edit your /etc/docker/daemon.json to contain the following:
{
"features": {
"buildkit": true
}
}
Restart your docker daemon:
$ sudo systemctl restart docker
Start a registry container
Docker image cache doesn’t understand multiplatform images, so we’ll need something that is more like a real registry. So install one as a container:
$ docker run -d -p 5000:5000 --name registry --network host registry:2
Create docker contexts for builds
The convention seems to be that one has to create a docker context for each “builder node”. I’ll create a single context, and a single builder node per platform. So:
$ docker context create builder-amd64
$ docker context create builder-arm64
Check that you have things approximately like this:
$ docker context ls
NAME DESCRIPTION DOCKER ENDPOINT ERROR
builder-amd64 Current DOCKER_HOST based configuration unix:///var/run/docker.sock
builder-arm64 Current DOCKER_HOST based configuration unix:///var/run/docker.sock
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sock
Create buildx builders for both platforms
Create a config.toml file with the following content:
[registry."localhost:5000"]
http = true
insecure = true
Next we’ll create a buildx builders and add two nodes under it:
$ docker buildx create --use --bootstrap --name builder \
--node amd64 --platform linux/amd64 \
--driver-opt network=host \
--config config.toml builder-amd64
$ docker buildx create --append --bootstrap --name builder \
--node arm64 --platform linux/arm64 \
--driver-opt network=host \
--config config.toml builder-arm64
Check buildx builders and nodes
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
builder * docker-container
amd64 builder-amd64 running v0.11.6 linux/amd64*, linux/amd64/v2, linux/amd64/v3, linux/386
arm64 builder-arm64 running v0.11.6 linux/arm64*, linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386
builder-amd64 docker
builder-amd64 builder-amd64 running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386
builder-arm64 docker
builder-arm64 builder-arm64 running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386
default docker
default default running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386
Build something
Now instead of the usual docker build
you’ll do docker buildx build
like so:
$ docker buildx build --platform linux/amd64,linux/arm64 -t localhost:5000/YOURIMAGENAMEHERE:WHATEVERTAGHERE --push .
You will need to push the results to a “real” repository, so the image name has to have the repository location as a prefix, and the build results must be pushed to a real registyr, hence the “–push” switch.
You can inspect the buildresult like this:
$ docker buildx imagetools inspect localhost:5000/YOURIMAGENAMEHERE:WHATEVERTAGHERE
.. and the output should indicate that the image has manifests for all built platforms.
Run non-native platform container images through QEMU emulation
Install QEMU and binfmt support
$ sudo apt-get install qemu binfmt-support qemu-user-static
Run some magic scripts to enable emulation:
$ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
Run some containers with different platforms:
$ docker run --platform linux/arm64 -it ubuntu:22.04 uname -m
[..]
aarch64
$ docker run --platform linux/amd64 -it ubuntu:22.04 uname -m
[..]
x86_64
So there you have it. Multiarch builds plus the ability to run alien architecture images with emulation.