Help us learn about your current experience with the documentation. Take the survey.

使用 BuildKit 构建 Docker 镜像

  • 层级:Free, Premium, Ultimate
  • 提供方式:GitLab.com, GitLab Self-Managed, GitLab Dedicated

BuildKit 是 Docker 使用的构建引擎, 提供多平台构建和构建缓存功能。

BuildKit 方法

BuildKit 提供以下构建 Docker 镜像的方法:

方法 安全要求 命令 适用场景
BuildKit rootless 无需特权容器 buildctl-daemonless.sh 最大安全性或 Kaniko 的替代方案
Docker Buildx 需要 docker:dind docker buildx 熟悉的 Docker 工作流
Native BuildKit 需要 docker:dind buildctl 高级 BuildKit 控制

前置条件

  • 带 Docker executor 的 GitLab Runner
  • 使用 Docker Buildx 需要 Docker 19.03 或更高版本
  • 包含 Dockerfile 的项目

BuildKit rootless

独立模式的 BuildKit 提供无需 Docker 守护进程依赖的 rootless 镜像构建。 此方法完全消除了特权容器,并提供了 Kaniko 构建的直接替代方案。

与其他方法的主要区别:

  • 使用 moby/buildkit:rootless 镜像
  • 包含 BUILDKITD_FLAGS: --oci-worker-no-process-sandbox 用于 rootless 操作
  • 使用 buildctl-daemonless.sh 自动管理 BuildKit 守护进程
  • 无需 Docker 守护进程或特权容器依赖
  • 需要手动配置注册表认证

向容器注册表认证

GitLab CI/CD 通过预定义变量为 GitLab 容器注册表提供自动认证。 对于 BuildKit rootless,您必须手动创建 Docker 配置文件。

向 GitLab 容器注册表认证

GitLab 自动提供这些预定义变量:

  • CI_REGISTRY: 注册表 URL
  • CI_REGISTRY_USER: 注册表用户名
  • CI_REGISTRY_PASSWORD: 注册表密码

要配置 rootless 构建的认证,在您的作业中添加 before_script 配置。 例如:

before_script:
  - mkdir -p ~/.docker
  - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json

向多个注册表认证

要向额外的容器注册表认证,在您的 before_script 部分组合认证条目。 例如:

before_script:
  - mkdir -p ~/.docker
  - |
    echo "{
      \"auths\": {
        \"${CI_REGISTRY}\": {
          \"auth\": \"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"
        },
        \"docker.io\": {
          \"auth\": \"$(printf "%s:%s" "${DOCKER_HUB_USER}" "${DOCKER_HUB_PASSWORD}" | base64 | tr -d '\n')\"
        }
      }
    }" > ~/.docker/config.json

向依赖代理认证

要通过 GitLab 依赖代理拉取镜像,在您的 before_script 部分配置认证。 例如:

before_script:
  - mkdir -p ~/.docker
  - |
    echo "{
      \"auths\": {
        \"${CI_REGISTRY}\": {
          \"auth\": \"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"
        },
        \"$(echo -n $CI_DEPENDENCY_PROXY_SERVER | awk -F[:] '{print $1}')\": {
          \"auth\": \"$(printf "%s:%s" ${CI_DEPENDENCY_PROXY_USER} "${CI_DEPENDENCY_PROXY_PASSWORD}" | base64 | tr -d '\n')\"
        }
      }
    }" > ~/.docker/config.json

有关更多信息,请参阅 CI/CD 中的认证

在 rootless 模式下构建镜像

要构建无需 Docker 守护进程依赖的镜像,添加类似以下示例的作业:

build-rootless:
  image: moby/buildkit:rootless
  stage: build
  variables:
    BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
  before_script:
    - mkdir -p ~/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
  script:
    - |
      buildctl-daemonless.sh build \
        --frontend dockerfile.v0 \
        --local context=. \
        --local dockerfile=. \
        --output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true

在 rootless 模式下构建多平台镜像

要在 rootless 模式下为多个架构构建镜像,配置您的作业以指定目标平台。 例如:

build-multiarch-rootless:
  image: moby/buildkit:rootless
  stage: build
  variables:
    BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
  before_script:
    - mkdir -p ~/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
  script:
    - |
      buildctl-daemonless.sh build \
        --frontend dockerfile.v0 \
        --local context=. \
        --local dockerfile=. \
        --opt platform=linux/amd64,linux/arm64 \
        --output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true

在 rootless 模式下使用缓存

要启用基于注册表的缓存以加快后续构建速度,在您的构建作业中配置缓存导入和导出。 例如:

build-cached-rootless:
  image: moby/buildkit:rootless
  stage: build
  variables:
    BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
    CACHE_IMAGE: $CI_REGISTRY_IMAGE:cache
  before_script:
    - mkdir -p ~/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
  script:
    - |
      buildctl-daemonless.sh build \
        --frontend dockerfile.v0 \
        --local context=. \
        --local dockerfile=. \
        --export-cache type=registry,ref=$CACHE_IMAGE \
        --import-cache type=registry,ref=$CACHE_IMAGE \
        --output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true

配置代理设置

如果您的 GitLab Runner 在 HTTP(S) 代理后运行,在您的作业中将代理设置配置为变量。 例如:

build-behind-proxy:
  image: moby/buildkit:rootless
  stage: build
  variables:
    BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
    http_proxy: <your-proxy>
    https_proxy: <your-proxy>
    no_proxy: <your-no-proxy>
  before_script:
    - mkdir -p ~/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
  script:
    - |
      buildctl-daemonless.sh build \
        --frontend dockerfile.v0 \
        --local context=. \
        --local dockerfile=. \
        --build-arg http_proxy=$http_proxy \
        --build-arg https_proxy=$https_proxy \
        --build-arg no_proxy=$no_proxy \
        --output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true

在此示例中,将 <your-proxy><your-no-proxy> 替换为您的代理配置。

添加自定义证书

要使用自定义 CA 证书推送到注册表,在构建前将证书添加到容器的证书存储中。 例如:

build-with-custom-certs:
  image: moby/buildkit:rootless
  stage: build
  variables:
    BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
  before_script:
    - |
      echo "-----BEGIN CERTIFICATE-----
      ...
      -----END CERTIFICATE-----" >> /etc/ssl/certs/ca-certificates.crt
    - mkdir -p ~/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
  script:
    - |
      buildctl-daemonless.sh build \
        --frontend dockerfile.v0 \
        --local context=. \
        --local dockerfile=. \
        --output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true

在此示例中,将证书占位符替换为您的实际证书内容。

从 Kaniko 迁移到 BuildKit

BuildKit rootless 是 Kaniko 的安全替代方案。 它提供改进的性能、更好的缓存和增强的安全功能,同时保持 rootless 操作。

更新您的配置

更新您现有的 Kaniko 配置以使用 BuildKit rootless 方法。 例如:

之前,使用 Kaniko:

build:
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - /kaniko/executor
      --context $CI_PROJECT_DIR
      --dockerfile $CI_PROJECT_DIR/Dockerfile
      --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

之后,使用 BuildKit rootless:

build:
  image: moby/buildkit:rootless
  variables:
    BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
  before_script:
    - mkdir -p ~/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
  script:
    - |
      buildctl-daemonless.sh build \
        --frontend dockerfile.v0 \
        --local context=. \
        --local dockerfile=. \
        --output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true

其他 BuildKit 方法

如果您不需要 rootless 构建,BuildKit 提供其他需要 docker:dind 服务但提供熟悉工作流或高级功能的方法。

Docker Buildx

Docker Buildx 使用 BuildKit 功能扩展 Docker 构建能力,同时保持熟悉的命令语法。 此方法需要 docker:dind 服务。

构建基础镜像

要使用 Buildx 构建 Docker 镜像,配置您的作业使用 docker:dind 服务并创建 buildx 构建器。 例如:

variables:
  DOCKER_TLS_CERTDIR: "/certs"

build-image:
  image: docker:latest
  services:
    - docker:dind
  stage: build
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker buildx create --use --driver docker-container --name builder
    - docker buildx inspect --bootstrap
  script:
    - docker buildx build --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --push .
  after_script:
    - docker buildx rm builder

构建多平台镜像

多平台构建在单个构建命令中为不同架构创建镜像。 生成的清单支持多种架构,Docker 会自动为每个部署目标选择适当的镜像。

要为多个架构构建镜像,添加 --platform 标志以指定目标架构。 例如:

variables:
  DOCKER_TLS_CERTDIR: "/certs"

build-multiplatform:
  image: docker:latest
  services:
    - docker:dind
  stage: build
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker buildx create --use --driver docker-container --name multibuilder
    - docker buildx inspect --bootstrap
  script:
    - docker buildx build
        --platform linux/amd64,linux/arm64
        --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
        --push .
  after_script:
    - docker buildx rm multibuilder

使用构建缓存

基于注册表的缓存将构建层存储在容器注册表中以便在构建间重用。

mode=max 选项将所有层导出到缓存中,为后续构建提供最大的重用潜力。

要使用构建缓存,在您的构建命令中添加缓存选项。 例如:

variables:
  DOCKER_TLS_CERTDIR: "/certs"
  CACHE_IMAGE: $CI_REGISTRY_IMAGE:cache

build-with-cache:
  image: docker:latest
  services:
    - docker:dind
  stage: build
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker buildx create --use --driver docker-container --name cached-builder
    - docker buildx inspect --bootstrap
  script:
    - docker buildx build
        --cache-from type=registry,ref=$CACHE_IMAGE
        --cache-to type=registry,ref=$CACHE_IMAGE,mode=max
        --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
        --push .
  after_script:
    - docker buildx rm cached-builder

Native BuildKit

使用原生 BuildKit buildctl 命令来更精细地控制构建过程。 此方法需要 docker:dind 服务。

要直接使用 BuildKit,配置您的作业使用 BuildKit 镜像和 docker:dind 服务。 例如:

variables:
  DOCKER_TLS_CERTDIR: "/certs"

build-with-buildkit:
  image: moby/buildkit:latest
  services:
    - docker:dind
  stage: build
  before_script:
    - mkdir -p ~/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
  script:
    - |
      buildctl build \
        --frontend dockerfile.v0 \
        --local context=. \
        --local dockerfile=. \
        --output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true

故障排除

构建因认证错误失败

如果您遇到注册表认证失败:

  • 验证 CI_REGISTRY_USERCI_REGISTRY_PASSWORD 变量是否可用。
  • 检查您是否有推送到目标注册表的权限。
  • 对于外部注册表,确保认证凭据在项目的 CI/CD 变量中正确配置。

Rootless 构建因权限错误失败

对于 rootless 模式中的权限相关问题:

  • 确保 BUILDKITD_FLAGS: --oci-worker-no-process-sandbox 已设置。
  • 验证 GitLab Runner 已分配足够的资源。
  • 检查您的 Dockerfile 中是否尝试了特权操作。

多平台构建失败

对于多平台构建问题:

  • 验证您的 Dockerfile 中的基础镜像是否支持目标架构。
  • 检查所有目标平台的架构特定依赖是否可用。
  • 考虑在您的 Dockerfile 中使用条件语句进行架构特定逻辑。