This article documents my experience in building Docker images for multi-system architectures, as well as some of my personal understandings.
Pre-requisite knowledge
CPU architectures
The mainstream CPU architectures are of two types: x86 and ARM, but they are not always named as such during development. For example, amd64 and x86_64 refer to the 64-bit architecture of x86, while arm64v8, aarch64, and arm64 refer to the 64-bit architecture of ARM.
In the docker hub, the supported architectures are listed for all major images, and you can filter the images by Architectures.
docker buildx
Prior to docker buildx, we could only build images via docker build. As the name suggests, docker buildx is an extension to docker’s build capabilities, and one of its biggest highlights is its support for multi-system architecture builds.
docker buildx for Docker v19.03+
A docker buildx build example.
|
|
We will describe this command in detail below.
docker manifest
The docker manifest manifest, which is still experimental, is a key command for building multi-system architectures. It gives us information about the hierarchy of an image, its size, signature and, most critically, the architecture supported by the image.
|
|
The platform column. This is the information about the architecture support that we are most interested in.
Comparing the digest information, you can see that it is the same as the docker hub information.
This article describes the environment
All operations in this article are based on Mac M1, Docker Desktop. All operations in this article are based on Mac M1, Docker Desktop, and may involve experimentation and buildkit features, which need to be enabled. My configuration reference:
Pulling multi-architecture mirrors
Before the Mac M1 / ARM architecture was used, pulling images didn’t seem to be that much of a hassle.
|
|
As you can see from the previous article, openjdk has different digest for different architectures, and docker will determine the architecture of the current machine and pull the version of the corresponding architecture. For example, on Mac M1, I pull the arm64 version.
We can also specify the image corresponding to the OS & architecture to be pulled by using the --platform
parameter.
|
|
The same image tag, only one copy will be saved locally, check the architecture information of the local image again, it is already amd64.
The hub side supports storing multiple mirrors according to Arch, actually using mechanisms such as manifest, but not all mirrors support manifest.
This also means that the --platform
argument does not apply to all mirrors, and you can check the Arch support of mirrors with docker manifest inspect
.
Build Multi-Architecture Mirror
I had a lot of confusion and pitfalls while investigating the option of building multi-architecture mirrors, but I ended up with the option of building multi-architecture mirrors with docker buildx and merging manifest lists with docker manifest.
Finding a parent image that supports multiple architectures
Take openjdk for example, it provides both arm64 and amd64 versions, so we’ll use it for the demo.
Java demo.
Dockerfile:
Build multi-architecture images locally
The default builder of docker buildx supports building images for linux/arm64, linux/amd64, etc. Docker achieves this capability by cross-building, so it is not limited to the CPU architecture of the build machine.
The docker buildx supports the -platform
argument, which specifies the OS & CPU architecture of the build image.
Creating a Push Manifest List
In the previous step, we have actually built a multi-architecture image, but at this point, different architectures have different tags, which is a bit different from the familiar openjdk solution. openjdk and other images use docker manifest to bind multiple architectures to the same tag.
Note that the manifest kiritomoe/java-multi-arch-demo:1.0 was finally pushed, and no other images were pushed. And after the manifest is pushed, the local copy needs to be deleted, which makes it possible to perform operations such as docker manifest inspect kiritomoe/java-multi-arch-demo:1.0
locally in the future to ensure that it is loaded from the remote repository, and the manifest only makes sense if it exists in the remote repository to make sense.
View multi-arch images from remote repositories
Successfully bind multiple architectures to the same tag.
Use the command line to view it.
|
|
Some of the “best” practices
If you research the multi-architecture support, you will find that the above mentioned solutions are not the only ones supported, and with my limited energy, I didn’t look into the history of multi-architecture support for docker in detail. If not for the project, God knows I would have spent two days researching these things. But the above solution is the simplest one I have come up with so far.
While docker implements the ability to automatically pull native images based on the compiling machine, this capability is not available in all cases. For example
- if the build machine is not controllable, then the act of compiling will also become uncontrollable.
- the build machine is not necessarily the machine that will eventually run the image
- local builds for test development scenarios
To keep it all under control, my personal advice is to follow two principles.
- Business images offer multi-arch support. For example, I chose centos for my base image (centos supports multi-arch), my local environment is Mac M1, and our company’s build machine is x86, not everyone is a docker expert, I want to make the
From centos
strategy of pulling images manageable, and I am willing to write two Dockerfiles for it: Dockerfile_amd64 and Dockerfile_arm64. I would like to write two Dockerfiles for this purpose: Dockerfile_amd64 and Dockerfile_arm64, and eventually merge the manifest of my two products to implement multi-arch. - Other generic mirrors support multi-arch while providing different Arch tags, for example, in business scenarios, several types of base mirrors are generally required
- Base images for java applications: java-base:1.0, java-base:1.0-aarch64, java-base:1.0-x86_64
- Base images for front-end applications: nginx-base:1.0, nginx-base:1.0-aarch64, nginx-base:1.0-x86_64
- Base images for general-purpose applications: centos-base:1.0, centos-base:1.0-aarch64, centos-base:1.0-x86_64
While I am aware that it is possible to accurately pull the image of a given Arch via sha256, it would add a lot of cost to understanding.