3.5 KiB
| title | description | keywords |
|---|---|---|
| Building multi-platform images | Different strategies for building multi-platform images | build, buildx, buildkit, multi-platform images |
BuildKit is designed to work well for building for multiple platforms and not only for the architecture and operating system that the user invoking the build happens to run.
When you invoke a build, you can set the --platform flag to specify the target
platform for the build output, (for example, linux/amd64, linux/arm64, or
darwin/amd64).
When the current builder instance is backed by the docker-container driver,
you can specify multiple platforms together. In this case, it builds a manifest
list which contains images for all specified architectures. When you use this
image in docker run or
docker service, Docker picks
the correct image based on the node's platform.
You can build multi-platform images using three different strategies that are supported by Buildx and Dockerfiles:
- Using the QEMU emulation support in the kernel
- Building on multiple native nodes using the same builder instance
- Using a stage in Dockerfile to cross-compile to different architectures
QEMU is the easiest way to get started if your node already supports it (for
example. if you are using Docker Desktop). It requires no changes to your
Dockerfile and BuildKit automatically detects the secondary architectures that
are available. When BuildKit needs to run a binary for a different architecture,
it automatically loads it through a binary registered in the binfmt_misc
handler.
For QEMU binaries registered with binfmt_misc on the host OS to work
transparently inside containers, they must be statically compiled and registered
with the fix_binary flag. This requires a kernel >= 4.8 and
binfmt-support >= 2.1.7. You can check for proper registration by checking if
F is among the flags in /proc/sys/fs/binfmt_misc/qemu-*. While Docker
Desktop comes preconfigured with binfmt_misc support for additional platforms,
for other installations it likely needs to be installed using
tonistiigi/binfmt{:target="blank" rel="noopener" class=""}
image.
$ docker run --privileged --rm tonistiigi/binfmt --install all
Using multiple native nodes provide better support for more complicated cases
that are not handled by QEMU and generally have better performance. You can
add additional nodes to the builder instance using the --append flag.
Assuming contexts node-amd64 and node-arm64 exist in docker context ls;
$ docker buildx create --use --name mybuild node-amd64
mybuild
$ docker buildx create --append --name mybuild node-arm64
$ docker buildx build --platform linux/amd64,linux/arm64 .
Finally, depending on your project, the language that you use may have good
support for cross-compilation. In that case, multi-stage builds in Dockerfiles
can be effectively used to build binaries for the platform specified with
--platform using the native architecture of the build node. A list of build
arguments like BUILDPLATFORM and TARGETPLATFORM is available automatically
inside your Dockerfile and can be leveraged by the processes running as part
of your build.
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" > /log
FROM alpine
COPY --from=build /log /log