【问题标题】:GitLab CI/CD: building multiarch Docker imagesGitLab CI/CD:构建多架构 Docker 镜像
【发布时间】:2020-08-09 07:17:13
【问题描述】:

我想要一种在 GitLab 运行器中构建多架构 Docker 映像的简单方法。简单来说,我的意思是我只需要在我的项目中添加一个 .gitlab-ci.yml 就可以了。

这是我写的 .gitlab-ci.yml。它使用 buildx 构建多架构映像,然后将其推送到 GitLab 注册表:

image: cl00e9ment/buildx

services:
- name: docker:dind

variables:
  PLATFORMS: linux/amd64,linux/arm64
  TAG: latest

before_script:
  - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"

build:
  stage: build
  script:
  - docker buildx build --platform "$PLATFORMS" -t "${CI_REGISTRY_IMAGE}:${TAG}" . --push

问题是linux/arm64平台不可用。

这是我构建 cl00e9ment/buildx 映像的方法(强烈灵感来自 snadn/docker-buildx):

这里是 Dockerfile:

FROM docker:latest

ENV DOCKER_CLI_EXPERIMENTAL=enabled
ENV DOCKER_HOST=tcp://docker:2375/

RUN mkdir -p ~/.docker/cli-plugins \
  && wget -qO- https://api.github.com/repos/docker/buildx/releases/latest | grep "browser_download_url.*linux-amd64" | cut -d : -f 2,3 | tr -d '"' | xargs wget -O ~/.docker/cli-plugins/docker-buildx \
  && chmod a+x ~/.docker/cli-plugins/docker-buildx
RUN docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
RUN docker context create buildx \
    && docker buildx create buildx --name mybuilder \
    && docker buildx use mybuilder
RUN docker buildx inspect --bootstrap

...在此处添加用于构建和推送 cl00e9ment/buildx 映像的 .gitlab-ci.yml 文件:

image: docker:latest

services:
  - name: docker:dind

before_script:
  - docker login -u cl00e9ment -p "$DOCKER_HUB_TOKEN"

build:
  stage: build
  script:
  - docker build --add-host docker:`grep docker /etc/hosts | awk 'NR==1{print $1}'` --network host -t cl00e9ment/buildx .
  - docker run --add-host docker:`grep docker /etc/hosts | awk 'NR==1{print $1}'` --network host cl00e9ment/buildx docker buildx inspect --bootstrap
  - docker push cl00e9ment/buildx

test:
  stage: test
  script:
  - docker run --add-host docker:`grep docker /etc/hosts | awk 'NR==1{print $1}'` --network host cl00e9ment/buildx docker buildx inspect --bootstrap

那么发生了什么?

  • 在构建结束时,在 Dockerfile 中,我运行 docker buildx inspect --bootstrap 以列出可用平台。它给了linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6。所以一切都很好。
  • 之后,我再次运行它(就在构建之后和推送之前),它仍然提供linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6。
  • 但是,在测试阶段,当在干净的环境中从 Docker Hub 新下载映像时,它会给出linux/amd64, linux/386。

为什么?

【问题讨论】:

    标签: docker build gitlab gitlab-ci multiarch


    【解决方案1】:

    不幸的是,有很多关于在 GitLab CI 上构建多架构映像的过时和不正确的信息。似乎经常更改,因为它仍然是一个实验性功能。但截至本文发布之时,这就是我如何让我的多架构构建在 GitLab 公共运行器(armv6、armv6、arm64、amd64)上工作:

    首先,必须构建并推送包含 buildx 二进制文件的 Docker 映像。这是我正在使用的 Dockerfile:

    FROM docker:latest
    ARG BUILDX_VER=0.4.2
    RUN mkdir -p /root/.docker/cli-plugins && \
        wget -qO ~/.docker/cli-plugins/docker-buildx \
        https://github.com/docker/buildx/releases/download/v${BUILDX_VER}/buildx-v${BUILDX_VER}.linux-amd64 && \
        chmod +x /root/.docker/cli-plugins/docker-buildx
    

    尽管运行了初始化代码,但当前 GitLab 运行器映像未正确初始化 binfmt 处理程序:https://gitlab.com/gitlab-org/gitlab-runner/-/blob/523854c8/.gitlab/ci/_common.gitlab-ci.yml#L91

    所以我们必须在我们的管道中这样做。我们引用 GitLab Runner 代码的 MR 1861 中的 cmets,并在我们的 .gitlab-ci.yml 中添加以下魔法酱:

    before_script:
      - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes 
    

    然后我们可以使用docker login、docker buildx build --use、docker buildx build --push ... 等运行管道脚本的其余部分。

    现在运行器已准备好为多种架构构建。 我的最终.gitlab-ci.yml可以在这里看到:https://github.com/oofnikj/nuttssh/blob/multiarch/.gitlab-ci.yml

    【讨论】:

    • 谢谢,它有效,您的解决方案比 gdunstone 的解决方案更干净。然而,通过浏览 buildx 镜像,我发现了一个非常流行的镜像(jonoh/docker-buildx-qemu),它提供了一个更好的解决方案 IMO(它不需要你添加的“魔法酱”)。你怎么看:gitlab.com/Cl00e9ment/ci-cd-tests?
    • 这种方法将binfmt 步骤与在一个容器中安装buildx 插件相结合,并且完全有效。 “魔法酱”仍然在这里发生,只是在其他地方发生。
    • 在 gitlab CI 中,我没有使用docker run,而是使用:``` services: - name: multiarch/qemu-user-static: command: ["--reset", "--persistent", "yes"] ``` 在我的 gitlab 运行器上,这似乎缓存得更好。抱歉格式化;我无法像在 Markdown 中期望的那样预格式化。
    【解决方案2】:

    好的,我认为我知道这里发生了什么:您需要在某处调用update-binfmts --enable 以启用 binfmt_misc 为 . 提供的额外格式。

    我能够使用这个 repo 及其 docker 图像在 gitlab-ci 上(经过大量搜索)获得与 buildx 一起使用的多架构图像:https://gitlab.com/ericvh/docker-buildx-qemu

    但是,该 repo 对自己的 docker 映像存储库具有自依赖性来构建自身的多架构版本,并且它依赖于 gitlab-ci 模板 repo 的 ci。我对这个依赖网络是如何开始的并不是非常有信心,而且那个 repo 的所有者比我熟练得多,但是为了我的用途,我已经分叉了 repo,我现在正试图将它的 CI 修改为较少依赖外部资源。

    编辑:对于未来的人来说,这是 Dockerfile:

    FROM debian
    # Install Docker and qemu
    # TODO Use docker stable once it properly supports buildx
    RUN apt-get update && apt-get install -y \
            apt-transport-https \
            ca-certificates \
            curl \
            gnupg2 \
            software-properties-common && \
        curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - && \
        add-apt-repository "deb https://download.docker.com/linux/debian $(lsb_release -cs) stable" && \
        apt-get update && apt-get install -y \
            docker-ce-cli \
            binfmt-support \
            qemu-user-static
    
    # Install buildx plugin
    RUN mkdir -p ~/.docker/cli-plugins && \
        ARCH=`dpkg --print-architecture` && echo Running on $ARCH && curl -s https://api.github.com/repos/docker/buildx/releases/latest | \
            grep "browser_download_url.*linux-$ARCH" | cut -d : -f 2,3 | tr -d \" | \
        xargs curl -L -o ~/.docker/cli-plugins/docker-buildx && \
        chmod a+x ~/.docker/cli-plugins/docker-buildx
    
    # Write version file
    RUN printf "$(docker --version | perl -pe 's/^.*\s(\d+\.\d+\.\d+.*),.*$/$1/')_$(docker buildx version | perl -pe 's/^.*v?(\d+\.\d+\.\d+).*$/$1/')" > /version && \
        cat /version
    

    还有一个精简版的 .gitlab-ci.yml

    build:
      image: docker:dind
      stage: build
      services:
        - name: docker:dind
          entrypoint: ["env", "-u", "DOCKER_HOST"]
          command: ["dockerd-entrypoint.sh"]
      variables:
        DOCKER_HOST: tcp://docker:2375/
        DOCKER_DRIVER: overlay2
        # See https://github.com/docker-library/docker/pull/166
        DOCKER_TLS_CERTDIR: ""
      before_script:
        - |
          if [[ -z "$CI_COMMIT_TAG" ]]; then
            export CI_APPLICATION_REPOSITORY=${CI_APPLICATION_REPOSITORY:-$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG}
            export CI_APPLICATION_TAG=${CI_APPLICATION_TAG:-$CI_COMMIT_SHA}
          else
            export CI_APPLICATION_REPOSITORY=${CI_APPLICATION_REPOSITORY:-$CI_REGISTRY_IMAGE}
            export CI_APPLICATION_TAG=${CI_APPLICATION_TAG:-$CI_COMMIT_TAG}
          fi
        - echo "$CI_REGISTRY_PASSWORD" | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
      script:
        - docker build -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" -t "$CI_APPLICATION_REPOSITORY:latest" .
        - docker push "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"  
        - docker push "$CI_APPLICATION_REPOSITORY:latest"
    

    编辑:

    另外,我发现这个使用上面构建的镜像的gitlabci配置可以使用构建缓存:

    stages:
      - build
    
    variables:
      CI_BUILD_ARCHS: "linux/amd64,linux/arm/v6,linux/arm/v7"
      CI_BUILD_IMAGE: "registry.gitlab.com/gdunstone/docker-buildx-qemu"
    
    build_master:
      image: $CI_BUILD_IMAGE
      stage: build
      services:
        - name: docker:dind
          entrypoint: ["env", "-u", "DOCKER_HOST"]
          command: ["dockerd-entrypoint.sh"]
      variables:
        DOCKER_HOST: tcp://docker:2375/
        DOCKER_DRIVER: overlay2
        # See https://github.com/docker-library/docker/pull/166
        DOCKER_TLS_CERTDIR: ""
      retry: 2
      before_script:
        - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
      # Use docker-container driver to allow useful features (push/multi-platform)
        - update-binfmts --enable # Important: Ensures execution of other binary formats is enabled in the kernel
        - docker buildx create --driver docker-container --use
        - docker buildx inspect --bootstrap
      script:
        - >
            docker buildx build --platform $CI_BUILD_ARCHS 
            --cache-from=type=registry,ref=$CI_REGISTRY_IMAGE/cache:latest
            --cache-to=type=registry,ref=$CI_REGISTRY_IMAGE/cache:latest
            --progress plain 
            --pull --push 
            --build-arg CI_PROJECT_PATH=$CI_PROJECT_PATH
            -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA" 
            -t "$CI_REGISTRY_IMAGE:latest" .
      only:
        - master
    

    【讨论】:

    • Docker 有一篇博文涵盖了 GitLab CI/CD 上的多架构构建 docker.com/blog/multi-arch-build-what-about-gitlab-ci
    • 我尝试了这篇博文中的内容,但没有奏效。我收到了这个错误:failed to solve: rpc error: code = Unknown desc = failed to load LLB: runtime execution on platform linux/arm64 not supported。我保留了 gdunstone 的解决方案。
    • @Cl00e9ment 你是对的,博文是错误的。作者不再回答。这种方式很完美!谢谢。
    • @gdunstone 万岁 - 也适用于 linux/arm64
    猜你喜欢
    • 2021-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-23
    • 2018-02-13
    • 1970-01-01
    • 1970-01-01
    • 2019-08-03
    相关资源
    最近更新 更多