Docker offers an easy method of packaging and deploying applications, which is truly an amazing technology. Earlier, the developers built an image on one type of architecture and deployed it on the same kind of architecture only. They were not able to run it on multiple architectures. To remove this limitation, docker introduces the concept of multi-arch images.
Docker has the ability to manage multi-architecture containers. Using the same official container images and tag, we can pull the images and deploy them on different architectures like Intel AMD, or ARM. Due to the power of multi-arch images, Docker will automatically select the variation that matches the OS and CPU combination. There is no difference in its utilization from the user's point of view, but the question is how do we develop them?
This hands-on lab will help you learn how to develop multi-arch images using buildx, a new docker feature. First, we’ll understand cross-compilation.
What is Cross-Compilation in the Image Life Cycle?
Cross-compilation means a binary that builds on one type of platform should run on another type of platform. It is required to differentiate the
- Build-platform on which the compilation is performed to create an image. For example, the amd64 platform is used to build a binary.
- Host-platform on which the executable provided by the build-platform is expected to run that would be a similar or different platform. For example, the amd64/arm64 platform is used to run a binary.
To achieve cross-compilation while building an image we need a docker builder. In the next section, We will explore a builder.
What is a Builder and Why is it Required?
In general, whoever constructs something is called a builder. In the context of docker, currently, a builder is an instance of a buildkit daemon that is used to build a container image for multiple platforms.
Earlier with the
docker build command that uses legacy builder we can build an image that adopts the same architecture of the host machine and has limited capabilities.
We can create an image with one platform at a time without creating a builder instance using
--build-arg linux/arm64 or
--build-arg linux/amd64 option. But now, Builder is created with a docker-container driver, allowing us to create multiple images simultaneously. To execute a build process, the builder pulls different base images that are compatible with the specified platforms and builds images for different platforms.
Architecture of Docker Build
Build command is used to manage and run build operations. It is a user interface. We can instruct the server to start building using build. On the other hand, a builder is a server that manages the build execution. Build supports client-server architecture. As shown in the figure below, earlier docker build was using a legacy builder to build images.
Limitations of Docker build
- Docker build can not create an image with multiple architectures.
- Build has limited functionality as it accepts arguments only.
To build multi-arch images docker introduces docker manifest.
Docker manifest is a traditional way to build images for multi-platform architectures is a bit tedious and time-consuming.
Please click on the
Lab Setup button to earn hands-on experience. We will install the docker engine for the Ubuntu distribution of Linux using the script as it is recommended for testing and development environments.
- Download and run the script.
curl -fsSL https://get.docker.com -o install-docker.sh sudo sh install-docker.sh
- Login into the dockerhub account to push the images.
Let’s try to build a simple image from the Dockerfile with multiple platforms like arm64v8, amd64, etc.
- Create a directory.
mkdir manifest-ex && cd manifest-ex
- Write a
Dockerfilein the created directory to print a message using alpine base images available in different architectures.
Build multiple images
Build multiple images with different architectures and push to docker hub registry to create a single manifest.
- Build an images from multiple architecture.
docker build -t teamcloudyuga/mul-arch-ex:manifest-amd64 --build-arg ARCH=amd64/ --push .
docker build -t teamcloudyuga/mul-arch-ex:manifest-arm64v8 --build-arg ARCH=arm64v8/ --push .
Create a manifest list
Create a manifest list to put all the images with different architecture under the same image tag.
docker manifest create teamcloudyuga/mul-arch-ex:manifest-latest --amend teamcloudyuga/mul-arch-ex:manifest-amd64 --amend teamcloudyuga/mul-arch-ex:manifest-arm64v8
Verify the images architecture
Inspect the manifest to get the information of the platform which includes os and architecture of all the images listed in the manifest.
docker manifest inspect teamcloudyuga/mul-arch-ex:manifest-latest
Push manifest list to the docker hub registry
Using manifest, push multiple images under single tag to the docker hub registry.
docker manifest push teamcloudyuga/mul-arch-ex:manifest-latest
- Browse https://hub.docker.com/ website to verify our image.
- Create a container from recently pushed manifest.
docker container run -it --name cy1 teamcloudyuga/mul-arch-ex:manifest-latest
Output of the above command.
Finally, we created a multi-arch image and verified it. The whole process is tedious and time consuming so docker provides a multi-arch image building feature with buildx which is faster and a single command is required to build multiple images using custom builder.
What is buildx?
Docker comes up with a docker buildx command that extends the capabilities of a build command.
The standard build client used in older versions of Docker Engine and Docker Desktop has been replaced by Buildx. We use Buildx by default when we use the docker build command in more recent versions of Docker Desktop and Docker Engine.
Currently buildx is an experimental docker CLI plugin that is used to extend the builder’s functionalities and supports all the features provided by the BuildKit builder toolkit. All builds executed with buildx will run with the Moby Buildkit builder engine.
Docker is the default driver to build an image. We can not create multi-arch images using the default driver so we need to create a custom builder which supports docker-container drivers.
The driver controls the behaviour of a builder instance. Buildx supports the following drivers,
- Docker driver: The default driver created by buildkit daemon is docker driver. It does not support multi-arch images. The image is built with a default docker driver that can automatically store in the local image registry.
- Docker container driver: It supports multi-arch image building and cache features. The image built with docker container driver can not automatically load into the local image registry. To obtain the image locally we need to add
--loadflag to buildx command while image building.
- Kubernetes driver: It is used to create buildkit pods in kubernetes cluster.
- Remote driver: It is used to connect with the buildkit daemon runs on different hosts.
Now it's time to experiment with a sample application.
Build a sample Application
We will create a simple Python app, write a single Dockerfile to build images using buildx, verify in the docker hub registry, and run the image on a different platform as shown in the figure.
- To get the docker builder version.
docker builder version
- To list the builder instance.
docker builder ls
Create a Python App
We’ll create a webpage with a simple Hello message to demonstrate the image build process. Python file
requirements.txt are attached to the lab environment.
Dockerfile is a key requirement to assemble an image. It is a text file that contains a set of commands to run on CLI to build an image.The Following
Dockerfile is attached to the lab environment.
Here, buildkit will pull different versions of python:3.8-alpine image parallely to build images for different platforms.
Create and set a custom builder
We will create a builder instance of a buildkit daemon using the builder or buildx command to build multiple images of different architecture using a single command.
- Create a builder and list it.
docker buildx create --name app-builder
docker buildx ls
You will see that the new custom builder has a docker-container driver.
- To set the custom builder instance.
docker buildx use app-builder
You will see that the new custom builder is still inactive.
- To activate the custom builder immediately. (It is an optional step. we can directly trigger it in the build process)
docker buildx inspect app-builder --bootstrap
- List the docker builder instance.
docker buildx ls
We can see the status of app-builder is now running, and it is currently active builder with sign * on it.
Trigger the build with buildx
- Build a multi-arch images with the buildx.
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t teamcloudyuga/mul-arch-img --push .
Run a container from image
If you have a
Linux/arm64 processor, then verify by running containers on it, or else find images in the docker hub repo.
- Create a container from the multi-arch image.
docker run --name cy-cont1 -p 8008:5000 -it teamcloudyuga/mul-arch-img
Click on the
app-port-8008 under Lab URLs to access the application.
Verify with the Docker Hub Repo
Let’s browse the docker hub registry and find out the images in our repository.
Docker is smart enough to pull the image as per the CPU and OS compatibility. Let’s understand one more variation in image-building using the bake command.
Build an image using the bake command
Bake command is used to build an image using other formats like
JSON, HCL, or
YAML. Following are the default file names to build an image
docker-bake.hclfile is attached to the lab. It defines the image-building instruction. The bake command will use this file as a default choice to build an image.
Note: Dockerfile must be there in the same directory of the hcl file to work with the following code else we need to specify a path of Dockerfile in
docker-bake.hcl file is attached to the lab environment.
- Apply the following command to build and push the image to the docker hub as per the above file definition.
docker buildx bake --push
If we provide a custom file name add the
-f flag with the filename in the above command.
- Verify with the docker hub
At the end, we should remove the stopped containers, unnecessary images and builder instances.
- To remove the builder instance.
docker builder rm app-builder
We can create multi-arch images using the docker manifest. But it is tedious and time-consuming. In this Hands-on lab we learned how to use buildx to construct images for multiple architectures in an easy way.