Why build multiarch docker images?

Amazon Web Services announced their new A1 instance type re:Invent in 2018, which are based on 64bit arm architecture powered by the Graviton processor. Since then customers have been using A1 instances as a cheaper way to run work loads in EC2. There are now even more options to use the A1 instance as AWS has just announced support for the instance type with EKS, its Kubernetes service as a public preview. But in order for you to run docker workloads on the A1’s you needed to build docker images specifically for that architecture. It was possible to then have x86_64 and arm64 images in the same repo but you had to role manifest files by hand in order to to allow end users to pull the right version for their architecture. Things just go a lot easier with Dockers edge version of the docker desktop which takes advantage of cross compilation in QEMU.

How is this possible?

So let’s look at the new features in docker desktop, QEMU comes with a tool set binfmt_misc to run and build binaries of any supported architecture via emulation. In our case we want arm support, and specifically the arm64 version to run on the A1 instances. The tool chain will however support builds for, arm/v6, arm/v7 and arm64. Which means you can build containers for arm/v7 that will run on your raspberry pi at home. Docker desktop comes equipped with a new CLI command called buildx which allows you to build multi-arch images, link them together with a manifest file, and push it all to a registry, simply, efficiently and all from one command. So far I’m aware of this working on the edge docker desktop builds for OSX and Windows, I’m hoping to try linux support asap!

Putting this to work!

So we’ll dive a little deeper with the build process and workflow, the video below will show you how to use buildx and I’ve included some instructions to get you up and running.

Now let’s look at those commands, buildx has a few commands so let’s dive into some of them here:

docker buildx

Usage:	docker buildx COMMAND

Build with BuildKit

Management Commands:
imagetools  Commands to work on images in registry

Commands:
bake        Build from a file
build       Start a build
create      Create a new builder instance
inspect     Inspect current builder instance
ls          List builder instances
rm          Remove a builder instance
stop        Stop builder instance
use         Set the current builder instance


I’ve included a Dockerfile and some code here to get you started

If you are looking to just build for one particular platform the following commands will get you started, and you can see that QEMU will run it in emulation for you also!

docker buildx build .
docker buildx build --platform linux/arm64 --load .
docker images
docker inspect <SHA>
docker run <SHA> uname -m


Now if you are looking to use the full power of buildx and build a multiarch docker image then push to a docker hub repository this is where buildx really comes into its own.

Lets setup the environment and check we can compile for different architectures:

docker buildx create --name mybuilder
docker buildx use mybuilder



If you want to see what the environment can do run the following:

docker buildx ls

NAME/NODE    DRIVER/ENDPOINT             STATUS  PLATFORMS
mybuilder    docker-container
mybuilder0 unix:///var/run/docker.sock stopped
default *    docker
default    default                     running linux/amd64, linux/arm64, linux/arm/v7, linux/arm/v6


Now for the fun bit, let’s build for multiple architectures at the same time and push to docker hub. First create yourself a new docker hub repo that we can use as the target. The run the following commands on the cloned code I provided earlier.

docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t <USER/REPO:TAG> --push .
docker buildx imagetools inspect docker.io/<USER/REPO:TAG>


You’ll see that buildx uses QEMU to build the images simultaneously and then automatically creates a manifest and pushes it to docker hub. This saves so much time to doing all these bits manually. To take a look at the manifest run the following and you’ll see three images in the manifest for different architectures.

docker buildx imagetools inspect richarvey/nginx-demo:latest
Name:      docker.io/richarvey/nginx-demo:latest
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest:    sha256:8b914bd551ea48016cb353a13d47f63d12317047a1b6f8565a4ca120aa7cb1f7

Manifests:
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform:  linux/amd64

Name:      docker.io/richarvey/nginx-demo:latest@sha256:9285415479bfe945cb77bec9a86638874be6d4aa860cc0392941a05b7a94affa
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform:  linux/arm64

Name:      docker.io/richarvey/nginx-demo:latest@sha256:4984cb8c605b94cb4087868563aa60e3df7f352b3daa3f51643f2be472fa6aa9
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform:  linux/arm/v7


Now all thats left to do is spin up some A1 and x86_64 instances and use your normal work flow,

docker run -d -p 80:80 <USER/REPO:TAG>


If you exec into those containers you can even run uname -a to see that docker automatically pulls the right architecture type for what you are running on, making it super simple for end users no matter the platform. If you already have existing workloads in AWS take a look at the A1 instances to see how you can start saving now!