Buildx Mastery: Elevating Docker Development Across Architectures

“Transform Docker builds with Buildx: cross-architecture compatibility, faster workflows, and hands-on labs. Join us in shaping the future of containerization!”

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.
Figure 1: Cross Compilation
Figure 1: Cross Compilation

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.

Figure 2: Build an image using build command
Figure 2: Build an image using build command

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.

Figure 3: Build Architecture
Figure 3: Build Architecture

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

Docker manifest is a traditional way to build images for multi-platform architectures is a bit tedious and time-consuming.

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.
docker login

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 Dockerfile in the created directory to print a message using alpine base images available in different architectures.
vim Dockerfile
# Dockerfile
ARG ARCH=amd64/
FROM ${ARCH}alpine:3.8
CMD echo "Hello Docker!"

Note: We’ll provide ARG from CLI with build instructions while building an image.

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 .

Note: Change the dockerhub username name with your dockerhub username. Here it is teamcloudyuga.

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
Figure 4: Multi Architecture Images using manifest Feature
Figure 4: Multi Architecture Images using manifest Feature
  • Create a container from recently pushed manifest.
docker container run -it --name cy1 teamcloudyuga/mul-arch-ex:manifest-latest

Output of the above command.

Figure 5: Container Run from Custom Image
Figure 5: Container Run from Custom Image

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.

Figure 6: Buildx in Docker Build Architecture
Figure 6: Buildx in Docker Build Architecture

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.

Buildx 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 --load flag 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.

Figure 7: Image Build using buildx command
Figure 7: Image Build using buildx command
  • 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 app.py and requirements.txt are attached to the lab environment. 

Dockerfile

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. Dockerfile is given below.

FROM python:3.8-alpine
RUN mkdir /app
ADD . /app
WORKDIR /app
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["python", "app.py"]

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 .

Note: Change the dockerhub username name with your dockerhub username. Here it is teamcloudyuga.

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 port-8008 to access the application.

Figure 8: Run a Container Using Newly Created Image
Figure 8: Run a Container Using Newly Created Image

Verify with the Docker Hub Repo

Let’s browse the docker hub registry and find out the images in our repository.

Figure 9: Verify Image Built with Multiple Architecture on Docker-hub
Figure 9: Verify Image Built with Multiple Architecture on Docker-hub

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.override.hcldocker-bake.jsondocker-compose.yaml
docker-bake.hcldocker-bake.override.jsondocker-compose.yml
  • The docker-bake.hcl file 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.

docker-bake.hcl file is given below.

variable "TAG" {
  default = "latest"
}

group "default" {
  targets = ["webapp"]
}

target "webapp" {
  dockerfile = "Dockerfile"
  platforms = ["linux/amd64", "linux/arm64"]
  tags = ["docker.io/teamcloudyuga/webapp:${TAG}"]
}

Note: Change the dockerhub username name with your dockerhub username. Here it is teamcloudyuga.

  • 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
Figure 10: Image build Using bake Command
Figure 10: Image build Using bake Command

Clean Up

At the end, we should remove the stopped containers, unnecessary  images and builder instances.

  • To remove the builder instance.
docker builder rm app-builder

Conclusion

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.

References

[1] https://docs.docker.com/engine/reference/commandline/buildx/

[2] https://docs.docker.com/engine/reference/commandline/builder_build/

[3] https://www.youtube.com/live/0iN4dJB3OLk?si=Iiz1IWmlxoCKkcLz

[4]https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/

[5] https://docs.docker.com/engine/reference/commandline/manifest/

Join Our Newsletter

Share this article:

Table of Contents