Optimizing Multi-Architecture Docker Builds with GitLab CI and Buildx

Farid Vosoughi
4 min readOct 9, 2024

--

In today’s diverse computing landscape, supporting multiple CPU architectures has become increasingly important. This is especially true for Docker images, which might need to run on a variety of systems, from cloud servers to edge devices. In this article, we’ll explore an efficient method to build Docker images for both AMD64 and ARM64 architectures using GitLab CI and Docker Buildx, resulting in a final image that supports both architectures.

The Challenge

Building Docker images for multiple architectures often involves complex setups or time-consuming sequential builds. We needed a solution that could build for different architectures efficiently and in parallel.

The Solution

We’ve developed a GitLab CI configuration that addresses these challenges by:

  1. Using Docker Buildx to build for both AMD64 and ARM64 architectures
  2. Running builds in parallel to reduce overall build time
  3. Utilizing architecture-specific runners for native performance
  4. Merging the results into a multi-architecture image

Let’s break down the key components of this solution.

The GitLab CI Configuration

Here’s our optimized GitLab CI configuration:

variables:
IIMAGE_NAME: $CI_REGISTRY_IMAGE/$CI_PROJECT_NAME
APP_NAME: $CI_PROJECT_NAME
CI_REGISTRY_IMAGE: $CI_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME
TAG: $CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA
BUILDX_VERSION: "v0.10.4"
DOCKER_BUILDKIT: "1"
stages:
- build
- merge
workflow:
rules:
- if: $CI_COMMIT_BRANCH == "main"
- when: never
.build_template: &build_template
image: docker:27-dind
variables:
DOCKER_TLS_CERTDIR: ""
services:
- name: docker:27-dind
command: ["--tls=false"]
before_script:
- mkdir -p $HOME/.docker
- echo ${DOCKER_AUTH_CONFIG} > $HOME/.docker/config.json
- echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USER" --password-stdin
- wget -O /usr/local/bin/docker-buildx https://github.com/docker/buildx/releases/download/${BUILDX_VERSION}/buildx-${BUILDX_VERSION}.linux-amd64
- chmod +x /usr/local/bin/docker-buildx
- docker buildx create --use --name mybuilder
- docker buildx inspect --bootstrap mybuilder
build_amd64:
<<: *build_template
stage: build
tags:
- amd64
script:
- >
docker buildx build \
--platform linux/amd64 \
--cache-from type=registry,ref=$CI_REGISTRY_IMAGE:cache-amd64 \
--cache-to type=registry,ref=$CI_REGISTRY_IMAGE:cache-amd64,mode=max \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--tag $CI_REGISTRY_IMAGE:$TAG-amd64 \
--push \
.
build_arm64:
<<: *build_template
stage: build
tags:
- arm64
script:
- >
docker buildx build \
--platform linux/arm64 \
--cache-from type=registry,ref=$CI_REGISTRY_IMAGE:cache-arm64 \
--cache-to type=registry,ref=$CI_REGISTRY_IMAGE:cache-arm64,mode=max \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--tag $CI_REGISTRY_IMAGE:$TAG-arm64 \
--push \
.
merge_manifests:
<<: *build_template
stage: merge
needs: ["build_amd64", "build_arm64"]
script:
- docker buildx imagetools create -t $CI_REGISTRY_IMAGE:$TAG -t $CI_REGISTRY_IMAGE:latest $CI_REGISTRY_IMAGE:$TAG-amd64 $CI_REGISTRY_IMAGE:$TAG-arm64
only:
- main

How It Works

  1. Parallel Builds with Buildx: We define two build jobs, build_amd64 and build_arm64, which run in parallel. Each job uses Docker Buildx to create architecture-specific images.
  2. Architecture-Specific Runners: Each build job is tagged to run on the appropriate architecture runner (amd64 or arm64).
  3. Caching: We use Docker’s build cache with registry storage to speed up subsequent builds.
  4. Manifest Merging: After both builds are complete, we use Docker Buildx to create a multi-architecture image.

Our Two-Runner Setup

In our implementation, we use two dedicated runners:

  1. An AMD64 runner hosted on an x86–64 server
  2. An ARM64 runner hosted on an ARM-based server

This setup allows us to build natively on both architectures while leveraging the power of Buildx.

Runner Configuration

  • AMD64 Runner:
  • Tagged with amd64
  • Used for building AMD64 images and merging manifests
  • Hosted on a standard x86–64 server (e.g., Intel or AMD based)
  • ARM64 Runner:
  • Tagged with arm64
  • Used for building ARM64 images
  • Hosted on an ARM-based server (e.g., AWS Graviton, Raspberry Pi 4, or other ARM64 hardware)

Advantages of This Method

  1. Native Performance with Cross-Compilation Capability: While we use native runners, Buildx allows for cross-compilation if needed, providing flexibility.
  2. Parallel Processing: Building for both architectures simultaneously significantly reduces the total build time.
  3. Efficient Caching: Architecture-specific caching improves build times for subsequent runs.
  4. Simplified Configuration: Using Buildx simplifies the configuration for multi-architecture builds.
  5. Consistent Build Environment: Buildx ensures a consistent build environment across different architectures.
  6. Easy Scaling: This setup allows for easy addition of more architectures or runners as needed.
  7. Optimized Resource Usage: Each runner is optimized for its specific architecture, ensuring efficient use of computational resources.
  8. Flexible Image Creation: Buildx makes it easy to create and push multi-architecture images.

Setting Up Your GitLab Runners

To implement this strategy, you’ll need:

  1. At least one runner tagged with amd64 for the AMD64 builds.
  2. An ARM64 runner tagged with arm64.
  3. Docker (version 27 or later) installed on both runners.
  4. Docker Buildx installed and configured on both runners.

Conclusion

By leveraging GitLab CI, Docker Buildx, and architecture-specific runners, we’ve created an efficient pipeline that builds Docker images for both AMD64 and ARM64 architectures. This approach not only speeds up the build process but also results in a flexible, multi-architecture image that can run on a wide range of systems.

As the diversity of computing architectures continues to grow, strategies like this will become increasingly important for maintaining efficient and flexible software deployment pipelines. Our setup provides a robust foundation that can easily adapt to future architectural needs.

--

--

Farid Vosoughi
Farid Vosoughi

Written by Farid Vosoughi

Interested in learning new technologies related to software development

Responses (1)