I’m still giddy; giddy about Docker buildx; giddy about creating multi-arch Docker images on my Mac and having them uploaded and properly stored on Docker Hub with a single, simple command.
Why I needed Docker buildx
Docker makes my life so much easier – period.
Unless there is really good reason to not Dockerize a piece of software it should be.
I run a small water-proof Raspberry Pi on the roof of my house. It has been serving as a LoRaWAN gateway for TheThingsNetwork for the past three years. Hence, it’s one of the devices here that’s on 24/7. Needless to say that the gateway software runs in a Docker container I helped create.
Consequently, I sometimes build new images for that RPi and since it runs on ARM architecture, rather than e.g. x86, creating such images is a little more involved (than it should). You can’t just build such images on Docker Hub. In order to work around that you can build on a CI platform like Travis CI using QEMU or build them on the target platform. The latter isn’t exactly convenient if you want to support multiple architectures.
Of course, if you make an image public it should ideally support multiple architectures in order to be useful for as many users as possible.
To cut a long story short: I want to write a single Dockerfile whose images run on ARM for e.g. RPi and x86 for everyone else.
Build multi-arch images with Docker buildx
My inspiration & guidance for the use of Docker buildx have been a Docker engineering post from April and a post from Collabnix.
Enable experimental Docker features
buildx comes bundled with Docker CE starting with 19.03. However, at the time of this writing it still requires experimental mode to be enabled. To do so you can either
- activate it globally by going to Docker preferences > Deamon > enable checkbox
- set the
DOCKER_CLI_EXPERIMENTAL=enabled
environment variable whenever needed e.g. in a build script - permanently add
"experimental": "enabled"
to the CLI configuration file~/.docker/config.json
Create a buildx builder
Start by listing all architectures your Docker installation supports.
~/Data/my-image > docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
default docker
default default running linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
Then create a new buildx builder and use it.
~/Data/my-image > docker buildx create --name my-builder
my-builder
~/Data/my-image > docker buildx use my-builder
To get insight into what it was created you can inspect the new builder
~/Data/my-image > docker buildx inspect --bootstrap
Name: my-builder
Driver: docker-container
Nodes:
Name: my-builder0
Endpoint: unix:///var/run/docker.sock
Status: running
Platforms: linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
Build and push the multi-arch image
With the preparation done building & pushing multi-arch images is now down to a single command. Most noteworthy is how similar this is to what you might already be familiar with.
~/Data/my-image > docker buildx build --platform \
linux/amd64,linux/arm64,linux/arm/v7 \
-t marcelstoer/my-image --push .

Once done you’ll find a couple of freshly pressed images on Docker Hub – one for every architecture you selected. Yay!

Oh, and in case you were wondering…Yes, when you do a docker pull
for this image Docker automatically selects the one for the matching architecture.