From b261afcbd2fd8d5d5e184fc0f1c4ded3a97c1660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Molinero=20Fern=C3=A1ndez?= Date: Mon, 2 Aug 2021 21:06:18 +0200 Subject: [PATCH] Build multiarch image --- .gitlab-ci.yml | 83 ++++++++++++++--- Dockerfile => Dockerfile.m4 | 10 +- Makefile | 177 ++++++++++++++++++++++++++++++++---- 3 files changed, 237 insertions(+), 33 deletions(-) rename Dockerfile => Dockerfile.m4 (85%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3da6617..6ddedf5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,7 @@ stages: - 'build:images' - 'push:images' + - 'push:manifests' variables: DOCKER_HOST: 'tcp://dockerd:2376' @@ -14,25 +15,83 @@ default: - name: 'docker.io/docker:dind' alias: 'dockerd' -build:images: +.cmds: + - &cmd_install_build_packages 'apk add --no-cache coreutils git m4 make zstd' + - &cmd_install_push_packages 'apk add --no-cache coreutils git make zstd' + - &cmd_docker_login_registry 'docker login -u "${CI_REGISTRY_USER:?}" -p "${CI_REGISTRY_PASSWORD:?}" "${CI_REGISTRY:?}"' + +.regexes: + - ®ex_version '/^v[0-9]+$/' + +build:images:native: stage: 'build:images' + except: [*regex_version] before_script: - - 'apk add --no-cache coreutils git make zstd' + - *cmd_install_build_packages script: - - 'make IMAGE_BUILD_OPTS="--pull" build-image save-image' + - 'make IMAGE_BUILD_OPTS="--pull" build-native-image save-native-image' artifacts: expire_in: '1 day' paths: - './dist/' -push:images: - stage: 'push:images' - dependencies: - - 'build:images' - only: - - '/^v[0-9]+$/' +.build:images:cross: + stage: 'build:images' + only: [*regex_version] before_script: - - 'apk add --no-cache coreutils git make zstd' - - 'docker login -u "${CI_REGISTRY_USER:?}" -p "${CI_REGISTRY_PASSWORD:?}" "${CI_REGISTRY:?}"' + - *cmd_install_build_packages script: - - 'make load-image push-image' + - 'make binfmt-register' + - 'make IMAGE_BUILD_OPTS="--pull" "build-${ARCH:?}-image" "save-${ARCH:?}-image"' + artifacts: + expire_in: '1 week' + paths: + - './dist/' + +build:images:amd64: + extends: '.build:images:cross' + variables: {ARCH: 'amd64'} + +build:images:arm64v8: + extends: '.build:images:cross' + variables: {ARCH: 'arm64v8'} + +build:images:arm32v7: + extends: '.build:images:cross' + variables: {ARCH: 'arm32v7'} + +.push:images:cross: + stage: 'push:images' + only: [*regex_version] + before_script: + - *cmd_install_push_packages + - *cmd_docker_login_registry + script: + - 'make "load-${ARCH:?}-image" "push-${ARCH:?}-image"' + +push:images:amd64: + extends: '.push:images:cross' + dependencies: ['build:images:amd64'] + variables: {ARCH: 'amd64'} + +push:images:arm64v8: + extends: '.push:images:cross' + dependencies: ['build:images:arm64v8'] + variables: {ARCH: 'arm64v8'} + +push:images:arm32v7: + extends: '.push:images:cross' + dependencies: ['build:images:arm32v7'] + variables: {ARCH: 'arm32v7'} + +push:manifests:cross: + stage: 'push:manifests' + dependencies: ['push:images:amd64', 'push:images:arm64v8', 'push:images:arm32v7'] + only: [*regex_version] + variables: + DOCKER_CLI_EXPERIMENTAL: 'enabled' + before_script: + - *cmd_install_push_packages + - *cmd_docker_login_registry + script: + - 'make push-cross-manifest' diff --git a/Dockerfile b/Dockerfile.m4 similarity index 85% rename from Dockerfile rename to Dockerfile.m4 index 1ef0f6b..2aebbfa 100644 --- a/Dockerfile +++ b/Dockerfile.m4 @@ -1,8 +1,11 @@ +m4_changequote([[, ]]) + ################################################## ## "build" stage ################################################## -FROM docker.io/ubuntu:20.04 AS build +m4_ifdef([[CROSS_ARCH]], [[FROM docker.io/CROSS_ARCH/ubuntu:20.04]], [[FROM docker.io/ubuntu:20.04]]) AS build +m4_ifdef([[CROSS_QEMU]], [[COPY --from=docker.io/hectormolinero/qemu-user-static:latest CROSS_QEMU CROSS_QEMU]]) # Install system packages RUN export DEBIAN_FRONTEND=noninteractive \ @@ -37,10 +40,11 @@ RUN printf '%s' "${REACTOS_ISO_CHECKSUM:?} /tmp/reactos.zip" | sha256sum -c RUN unzip -p /tmp/reactos.zip 'ReactOS-*.iso' > /tmp/reactos.iso ################################################## -## "qemu-reactos" stage +## "main" stage ################################################## -FROM docker.io/ubuntu:20.04 AS qemu-reactos +m4_ifdef([[CROSS_ARCH]], [[FROM docker.io/CROSS_ARCH/ubuntu:20.04]], [[FROM docker.io/ubuntu:20.04]]) AS main +m4_ifdef([[CROSS_QEMU]], [[COPY --from=docker.io/hectormolinero/qemu-user-static:latest CROSS_QEMU CROSS_QEMU]]) # Install system packages RUN export DEBIAN_FRONTEND=noninteractive \ diff --git a/Makefile b/Makefile index 71b8b79..3579646 100644 --- a/Makefile +++ b/Makefile @@ -5,10 +5,11 @@ SHELL := /bin/sh DOCKER := $(shell command -v docker 2>/dev/null) GIT := $(shell command -v git 2>/dev/null) +M4 := $(shell command -v m4 2>/dev/null) DISTDIR := ./dist VERSION_FILE = ./VERSION -DOCKERFILE := ./Dockerfile +DOCKERFILE_TEMPLATE := ./Dockerfile.m4 IMAGE_REGISTRY := docker.io IMAGE_NAMESPACE := hectormolinero @@ -22,25 +23,89 @@ endif IMAGE_BUILD_OPTS := -IMAGE_TARBALL := $(DISTDIR)/$(IMAGE_PROJECT).tzst +IMAGE_NATIVE_DOCKERFILE := $(DISTDIR)/Dockerfile +IMAGE_NATIVE_TARBALL := $(DISTDIR)/$(IMAGE_PROJECT).tzst + +IMAGE_AMD64_DOCKERFILE := $(DISTDIR)/Dockerfile.amd64 +IMAGE_AMD64_TARBALL := $(DISTDIR)/$(IMAGE_PROJECT).amd64.tzst + +IMAGE_ARM64V8_DOCKERFILE := $(DISTDIR)/Dockerfile.arm64v8 +IMAGE_ARM64V8_TARBALL := $(DISTDIR)/$(IMAGE_PROJECT).arm64v8.tzst + +IMAGE_ARM32V7_DOCKERFILE := $(DISTDIR)/Dockerfile.arm32v7 +IMAGE_ARM32V7_TARBALL := $(DISTDIR)/$(IMAGE_PROJECT).arm32v7.tzst ################################################## ## "all" target ################################################## .PHONY: all -all: save-image +all: save-native-image ################################################## ## "build-*" targets ################################################## -.PHONY: build-image -build-image: +.PHONY: build-native-image +build-native-image: $(IMAGE_NATIVE_DOCKERFILE) + +$(IMAGE_NATIVE_DOCKERFILE): $(DOCKERFILE_TEMPLATE) + mkdir -p '$(DISTDIR)' + '$(M4)' \ + --prefix-builtins \ + '$(DOCKERFILE_TEMPLATE)' | cat --squeeze-blank > '$@' '$(DOCKER)' build $(IMAGE_BUILD_OPTS) \ --tag '$(IMAGE_NAME):$(IMAGE_VERSION)' \ --tag '$(IMAGE_NAME):latest' \ - --file '$(DOCKERFILE)' ./ + --file '$@' ./ + +.PHONY: build-cross-images +build-cross-images: build-amd64-image build-arm64v8-image build-arm32v7-image + +.PHONY: build-amd64-image +build-amd64-image: $(IMAGE_AMD64_DOCKERFILE) + +$(IMAGE_AMD64_DOCKERFILE): $(DOCKERFILE_TEMPLATE) + mkdir -p '$(DISTDIR)' + '$(M4)' \ + --prefix-builtins \ + -D CROSS_ARCH=amd64 \ + -D CROSS_QEMU=/usr/bin/qemu-x86_64-static \ + '$(DOCKERFILE_TEMPLATE)' | cat --squeeze-blank > '$@' + '$(DOCKER)' build $(IMAGE_BUILD_OPTS) \ + --tag '$(IMAGE_NAME):$(IMAGE_VERSION)-amd64' \ + --tag '$(IMAGE_NAME):latest-amd64' \ + --file '$@' ./ + +.PHONY: build-arm64v8-image +build-arm64v8-image: $(IMAGE_ARM64V8_DOCKERFILE) + +$(IMAGE_ARM64V8_DOCKERFILE): $(DOCKERFILE_TEMPLATE) + mkdir -p '$(DISTDIR)' + '$(M4)' \ + --prefix-builtins \ + -D CROSS_ARCH=arm64v8 \ + -D CROSS_QEMU=/usr/bin/qemu-aarch64-static \ + '$(DOCKERFILE_TEMPLATE)' | cat --squeeze-blank > '$@' + '$(DOCKER)' build $(IMAGE_BUILD_OPTS) \ + --tag '$(IMAGE_NAME):$(IMAGE_VERSION)-arm64v8' \ + --tag '$(IMAGE_NAME):latest-arm64v8' \ + --file '$@' ./ + +.PHONY: build-arm32v7-image +build-arm32v7-image: $(IMAGE_ARM32V7_DOCKERFILE) + +$(IMAGE_ARM32V7_DOCKERFILE): $(DOCKERFILE_TEMPLATE) + mkdir -p '$(DISTDIR)' + '$(M4)' \ + --prefix-builtins \ + -D CROSS_ARCH=arm32v7 \ + -D CROSS_QEMU=/usr/bin/qemu-arm-static \ + '$(DOCKERFILE_TEMPLATE)' | cat --squeeze-blank > '$@' + '$(DOCKER)' build $(IMAGE_BUILD_OPTS) \ + --tag '$(IMAGE_NAME):$(IMAGE_VERSION)-arm32v7' \ + --tag '$(IMAGE_NAME):latest-arm32v7' \ + --file '$@' ./ ################################################## ## "save-*" targets @@ -50,13 +115,33 @@ define save_image '$(DOCKER)' save '$(1)' | zstd -T0 -19 > '$(2)' endef -.PHONY: save-image -save-image: $(IMAGE_TARBALL) +.PHONY: save-native-image +save-native-image: $(IMAGE_NATIVE_TARBALL) -$(IMAGE_TARBALL): build-image - mkdir -p '$(DISTDIR)' +$(IMAGE_NATIVE_TARBALL): $(IMAGE_NATIVE_DOCKERFILE) $(call save_image,$(IMAGE_NAME):$(IMAGE_VERSION),$@) +.PHONY: save-cross-images +save-cross-images: save-amd64-image save-arm64v8-image save-arm32v7-image + +.PHONY: save-amd64-image +save-amd64-image: $(IMAGE_AMD64_TARBALL) + +$(IMAGE_AMD64_TARBALL): $(IMAGE_AMD64_DOCKERFILE) + $(call save_image,$(IMAGE_NAME):$(IMAGE_VERSION)-amd64,$@) + +.PHONY: save-arm64v8-image +save-arm64v8-image: $(IMAGE_ARM64V8_TARBALL) + +$(IMAGE_ARM64V8_TARBALL): $(IMAGE_ARM64V8_DOCKERFILE) + $(call save_image,$(IMAGE_NAME):$(IMAGE_VERSION)-arm64v8,$@) + +.PHONY: save-arm32v7-image +save-arm32v7-image: $(IMAGE_ARM32V7_TARBALL) + +$(IMAGE_ARM32V7_TARBALL): $(IMAGE_ARM32V7_DOCKERFILE) + $(call save_image,$(IMAGE_NAME):$(IMAGE_VERSION)-arm32v7,$@) + ################################################## ## "load-*" targets ################################################## @@ -69,11 +154,29 @@ define tag_image '$(DOCKER)' tag '$(1)' '$(2)' endef -.PHONY: load-image -load-image: - $(call load_image,$(IMAGE_TARBALL)) +.PHONY: load-native-image +load-native-image: + $(call load_image,$(IMAGE_NATIVE_TARBALL)) $(call tag_image,$(IMAGE_NAME):$(IMAGE_VERSION),$(IMAGE_NAME):latest) +.PHONY: load-cross-images +load-cross-images: load-amd64-image load-arm64v8-image load-arm32v7-image + +.PHONY: load-amd64-image +load-amd64-image: + $(call load_image,$(IMAGE_AMD64_TARBALL)) + $(call tag_image,$(IMAGE_NAME):$(IMAGE_VERSION)-amd64,$(IMAGE_NAME):latest-amd64) + +.PHONY: load-arm64v8-image +load-arm64v8-image: + $(call load_image,$(IMAGE_ARM64V8_TARBALL)) + $(call tag_image,$(IMAGE_NAME):$(IMAGE_VERSION)-arm64v8,$(IMAGE_NAME):latest-arm64v8) + +.PHONY: load-arm32v7-image +load-arm32v7-image: + $(call load_image,$(IMAGE_ARM32V7_TARBALL)) + $(call tag_image,$(IMAGE_NAME):$(IMAGE_VERSION)-arm32v7,$(IMAGE_NAME):latest-arm32v7) + ################################################## ## "push-*" targets ################################################## @@ -82,10 +185,47 @@ define push_image '$(DOCKER)' push '$(1)' endef -.PHONY: push-image -push-image: - $(call push_image,$(IMAGE_NAME):$(IMAGE_VERSION)) - $(call push_image,$(IMAGE_NAME):latest) +define push_cross_manifest + '$(DOCKER)' manifest create --amend '$(1)' '$(2)-amd64' '$(2)-arm64v8' '$(2)-arm32v7' + '$(DOCKER)' manifest annotate '$(1)' '$(2)-amd64' --os linux --arch amd64 + '$(DOCKER)' manifest annotate '$(1)' '$(2)-arm64v8' --os linux --arch arm64 --variant v8 + '$(DOCKER)' manifest annotate '$(1)' '$(2)-arm32v7' --os linux --arch arm --variant v7 + '$(DOCKER)' manifest push --purge '$(1)' +endef + +.PHONY: push-native-image +push-native-image: + @printf '%s\n' 'Unimplemented' + +.PHONY: push-cross-images +push-cross-images: push-amd64-image push-arm64v8-image push-arm32v7-image + +.PHONY: push-amd64-image +push-amd64-image: + $(call push_image,$(IMAGE_NAME):$(IMAGE_VERSION)-amd64) + $(call push_image,$(IMAGE_NAME):latest-amd64) + +.PHONY: push-arm64v8-image +push-arm64v8-image: + $(call push_image,$(IMAGE_NAME):$(IMAGE_VERSION)-arm64v8) + $(call push_image,$(IMAGE_NAME):latest-arm64v8) + +.PHONY: push-arm32v7-image +push-arm32v7-image: + $(call push_image,$(IMAGE_NAME):$(IMAGE_VERSION)-arm32v7) + $(call push_image,$(IMAGE_NAME):latest-arm32v7) + +push-cross-manifest: + $(call push_cross_manifest,$(IMAGE_NAME):$(IMAGE_VERSION),$(IMAGE_NAME):$(IMAGE_VERSION)) + $(call push_cross_manifest,$(IMAGE_NAME):latest,$(IMAGE_NAME):latest) + +################################################## +## "binfmt-*" targets +################################################## + +.PHONY: binfmt-register +binfmt-register: + '$(DOCKER)' run --rm --privileged docker.io/hectormolinero/qemu-user-static:latest --reset ################################################## ## "version" target @@ -109,5 +249,6 @@ version: .PHONY: clean clean: - rm -f '$(IMAGE_TARBALL)' + rm -f '$(IMAGE_NATIVE_DOCKERFILE)' '$(IMAGE_AMD64_DOCKERFILE)' '$(IMAGE_ARM64V8_DOCKERFILE)' '$(IMAGE_ARM32V7_DOCKERFILE)' + rm -f '$(IMAGE_NATIVE_TARBALL)' '$(IMAGE_AMD64_TARBALL)' '$(IMAGE_ARM64V8_TARBALL)' '$(IMAGE_ARM32V7_TARBALL)' if [ -d '$(DISTDIR)' ] && [ -z "$$(ls -A '$(DISTDIR)')" ]; then rmdir '$(DISTDIR)'; fi