Skip to content
Snippets Groups Projects
Commit ebbc0ff7 authored by Gilles Filippini's avatar Gilles Filippini
Browse files

ci: enable multiplatform builds with docker buildx

* Build from the Dockerfile and copy the artifacts from the resulting
  image
* Use cross-compilation at build stage when building for a foreign architecture
* Use 'docker buildx build ...' in CI to enable multiplatform builds
  If buildx is not installed use:
    DOCKER_BUILDKIT=1 docker build ...
* Gilab runner must expose tag 'multiplatform' which means it has native
  amd64 arch + emulated arm64 support:
    https://github.com/tonistiigi/binfmt/tree/deploy/v7.0.0-28#installing-emulators
    $ docker run --privileged --rm tonistiigi/binfmt --install arm64
parent 46fdd26c
No related branches found
No related tags found
1 merge request!150Draft: ci: enable multiplatform builds with docker buildx
......@@ -6,3 +6,4 @@ docker/Dockerfile
docker-compose.yml
arm-build/
**/target/
build/
# Runner tags:
# - dind: Docker in Docker
# - multiplatform: support amd64 and arm64 architectures
# https://github.com/tonistiigi/binfmt/tree/deploy/v7.0.0-28#installing-emulators
# $ docker run --privileged --rm tonistiigi/binfmt --install arm64
stages:
- schedule
- labels
......@@ -58,95 +64,59 @@ fmt_and_clippy:
- cargo clippy -- -V
- cargo clippy --all --tests -- -D warnings
build_debug:
extends: .env
rules:
- if: $CI_COMMIT_TAG
when: never
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" || $CI_COMMIT_BRANCH == "master"'
changes:
- Cargo.lock
- when: never
.docker_build:
image: docker:20.10
script:
- mkdir -p build
# A builder name and node name must be specified so that the command doesn't fail when the builder exists already
- docker buildx create --name gitlab-runner-buildx --node gitlab-runner-buildx0 --use
- docker buildx ls
- echo docker buildx build --tag "$IMAGE_NAME:$IMAGE_TAG" -f docker/Dockerfile $DOCKER_BUILD_OPTIONS .
- docker buildx build --tag "$IMAGE_NAME:$IMAGE_TAG" -f docker/Dockerfile $DOCKER_BUILD_OPTIONS .
tags:
- dind
.docker_build_save:
extends: .docker_build
stage: build
script:
- cargo clean -p duniter
- cargo build --locked
- mkdir build
- mv target/debug/duniter build/duniter
- !reference [.docker_build, script]
- docker run -d --rm --name "$CONTAINER" --entrypoint "" --user "$(id -u)" "$IMAGE_NAME:$IMAGE_TAG" sleep infinity
- docker cp "$CONTAINER:/usr/local/bin/duniter" build/
- docker stop "$CONTAINER"
artifacts:
paths:
- build/
expire_in: 3 day
cache:
- key:
files:
- Cargo.lock
paths:
- target/debug
policy: push
build_debug_with_cache:
extends: .env
build_debug:
extends: .docker_build_save
rules:
- changes:
- Cargo.lock
when: never
- if: $CI_COMMIT_TAG
when: never
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" || $CI_COMMIT_BRANCH == "master"'
- when: never
stage: build
script:
- cargo clean -p duniter
- cargo build --locked
- mkdir build
- mv target/debug/duniter build/duniter
artifacts:
paths:
- build/
expire_in: 3 day
cache:
- key:
files:
- Cargo.lock
paths:
- target/debug
policy: pull
variables:
IMAGE_NAME: "duniter/duniter-v2s"
IMAGE_TAG: "debug-sha-$CI_COMMIT_SHORT_SHA"
DOCKER_BUILD_OPTIONS: "--load --build-arg debug=1"
CONTAINER: "duniter-v2s_$IMAGE_TAG"
build_release:
extends: .env
extends: .docker_build_save
rules:
- if: "$CI_COMMIT_TAG && $CI_COMMIT_TAG =~ /^v*/"
- when: never
stage: build
script:
- cargo build --locked --release
- mkdir build
- mv target/release/duniter build/duniter
artifacts:
paths:
- build/
expire_in: 3 day
build_release_manual:
extends: .env
rules:
- if: $CI_COMMIT_TAG
when: never
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" || $CI_COMMIT_BRANCH == "master"'
- when: manual
stage: build
allow_failure: true
script:
- cargo build --locked --release
- mkdir build
- mv target/release/duniter build/duniter
artifacts:
paths:
- build/
expire_in: 3 day
variables:
IMAGE_NAME: "duniter/duniter-v2s"
IMAGE_TAG: "sha-$CI_COMMIT_SHORT_SHA"
DOCKER_BUILD_OPTIONS: "--load --platform linux/amd64"
CONTAINER: "duniter-v2s_$IMAGE_TAG"
tests_debug:
extends: .env
test_debug:
stage: tests
extends: .docker_build
rules:
- if: $CI_COMMIT_REF_NAME =~ /^wip*$/
when: manual
......@@ -154,128 +124,106 @@ tests_debug:
when: never
- if: '$CI_MERGE_REQUEST_ID || $CI_COMMIT_BRANCH == "master"'
- when: manual
stage: tests
variables:
DUNITER_BINARY_PATH: "../build/duniter"
DUNITER_END2END_TESTS_SPAWN_NODE_TIMEOUT: "20"
script:
- cargo test --workspace --exclude duniter-end2end-tests --exclude duniter-live-tests
- cargo cucumber -i account_creation*
- cargo cucumber -i certification*
- cargo cucumber -i identity_creation*
- cargo cucumber -i monetary_mass*
- cargo cucumber -i oneshot_account*
- cargo cucumber -i transfer_all*
after_script:
- cd target/debug/deps/
- rm cucumber_tests-*.d
- mv cucumber_tests* ../../../build/duniter-cucumber
artifacts:
paths:
- build/
expire_in: 3 day
IMAGE_NAME: "duniter/duniter-v2s-test"
IMAGE_TAG: "debug-sha-$CI_COMMIT_SHORT_SHA"
DOCKER_BUILD_OPTIONS: "--target build --build-arg debug=1 --build-arg cucumber=1"
tests_release:
extends: .env
test_release:
stage: tests
extends: .docker_build
rules:
- if: "$CI_COMMIT_TAG && $CI_COMMIT_TAG =~ /^v*/"
- when: never
stage: tests
variables:
DUNITER_BINARY_PATH: "../build/duniter"
DUNITER_END2END_TESTS_SPAWN_NODE_TIMEOUT: "20"
script:
- cargo test --workspace --exclude duniter-end2end-tests --exclude duniter-live-tests
- cargo cucumber -i account_creation*
- cargo cucumber -i certification*
- cargo cucumber -i identity_creation*
- cargo cucumber -i monetary_mass*
- cargo cucumber -i oneshot_account*
- cargo cucumber -i transfer_all*
after_script:
- cd target/debug/deps/
- rm cucumber_tests-*.d
- mv cucumber_tests* ../../../build/duniter-cucumber
artifacts:
paths:
- build/
expire_in: 3 day
dependencies:
- build_release
IMAGE_NAME: "duniter/duniter-v2s-test"
IMAGE_TAG: "sha-$CI_COMMIT_SHORT_SHA"
DOCKER_BUILD_OPTIONS: "--target build --build-arg cucumber=1"
.docker-build-app-image:
deploy_docker_debug_sha:
stage: deploy
image: docker:18.06
tags:
- docker
services:
- docker:dind
before_script:
- docker info
script:
- docker pull $CI_REGISTRY_IMAGE:$IMAGE_TAG || true
- docker build --cache-from $CI_REGISTRY_IMAGE:$IMAGE_TAG --pull -t "$CI_REGISTRY_IMAGE:$IMAGE_TAG" -f $DOCKERFILE_PATH .
- docker login -u "duniterteam" -p "$DUNITERTEAM_PASSWD"
- docker tag "$CI_REGISTRY_IMAGE:$IMAGE_TAG" "duniter/duniter-v2s:$IMAGE_TAG"
- docker push "duniter/duniter-v2s:$IMAGE_TAG"
deploy_docker_test_image:
extends: .docker-build-app-image
extends: .docker_build
rules:
- if: $CI_COMMIT_REF_NAME =~ /^wip*$/
when: manual
- if: '$CI_COMMIT_TAG || $CI_COMMIT_BRANCH == "master"'
- if: $CI_COMMIT_TAG
when: never
- when: manual
allow_failure: true
- if: $CI_COMMIT_BRANCH == "master"
script:
- docker login -u "duniterteam" -p "$DUNITERTEAM_PASSWD"
- !reference [.docker_build, script]
variables:
DOCKERFILE_PATH: "docker/Dockerfile"
IMAGE_TAG: "test-image-$CI_COMMIT_SHORT_SHA"
IMAGE_NAME: "duniter/duniter-v2s"
IMAGE_TAG: "debug-sha-$CI_COMMIT_SHORT_SHA"
DOCKER_BUILD_OPTIONS: "--push --build-arg debug=1"
deploy_docker_debug_sha:
extends: .docker-build-app-image
deploy_docker_release_sha:
stage: deploy
extends: .docker_build
rules:
- if: $CI_COMMIT_TAG
when: never
- if: $CI_COMMIT_BRANCH == "master"
variables:
DOCKERFILE_PATH: "docker/Dockerfile"
IMAGE_TAG: "debug-sha-$CI_COMMIT_SHORT_SHA"
after_script:
- when: manual
script:
- docker login -u "duniterteam" -p "$DUNITERTEAM_PASSWD"
- docker tag "duniter/duniter-v2s:$IMAGE_TAG" "duniter/duniter-v2s:debug-latest"
- docker push "duniter/duniter-v2s:debug-latest"
- !reference [.docker_build, script]
variables:
IMAGE_NAME: "duniter/duniter-v2s"
IMAGE_TAG: "sha-$CI_COMMIT_SHORT_SHA"
DOCKER_BUILD_OPTIONS: "--push --platform linux/amd64"
deploy_docker_release_sha:
extends: .docker-build-app-image
deploy_docker_release_sha_multiplatform:
stage: deploy
needs: ["deploy_docker_release_sha"]
extends: .docker_build
rules:
- if: $CI_COMMIT_TAG
when: never
- when: manual
allow_failure: true
script:
- docker login -u "duniterteam" -p "$DUNITERTEAM_PASSWD"
- !reference [.docker_build, script]
variables:
DOCKERFILE_PATH: "docker/Dockerfile"
IMAGE_NAME: "duniter/duniter-v2s"
IMAGE_TAG: "sha-$CI_COMMIT_SHORT_SHA"
dependencies:
- build_release_manual
DOCKER_BUILD_OPTIONS: "--push --platform linux/amd64,linux/arm64"
tags:
- dind
- multiplatform
deploy_docker_release_tag:
extends: .docker-build-app-image
stage: deploy
extends: .docker_build
rules:
- if: "$CI_COMMIT_TAG && $CI_COMMIT_TAG =~ /^v*/"
- when: never
script:
- docker login -u "duniterteam" -p "$DUNITERTEAM_PASSWD"
- !reference [.docker_build, script]
variables:
DOCKERFILE_PATH: "docker/Dockerfile"
IMAGE_NAME: "duniter/duniter-v2s"
IMAGE_TAG: "$CI_COMMIT_TAG"
after_script:
- docker login -u "duniterteam" -p "$DUNITERTEAM_PASSWD"
- docker tag "duniter/duniter-v2s:$IMAGE_TAG" "duniter/duniter-v2s:latest"
- docker push "duniter/duniter-v2s:latest"
dependencies:
- build_release
DOCKER_BUILD_OPTIONS: "--push --platform linux/amd64 --tag $IMAGE_NAME:latest"
deploy_docker_release_tag_multiplatform:
stage: deploy
needs: ["deploy_docker_release_tag"]
extends: .docker_build
rules:
- if: "$CI_COMMIT_TAG && $CI_COMMIT_TAG =~ /^v*/"
- when: never
script:
- docker login -u "$REPO_DOCKER_USER" -p "$REPO_DOCKER_PASS"
- !reference [.docker_build, script]
variables:
IMAGE_NAME: "pinidh/duniter-v2s"
IMAGE_TAG: "$CI_COMMIT_TAG"
DOCKER_BUILD_OPTIONS: "--push --platform linux/amd64,linux/arm64 --tag $IMAGE_NAME:latest"
tags:
- dind
- multiplatform
readme_docker_release_tag:
stage: deploy_readme
needs: ["deploy_docker_release_tag"]
rules:
- if: "$CI_COMMIT_TAG && $CI_COMMIT_TAG =~ /^v*/"
- when: never
......
# Build with `docker buildx build ...`
# If buildx is not installed use `DOCKER_BUILDKIT=1 docker build ...`
# ------------------------------------------------------------------------------
# Build Stage
# ------------------------------------------------------------------------------
# Building for Debian buster because we need the binary to be compatible
# with the image paritytech/ci-linux:production (currently based on
# debian:buster-slim) used by the gitlab CI
FROM rust:1-buster as build
# When building for a foreign arch, use cross-compilation
# https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/
FROM --platform=$BUILDPLATFORM rust:1-bullseye as build
ARG BUILDPLATFORM
ARG TARGETPLATFORM
# We need the target arch triplet in both Debian and rust flavor
RUN echo "DEBIAN_ARCH_TRIPLET='$(dpkg-architecture -A${TARGETPLATFORM#linux/} -qDEB_TARGET_MULTIARCH)'" >>/root/dynenv
RUN . /root/dynenv && \
echo "RUST_ARCH_TRIPLET='$(echo "$DEBIAN_ARCH_TRIPLET" | sed -E 's/-linux-/-unknown&/')'" >>/root/dynenv
WORKDIR /root
# Copy source tree
COPY . .
RUN test -x build/duniter || \
( \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y clang cmake protobuf-compiler \
)
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y clang cmake protobuf-compiler
# build duniter
ARG threads=1
RUN test -x build/duniter || \
( \
CARGO_PROFILE_RELEASE_LTO="true" \
cargo build --release -j $threads && \
mkdir -p build && \
mv target/release/duniter build/ \
)
ARG debug=0
RUN if [ "$debug" = 0 ]; then \
echo "CARGO_PROFILE_RELEASE_LTO=true; export CARGO_PROFILE_RELEASE_LTO" >>/root/dynenv && \
echo "CARGO_OPTIONS=--release" >>/root/dynenv && \
echo "TARGET_FOLDER=release" >>/root/dynenv; \
else \
echo "TARGET_FOLDER=debug" >>/root/dynenv; \
fi
# Configure cross-build environment if need be
RUN set -x && \
if [ "$TARGETPLATFORM" != "$BUILDPLATFORM" ]; then \
. /root/dynenv && \
apt install -y gcc-$DEBIAN_ARCH_TRIPLET binutils-$DEBIAN_ARCH_TRIPLET && \
rustup target add "$RUST_ARCH_TRIPLET" && \
: https://github.com/rust-lang/cargo/issues/4133 && \
echo "RUSTFLAGS='-C linker=$DEBIAN_ARCH_TRIPLET-gcc'; export RUSTFLAGS" >>/root/dynenv; \
fi
# Create fake duniter-cucumber if is not exist
# The goal is to avoid error later, this binary is optional
RUN test -x build/duniter-cucumber || \
( \
# Build
RUN set -x && \
cat /root/dynenv && \
. /root/dynenv && \
cargo build --locked $CARGO_OPTIONS --target "$RUST_ARCH_TRIPLET" && \
mkdir -p build && \
touch build/duniter-cucumber \
)
mv target/$RUST_ARCH_TRIPLET/$TARGET_FOLDER/duniter build/
# Run tests if requested, expted when cross-building
ARG cucumber=0
RUN if [ "$cucumber" != 0 ] && [ "$TARGETPLATFORM" = "$BUILDPLATFORM" ]; then \
cargo test --workspace --exclude duniter-end2end-tests --exclude duniter-live-tests && \
cargo cucumber -i account_creation* && \
cargo cucumber -i certification* && \
cargo cucumber -i identity_creation* && \
cargo cucumber -i monetary_mass* && \
cargo cucumber -i oneshot_account* && \
cargo cucumber -i transfer_all* && \
cd target/debug/deps/ && \
rm cucumber_tests-*.d && \
mv cucumber_tests* ../../../build/duniter-cucumber; \
fi
# ------------------------------------------------------------------------------
# Final Stage
# ------------------------------------------------------------------------------
FROM debian:buster-slim
FROM debian:bullseye-slim
LABEL maintainer="Gilles Filippini <gilles.filippini@pini.fr>"
LABEL version="0.0.0"
......@@ -55,6 +87,5 @@ ENTRYPOINT ["docker-entrypoint"]
USER duniter
# Intall
COPY --from=build /root/build/duniter /usr/local/bin/duniter
COPY --from=build /root/build/duniter-cucumber /usr/local/bin/duniter-cucumber
COPY --from=build /root/build /usr/local/bin/
COPY docker/docker-entrypoint /usr/local/bin/
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment