CircleCI builds multi-arch Docker image for amd64, arm64, risc64 …

Photo by Gavin Allanwood on Unsplash

Hi, in this article, I will be sharing a detailed guide on how to build a multi-arch Docker image with CircleCI.

We already knew the second-generation AWS Graviton of Aws Ec2, 6g, which using 64-bit Arm Neoverse cores and able to provide up to 40% better price performance. That made us excitedly plan to update our service docker images for running with Arm processors.

The very challenge here, our CI/CD is handling by CircleCI, a perfectly release pipeline. But unfortunately, the standard CircleCI docker executor doesn’t ready for the buildx feature, a CLI plugin support BuildKit, for building arm docker image.

The solution

After did some research, we found out a feasible solution by applying:

First, How an arm Single-arch CircleCI config file look like

First, below is our CircleCI config.yml for building an Arm docker image.

version: 2.1
commands:
setup:
steps:
- checkout
- run:
name: Setup buildx and qemu
command: |
sudo apt-get update
sudo apt-get install -y qemu-user-static
sudo apt-get install -y binfmt-support
- run:
name: Check versions
command: |
qemu-aarch64-static --version
update-binfmts --version
- run:
name: Create builder
command: |
export DOCKER_CLI_EXPERIMENTAL=enabled
docker buildx create --name arm-builder
docker buildx use arm-builder
docker buildx inspect --bootstrap
jobs:
build:
machine:
image: 'ubuntu-2004:202010-01'
steps:
- setup
- run:
name: Build ARM docker
command: |
DOCKER_CLI_EXPERIMENTAL=enabled docker buildx build --platform
linux/arm64 --load -t arm-image .
workflows:
version: 2
build:
jobs:
- build

After the build succeeded, just ssh to circleCI build job and run docker manifest inspect command to confirm the image in arm architect

docker manifest inspect ‐‐verbose arm-image:latest#The output should have json like:
# ...
# "platform": {
# "architecture": "arm",
# "os": "linux",
# "variant": "v8"
# }
# ...

For Multi-arch, we just simply update the job where buildx command with -- push

Before change to push, don’t forget to login to your docker registry, here we login to our ECR.

jobs:
build:
machine:
image: 'ubuntu-2004:202010-01'
steps:
- setup
- aws-ecr/ecr-login:
account-url: YOUR_ECR_URL
region: YOUR_AWS_DEFAULT_REGION
- run:
name: Build ARM docker
command: |
DOCKER_CLI_EXPERIMENTAL=enabled docker buildx build --platform
linux/arm64,linux/amd64,linux/386,linux/riscv64 --push -t arm-image .

After the build and push, our docker image should be confirmed multi-arch with docker manifest inspect

docker manifest inspect ‐‐verbose xxxxx.dkr.ecr.{aws-region}.amazonaws.com/arm-image:latest#The output should have json like:
# ...
# "platform": {
# "architecture": "arm64",
# "os": "linux"
# }
# ....
# "platform": {
# "architecture": "amd64",
# "os": "linux",
# }
# ...

Using this multi-arch docker image

We just simply pull the docker image to run it, base on the machine we run this docker the suitable image architecture will be pulled.

#If you stand in arm64 machine (C6g instance)
docker pull xxxxx.dkr.ecr.{aws-region}.amazonaws.com/arm-image:latest
#Checking the image architecture
docker inspect arm-image:latest
#We will get the return json containing:
#...
# "architecture": "arm64"
#...
# If you're in amd64 (C5 instance) the return will be:
# "architecture": "amd64"

Bonus

If you read this article to the end, here is our sample Github repos build and push a multi-architecture image to Docker Hub:

And, you can test that docker image

docker manifest inspect --verbose namiops/multiarch-netcat:latest

Cheers

Thank you for following this article.

A programmer, system architect and devops engineer