适用版本:Free、Premium、Ultimate;适用服务:GitLab.com、GitLab Self-Managed、GitLab Dedicated。
BuildKit是 Docker 使用的构建引擎,支持多平台构建与构建缓存。
构建方式
| 方式 | 安全要求 | 命令 | 适用情况 |
|---|---|---|---|
| BuildKit rootless | 不需要特权容器 | buildctl-daemonless.sh |
追求安全性或替代 Kaniko |
| Docker Buildx | 需要 docker:dind |
docker buildx |
熟悉的 Docker 工作流 |
| 原生 BuildKit | 需要 docker:dind |
buildctl |
更细致地控制 BuildKit |
先决条件
- 使用 Docker 执行器的 GitLab Runner。
- 使用 Docker Buildx 时,Docker 版本为 19.03 或更新。
- 项目包含
Dockerfile。
BuildKit rootless
独立运行的 BuildKit 可以在不依赖 Docker 守护进程的情况下以 rootless 模式构建镜像,构建任务不需要特权容器,可直接替代 Kaniko。
Runner 仍必须允许 BuildKit 创建用户命名空间和挂载点所需的系统调用。GitLab.com 托管 Runner 因为以特权模式运行,无须额外配置。自管理 Docker 执行器在非特权模式下可能出现权限错误,详见后文。无法改变 Runner 安全配置时,可使用 rootless Buildah。
此方式使用 moby/buildkit:rootless 镜像;通过 BUILDKITD_FLAGS: --oci-worker-no-process-sandbox 支持 rootless;由 buildctl-daemonless.sh 自动管理守护进程;不依赖 Docker 守护进程或特权构建容器,但须手动配置注册表认证。
容器注册表认证
GitLab CI/CD 通过预定义变量为 GitLab 容器注册表提供认证信息。rootless BuildKit 仍须手动创建 Docker 配置文件。
GitLab 容器注册表
变量 CI_REGISTRY、CI_REGISTRY_USER、CI_REGISTRY_PASSWORD 分别表示注册表 URL、用户名和密码。在任务中加入:
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:
name: moby/buildkit:rootless
entrypoint: [""]
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
必须覆盖 entrypoint: [""]。默认情况下,rootless 镜像启动长期运行的 BuildKit 守护进程;不覆盖入口时,任务会运行守护进程而非构建命令,一直挂起直到超时。
rootless 多平台构建
通过目标平台选项指定架构:
build-multiarch-rootless:
image:
name: moby/buildkit:rootless
entrypoint: [""]
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:
name: moby/buildkit:rootless
entrypoint: [""]
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
rootless 注册表镜像
注册表镜像可加快拉取速度,并缓解速率限制或网络限制。用 buildkit.toml 指定镜像端点:
build-mirror-rootless:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox --config /tmp/buildkit.toml
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
- cat <<'EOF' > /tmp/buildkit.toml
[registry."docker.io"]
mirrors = ["mirror.example.com"]
EOF
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
将 mirror.example.com 替换成自己的注册表镜像 URL。
代理设置
Runner 位于 HTTP(S) 代理后方时,在任务变量中配置代理:
build-behind-proxy:
image:
name: moby/buildkit:rootless
entrypoint: [""]
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 的注册表推送时,在守护进程启动前通过 BuildKit 配置文件设置信任证书:
build-with-custom-certs:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
before_script:
- mkdir -p "$HOME/.docker"
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > "$HOME/.docker/config.json"
- REG_HOST="${CI_REGISTRY%%/*}"
- mkdir -p "$HOME/.config/buildkit/certs/$REG_HOST"
- echo "$CA_CERT" > "$HOME/.config/buildkit/certs/$REG_HOST/ca.pem"
- |
cat > "$HOME/.config/buildkit/buildkitd.toml" << EOT
[registry."$REG_HOST"]
ca = ["$HOME/.config/buildkit/certs/$REG_HOST/ca.pem"]
EOT
- export SSL_CERT_FILE="$HOME/.config/buildkit/certs/$REG_HOST/ca.pem"
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
其中,REG_HOST="${CI_REGISTRY%%/*}" 提取注册表主机名;buildkitd.toml 为目标注册表配置可信 CA,BuildKit 自动从 $HOME/.config/buildkit/ 发现此文件;SSL_CERT_FILE 用于覆盖 BuildKit 守护进程完全初始化前发生的 TLS 连接。
新增 CA_CERT CI/CD 变量,提供包含根证书和所有中间证书的完整链。PEM 包含换行,因此变量值无法设为 masked。要隐藏值,可改用文件类型变量,并把 before_script 中的 echo "$CA_CERT" 替换为 cat "$CA_CERT"。如果目标注册表与 GitLab 实例使用相同 CA,且 Runner 已配置 tls-ca-file,可改用预定义变量 CI_SERVER_TLS_CA_FILE。
从 Kaniko 迁移
原文将 rootless BuildKit 描述为无需特权容器、提供更好性能、缓存和安全功能的 Kaniko 替代方案。
更新配置
原 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
改为 rootless BuildKit:
build:
image:
name: moby/buildkit:rootless
entrypoint: [""]
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
自定义 CA 迁移
Kaniko 任务若使用自定义 CA,迁移时必须显式配置。与 Kaniko 不同,moby/buildkit:rootless 镜像不包含系统证书存储,须在守护进程启动前创建 BuildKit 配置。
- 在名为
CA_CERT的 CI/CD 变量中保存完整证书链,包含根证书及中间证书。 - 更新任务,使其使用
buildkitd.toml和SSL_CERT_FILE。完整示例见上文自定义证书部分。
其他 BuildKit 方式
不需要 rootless 时,可以采用依赖 docker:dind 服务的方式,获得熟悉的工作流或高级控制功能。
Docker Buildx
Buildx 在熟悉的 Docker 命令基础上增加 BuildKit 功能,需要 docker:dind 服务。
基本镜像
配置服务并创建 builder:
variables:
DOCKER_TLS_CERTDIR: "/certs"
build-image:
image: docker:cli
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
多平台镜像
单次命令可构建多种架构,生成的 manifest 支持多个平台,Docker 会为部署目标自动选择合适镜像。使用 --platform 指定架构:
variables:
DOCKER_TLS_CERTDIR: "/certs"
build-multiplatform:
image: docker:cli
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:cli
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
原生 BuildKit
直接使用 buildctl 获得更多控制。此方式需要 docker:dind,任务使用 BuildKit 镜像:
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_USER 和 CI_REGISTRY_PASSWORD 可用;确认有目标注册表推送权限;使用外部注册表时,检查项目 CI/CD 变量中凭据配置是否正确。
rootless 权限错误
确认已设置 BUILDKITD_FLAGS: --oci-worker-no-process-sandbox,Runner 有足够资源,且 Dockerfile 没有尝试特权操作。Kubernetes Runner 的 AppArmor 挂载权限也可能阻止 rootless 容器,参阅 Kubernetes 执行器挂载错误。
如果出现以下错误,则 Runner 安全策略阻止了所需系统调用。
fork/exec /proc/self/exe: operation not permitted
Docker 执行器以非特权模式运行时可能出现:
could not connect to unix:///run/user/1000/buildkit/buildkitd.sock after 10 trials
[rootlesskit:parent] error: failed to start the child: fork/exec /proc/self/exe: operation not permitted
原因是 Runner 的 seccomp 配置阻止了 rootless BuildKit 所需调用。GitLab.com 托管 Runner 以特权模式运行,不受此问题影响。
自管理 Runner 应配置 Docker 执行器的 security_opt,仅允许 BuildKit 必需的调用。不要设置为 seccomp:unconfined:虽然可消除错误,却会禁用默认 seccomp 保护、降低隔离性。应使用只放行必要调用的自定义配置,或改用 rootless Buildah。
invalid local: stat path/to/image/Dockerfile: not a directory
此错误表示为 --local dockerfile= 传入了文件路径,BuildKit 需要包含 Dockerfile 的目录。使用 --local dockerfile=path/to/image,而非 --local dockerfile=path/to/image/Dockerfile。
多平台构建失败
检查基础镜像是否支持所有目标架构,检查各平台是否都有所需架构依赖,并考虑在 Dockerfile 中用条件语句处理架构专属逻辑。
来源:GitLab Inc. 与文档贡献者,官方原文。仓库许可明确将 doc/ 下正文按 CC BY-SA 4.0授权。本稿为中文翻译,保留全部 16 个代码块;翻译也按 CC BY-SA 4.0 提供,不代表 GitLab 背书,许可免责声明适用。此次只做静态文档核对,未运行构建或部署命令。











暂无评论内容