Use Docker buildx in AWS CodeBuild to build multi-architecture Container Images
4 min read
841 words
TLDR: The buildspec.yaml
can be found at the end of the post.
I’ve spent the last few days building multi-architecture containers in AWS Codebuild. There was no quick and easy guide, so I want to document my journey with this post.
Since I worked with buildx
before I wanted to use it so I can build both
images on the same host machine. I knew that GitHub actions for example already
have actions to easily integrate buildx
in one’s workflows.
I did not find a good introduction of using Docker buildx
in CodeBuild on AWS.
The only thing I found is a blog post from November 2020 [1] where AWS does
recommend building multi-architecture builds using 3 CodeBuild environments. 2
are used to build the native container image on x86
and arm64
architecture
machines respectively.
In the last step, the Docker manifest will be created to have the possibility to use the same image tag on multiple processor architectures natively. It will be connected via a single CodePipeline which will only build the manifest after both images are created successfully
Buildx to the rescue
Since this was not something I would love to build especially when multiple
different images are created this sounds like a huge configuration hassle to me.
So I thought why not check how to install buildx
in the CodeBuild process and
build it on the same machine even if it’s not (yet?) officially supported in the
CodeBuild Linux images . As you might see in the provided Dockerfile
definitions the AWS images use curl
to get the Docker binaries to install.[2][3]
The buildx
package is included when Docker is installed as a DEB or RPM
package.
Since this is not the case we will have to install it manually when starting the
CodeBuild instance via the official buildx
releases on GitHub [4]. Beware
that the docs [5] of buildx
tell you that it is not officially supported to
be installed in a production environment because of no automatic security
updates. So make sure that you keep an eye on the releases to always use the
most up to date version.
With the buildspec.yml
provided below CodeBuild should always try to get the
newest release of buildx
The binfmt
package
You will also have to run a privileged Docker container with the binfmt
package. This image will install the required QEMU binaries to emulate the
different processor architectures. Since CodeBuild only supports amd64
OR
arm64
architecture natively we will need the other to be emulated with QEMU.
So you will have to install it with --install arm64
or --install amd64
. It
depends on the CodeBuild host you decide to use.
If you want to install emulators for every supported platform you can also use
--install all
.
Quick Tip: You might encounter the problem of Docker’s pull limits when
starting your builds and pulling the binfmt
image from Docker Hub. So think
about also implementing a Docker Hub login or hosting the images in your
registry to make sure that your CodeBuild runs don’t fail because of this pull
limit. This is annoying I know.
CodeBuild buildspec.yaml
The following CodeBuild buildspec.yaml
will download and install buildx
in
the install step before logging in to Amazon Elastic Container Registry (ECR).
Afterwards in the main build step, we will build the container image for arm64
and amd64
respectively and push it into our ECR repository in the same step.
version: 0.2
phases:
install:
commands:
- export BUILDX_VERSION=$(curl --silent "https://api.github.com/repos/docker/buildx/releases/latest" |jq -r .tag_name)
- curl -JLO "https://github.com/docker/buildx/releases/download/$BUILDX_VERSION/buildx-$BUILDX_VERSION.linux-amd64"
- mkdir -p ~/.docker/cli-plugins
- mv "buildx-$BUILDX_VERSION.linux-amd64" ~/.docker/cli-plugins/docker-buildx
- chmod +x ~/.docker/cli-plugins/docker-buildx
- docker run --privileged --rm tonistiigi/binfmt --install arm64
# To install all the supported platforms:
# - docker run --privileged --rm tonistiigi/binfmt --install all
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- docker buildx create --use --name multiarch
- docker buildx build --push --platform=linux/amd64,linux/arm64 -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG .
Thank you for reading,
Niklas