FAQ | This is a LIVE service | Changelog

Skip to content
Snippets Groups Projects
Commit ae784e50 authored by Ryan Kowalewski's avatar Ryan Kowalewski :man_dancing:
Browse files

feat: add multi platform build support

Testing this process showed that multi-platform builds can be quite flaky or extremely
slow, depending on the complexities of each image's Dockerfile etc. With this in mind,
this commit leaves the default image type as `linux/amd64` but allows us to override
each build job as required using the `IMAGE_ARCHS` variable.

Initially, all `logan-terraform` jobs have been set to build both amd64 and arm64 image
types as this is the main requirement at this time.

Given the differences between the `docker build` and `docker buildx build` command usage,
the previous job functions have been refactored as a standalone `build.sh` file. The logic
is identical to the previous functions, however, the method is slightly different. This is
mostly due to the `docker buildx` command not having `tag` sub-command, instead, it uses
`--tag` and `--push` options on a single `docker buildx build` command to both build and
push images in one go.
parent aeefc21f
No related branches found
No related tags found
1 merge request!100Migrate to buildx for multi-arch builds
......@@ -14,14 +14,13 @@ variables:
PUSH_GITLAB: "True"
PUSH_TEST_IMAGES: "False"
ENVSUBST_DOCKERFILE: "False"
IMAGE_ARCHS: "linux/amd64"
# template (hidden job) for the 'real' build and push jobs to extend
.build_and_push:
stage: build
script:
- build
- push_local
- push_docker
- ./build.sh
variables:
ONLY_PUSH_ON_BRANCH: master
WHEN_TO_RUN: "daily"
......@@ -222,6 +221,7 @@ logan-terraform-0.13:
IMAGE_TYPE: logan-terraform
IMAGE_NAME: logan-terraform
TAGS: 0.13 0.13.7
IMAGE_ARCHS: "linux/amd64,linux/arm64"
TERRAFORM_VERSION: "0.13.7"
TERRAFORM_SHA256: "4a52886e019b4fdad2439da5ff43388bbcc6cce9784fde32c53dcd0e28ca9957"
BUILD_ARGS: "TERRAFORM_VERSION TERRAFORM_SHA256"
......@@ -233,6 +233,7 @@ logan-terraform-0.14:
IMAGE_TYPE: logan-terraform
IMAGE_NAME: logan-terraform
TAGS: 0.14 0.14.11
IMAGE_ARCHS: "linux/amd64,linux/arm64"
TERRAFORM_VERSION: "0.14.11"
TERRAFORM_SHA256: "171ef5a4691b6f86eab524feaf9a52d5221c875478bd63dd7e55fef3939f7fd4"
BUILD_ARGS: "TERRAFORM_VERSION TERRAFORM_SHA256"
......@@ -244,6 +245,7 @@ logan-terraform-1.1:
IMAGE_TYPE: logan-terraform
IMAGE_NAME: logan-terraform
TAGS: 1.1 1.1.9
IMAGE_ARCHS: "linux/amd64,linux/arm64"
TERRAFORM_VERSION: "1.1.9"
TERRAFORM_SHA256: "9d2d8a89f5cc8bc1c06cb6f34ce76ec4b99184b07eb776f8b39183b513d7798a"
BUILD_ARGS: "TERRAFORM_VERSION TERRAFORM_SHA256"
......@@ -255,6 +257,7 @@ logan-terraform-1.2:
IMAGE_TYPE: logan-terraform
IMAGE_NAME: logan-terraform
TAGS: 1.2 1.2.9
IMAGE_ARCHS: "linux/amd64,linux/arm64"
TERRAFORM_VERSION: "1.2.9"
TERRAFORM_SHA256: "0e0fc38641addac17103122e1953a9afad764a90e74daf4ff8ceeba4e362f2fb"
BUILD_ARGS: "TERRAFORM_VERSION TERRAFORM_SHA256"
......@@ -266,6 +269,7 @@ logan-terraform-1.3:
IMAGE_TYPE: logan-terraform
IMAGE_NAME: logan-terraform
TAGS: 1.3 1.3.9
IMAGE_ARCHS: "linux/amd64,linux/arm64"
TERRAFORM_VERSION: "1.3.9"
TERRAFORM_SHA256: "53048fa573effdd8f2a59b726234c6f450491fe0ded6931e9f4c6e3df6eece56"
BUILD_ARGS: "TERRAFORM_VERSION TERRAFORM_SHA256"
......@@ -277,6 +281,7 @@ logan-terraform-1.4:
IMAGE_TYPE: logan-terraform
IMAGE_NAME: logan-terraform
TAGS: latest 1.4 1.4.5
IMAGE_ARCHS: "linux/amd64,linux/arm64"
TERRAFORM_VERSION: "1.4.5"
TERRAFORM_SHA256: "ce10e941cd11554b15a189cd00191c05abc20dff865599d361bdb863c5f406a9"
BUILD_ARGS: "TERRAFORM_VERSION TERRAFORM_SHA256"
......@@ -326,81 +331,11 @@ pre-commit-3.3:
ENVSUBST_DOCKERFILE: "True"
WHEN_TO_RUN: "weekly"
# build and push functions used by jobs
.functions: &functions |
function build() {
if [ "${ENVSUBST_DOCKERFILE}" == "True" ]; then
apk update
apk add gettext # for envsubst command
echo "Building ${IMAGE_TYPE} with envsubst of Dockerfile"
envsubst < "./${IMAGE_TYPE}/Dockerfile" | \
docker build -t "${IMAGE_TYPE}" -f - "${IMAGE_TYPE}"
elif [ ! -z "${BUILD_ARGS}" ]; then
for a in ${BUILD_ARGS}
do
ARG_LIST="${ARG_LIST}--build-arg $a "
done
echo "Building ${IMAGE_TYPE} with '${ARG_LIST}'"
docker build -t "${IMAGE_TYPE}" ${ARG_LIST} "${IMAGE_TYPE}"
else
echo "Building ${IMAGE_TYPE}"
docker build -t "${IMAGE_TYPE}" "${IMAGE_TYPE}"
fi
}
function push_local() {
if [ "${PUSH_GITLAB}" == "True" ]; then
IMAGE_FOLDER=${CI_REGISTRY_IMAGE}
if [ "${CI_COMMIT_REF_NAME}" != "${ONLY_PUSH_ON_BRANCH}" ]; then
# If the PUSH_TEST_IMAGES variable is set, push images to a test sub-folder
# in the image registry.
if [ ! -z "${PUSH_TEST_IMAGES}" ]; then
echo "Branch '${CI_COMMIT_REF_NAME}' is not '${ONLY_PUSH_ON_BRANCH}' so pushing to 'test'."
IMAGE_FOLDER=${IMAGE_FOLDER}/test
else
echo "Not pushing: Branch '${CI_COMMIT_REF_NAME}' is not '${ONLY_PUSH_ON_BRANCH}'"
IMAGE_FOLDER=
fi
fi
if [ ! -z "${IMAGE_FOLDER}" ]; then
echo "Registry login with ${CI_REGISTRY_USER}"
docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
for tag in ${TAGS}; do
IMAGE_TAG=${IMAGE_FOLDER}/${IMAGE_NAME}:${tag}
echo "Tagging image ${IMAGE_TAG}"
docker tag "${IMAGE_TYPE}" "${IMAGE_TAG}"
docker push "${IMAGE_TAG}"
done
fi
else
echo "Not pushing: GitLab pushes are disabled for this build"
fi
}
function push_docker() {
if [ "${PUSH_DOCKER}" == "True" ]; then
if [ "${CI_COMMIT_REF_NAME}" == "${ONLY_PUSH_ON_BRANCH}" ]; then
if [ ! -z "${DOCKER_USER}" ] && [ ! -z "${DOCKER_PASS}" ]; then
echo "Docker Hub login"
docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
for tag in ${TAGS}; do
IMAGE_TAG=${DOCKER_ORG}/${IMAGE_NAME}:${tag}
echo "Tagging image ${IMAGE_TAG}"
docker tag "${IMAGE_TYPE}" "${IMAGE_TAG}"
docker push "${IMAGE_TAG}"
done
else
echo "Not pushing: no DOCKER_USER and DOCKER_PASS variables"
fi
else
echo "Not pushing: branch '${CI_COMMIT_REF_NAME}' is not '${ONLY_PUSH_ON_BRANCH}'."
fi
else
echo "Not pushing: Docker hub pushes are disabled for this build"
fi
}
# load functions before jobs start
# Install dependencies and load functions before jobs start
before_script:
- *functions
- apk update
- apk add bash gettext # for envsubst command
# The official docs state that `docker buildx create --use` should be enough on its own. However, there appears to be
# a bug which, even though this issue is closed https://github.com/docker/buildx/issues/584#issuecomment-830041059,
# still requires this `docker run --rm` workaround.
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes || true; docker buildx create --use
build.sh 0 → 100755
#! /bin/bash
set -ex
# See https://gitlab.com/gitlab-org/gitlab/-/issues/389577 for info on why "--provenance false" is required.
docker_args=(buildx build --platform "$IMAGE_ARCHS" --provenance false)
if [[ -n "$BUILD_ARGS" ]]; then
for a in $BUILD_ARGS; do
docker_args+=(--build-arg "$a")
done
fi
if [[ "$PUSH_GITLAB" == "True" ]]; then
image_folder=$CI_REGISTRY_IMAGE
if [[ "$CI_COMMIT_REF_NAME" != "$ONLY_PUSH_ON_BRANCH" ]]; then
if [[ "$PUSH_TEST_IMAGES" == "True" ]]; then
summary+="Branch '$CI_COMMIT_REF_NAME' is not '$ONLY_PUSH_ON_BRANCH' so pushing to 'test'.\n"
image_folder="$image_folder/test"
else
summary+="Not pushing to GitLab: Branch '$CI_COMMIT_REF_NAME' is not '$ONLY_PUSH_ON_BRANCH'.\n"
unset image_folder
fi
fi
if [[ -n "$image_folder" ]]; then
echo "Registry login with $CI_REGISTRY_USER"
docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
docker_args+=(--push)
for tag in $TAGS; do
image_tag="$image_folder/$IMAGE_NAME:$tag"
summary+="Pushed tag: $image_tag\n"
docker_args+=(--tag "$image_tag")
done
fi
else
summary+="Not pushing to GitLab: GitLab pushes are disabled for this build.\n"
fi
if [[ "$PUSH_DOCKER" == "True" ]]; then
if [[ "$CI_COMMIT_REF_NAME" == "$ONLY_PUSH_ON_BRANCH" ]]; then
if [[ -n "$DOCKER_USER" ]] && [[ -n "$DOCKER_PASS" ]]; then
echo "Docker Hub login"
docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
if [[ ! "${docker_args[*]}" =~ "--push" ]]; then
docker_args+=(--push)
fi
for tag in $TAGS; do
image_tag="$DOCKER_ORG/$IMAGE_NAME:$tag"
summary+="Pushed tag: $image_tag\n"
docker_args+=(--tag "$image_tag")
done
else
summary+="Not pushing to Docker Hub: no DOCKER_USER and DOCKER_PASS variables.\n"
fi
else
summary+="Not pushing to Docker Hub: branch '$CI_COMMIT_REF_NAME' is not '$ONLY_PUSH_ON_BRANCH'.\n"
fi
else
summary+="Not pushing to Docker Hub: Docker Hub pushes are disabled for this build.\n"
fi
if [[ ! "${docker_args[*]}" =~ "--tag" ]]; then
docker_args+=(--tag "$IMAGE_TYPE")
local_build_only=1
fi
if [ "$ENVSUBST_DOCKERFILE" == "True" ]; then
docker_args+=(-f - "$IMAGE_TYPE")
echo "Building $IMAGE_TYPE with envsubst of Dockerfile"
envsubst < "./$IMAGE_TYPE/Dockerfile" | docker "${docker_args[@]}"
else
docker_args+=("$IMAGE_TYPE")
docker "${docker_args[@]}"
fi
# This extracts the values for any --tag arguments from the $docker_args array. They will be used in the target platform
# check below.
registry_tags=()
if [[ -z $local_build_only ]]; then
for i in "${!docker_args[@]}"; do
if [[ "${docker_args[$i]}" == "--tag" ]]; then
registry_tags+=("${docker_args[$i+1]}")
fi
done
fi
# This is a check to ensure that an image is built correctly for each of the target platforms. We do this by comparing
# the output of 'uname --machine' to each of the platforms specified in $IMAGE_ARCHS.
IFS=',' read -ra image_archs_array <<< "$IMAGE_ARCHS"
for registry_tag in "${registry_tags[@]}"; do
for image_arch in "${image_archs_array[@]}"; do
uname_output=$(docker run --rm --platform "$image_arch" \
--entrypoint "/bin/sh" "$registry_tag" -c "uname --machine")
case $uname_output in \
x86_64) uname_arch="amd64" ;;
arm64) uname_arch="arm64" ;;
aarch64) uname_arch="arm64" ;;
*) echo "Error: unsupported arch, exit..."; exit 1 ;;
esac
if ! echo "$image_arch" | grep -q "$uname_arch"; then
echo "Error: Image arch should be $image_arch but 'uname --machine' returned $uname_output ($uname_arch)."; exit 1
fi
done
done
# ANSI colour escape codes.
purple='\033[1;35m'
nc='\033[0m' # No Color
echo -e "$purple$summary$nc"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment