Moby 引擎构建与交叉编译实战:基于 Docker Buildx Bake 的 docker-bake.hcl 全解
2026/9/5 20:18:54 网站建设 项目流程

Moby 引擎构建与交叉编译实战:基于 Docker Buildx Bake 的 docker-bake.hcl 全解

【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby

本篇基于 ctn-build.md 贡献指南展开,讲解 Moby(Docker Engine 上游项目)如何利用根目录的 Dockerfile 与 docker-bake.hcl 构建定义,通过docker buildx bake完成 dockerd 及辅助工具的二进制构建与多平台交叉编译,并逐条拆解 bake 变量、target 与 Makefile、hack/make.sh 底层脚本的协作关系,读完即可在本地复现官方 CI 同款构建链路。

构建体系总览

Moby 的构建体系围绕三个文件组织:

  • Dockerfile:根目录多阶段构建文件,声明了 Go 工具链、交叉编译助手tonistiigi/xx、以及 containerd、runc、tini、rootlesskit、crun、containerutility 等依赖组件的独立构建阶段;
  • docker-bake.hcl:Buildx Bake 定义文件,声明了所有构建变量、target 与平台矩阵,是docker buildx bake命令的“配方”;
  • Makefile:把docker buildx bake封装成make binarymake cross等便捷入口,并负责把本地环境变量透传进构建容器。

正如 ctn-build.md 所述:Dockerfile 支持使用 Docker Buildx 与 BuildKit 构建和交叉编译 docker daemon 及额外工具,而 bake 定义正是为了简化这一过程而存在的。文档同时指出,set-up-dev-env.md 中“development container”一节介绍了如何在 Linux 容器中开发和编译改动,而二进制构建与交叉编译则由 bake 命令直接完成。

核心构建命令全解

以下是 ctn-build.md 给出的全部官方命令,按用途分组:

# build binaries for the current host platform # output to ./bundles/binary-daemon by default docker buildx bake # or docker buildx bake binary # build binaries for the current host platform # output to ./bin DESTDIR=./bin docker buildx bake # build dynamically linked binaries # output to ./bundles/dynbinary-daemon by default DOCKER_STATIC=0 docker buildx bake # or docker buildx bake dynbinary # build binaries for all supported platforms docker buildx bake binary-cross # build binaries for a specific platform docker buildx bake --set *.platform=linux/arm64 # build "complete" binaries (including containerd, runc, etc.) docker buildx bake all # build "complete" binaries for all supported platforms docker buildx bake all-cross # build non-runnable image wrapping "complete" binaries # useful for use with undock and sharing via a registry docker buildx bake bin-image # build non-runnable image wrapping "complete" binaries, with custom tag docker buildx bake bin-image --set "*.tags=foo/moby-bin:latest" # build non-runnable image wrapping "complete" binaries for all supported platforms # multi-platform images must be directly pushed to a registry docker buildx bake bin-image-cross --set "*.tags=foo/moby-bin:latest" --push

逐条说明:

  1. docker buildx bake(不带 target)等价于bake binary。原因在 docker-bake.hcl 中:group "default"targets = ["binary"],bake 默认执行 default group。
  2. 输出目录由DESTDIR变量控制。docker-bake.hcl 定义了DESTDIR变量和一个bindir函数:DESTDIR != "" ? DESTDIR : "./bundles/${defaultdir}",所以默认落在./bundles/binary-daemon(静态)或./bundles/dynbinary-daemon(动态),设置DESTDIR=./bin后则落到./bin
  3. DOCKER_STATIC=0生成动态链接二进制,与bake dynbinary等价。默认DOCKER_STATIC=1,即静态构建;动态目标会输出到bundles/dynbinary-daemon
  4. binary-cross覆盖所有支持平台。平台清单定义在 docker-bake.hcl 的_platformstarget 中:linux/amd64linux/arm/v5linux/arm/v6linux/arm/v7linux/arm64linux/ppc64lelinux/s390xwindows/amd64
  5. --set *.platform=linux/arm64是 Buildx bake 的变量覆盖语法,可把任意 target 的平台收窄到单个平台做定向构建(例如只构建 Windows 版)。
  6. all/all-cross构建“完整”二进制集,除 dockerd、docker-proxy 外还打包 containerd、runc 等配套工具,对应 Dockerfile 中的all阶段。
  7. bin-image/bin-image-cross把“完整”二进制封装进一个不可运行(scratch 基底)的镜像,便于配合undock解包分发,或直接推送到 registry 共享。bin-image-cross使用type=image输出,多平台镜像必须--push直接推送(因为多平台 manifest 本地无法落盘)。

深入 docker-bake.hcl:变量与 Target 矩阵

构建变量

docker-bake.hcl 顶部声明了以下变量,均可通过环境变量注入:

变量默认值作用
DOCKER_DEBUG""非空时开启调试:hack/make.sh会加-gcflags="all=-N -l"禁用内联与优化,并打印构建命令
DOCKER_STATIC"1"1为静态链接,0为动态链接(影响 dockerd 本体与 containerd、runc 等组件的构建方式)
DOCKER_LDFLAGS""追加给go build -ldflags,可用于构建期注入值(见下文 Makefile 的 graphdriver 优先级示例)
DOCKER_BUILDTAGS""追加 Go build tag
DOCKER_GITCOMMITnull指定写入二进制的 git 提交号
VERSION""Docker 版本号,如23.0.0-dev,通常由 Git ref 自动生成
PLATFORM""平台名,如Docker Engine - Community
PRODUCT""产品名,用于 BuildKit 的ExportedProduct,在不支持某特性的版本上给出友好报错
DEFAULT_PRODUCT_LICENSE""对应version.DefaultProductLicense,可放置商业引擎的许可证摘要
PACKAGER_NAME""打包者名称,用于 Windows 清单的CompanyName
DESTDIR""覆盖输出目录(见上)
SYSTEMD/FIREWALLDfalse仅用于devtarget,决定开发容器是否安装 systemd、firewalld
GOVULNCHECK_FORMATnullgovulnchecktarget 的输出格式

所有核心变量(除DESTDIR外的前 11 个)都被_commontarget(docker-bake.hcl)以args形式统一注入 Dockerfile 构建,实现“一处声明、全局生效”。

Target 依赖关系

从源码结构看,各 target 通过inherits形成清晰的继承链:

  • binary:继承_common,构建 Dockerfile 的binary阶段,输出bindir(DOCKER_STATIC == "1" ? "binary" : "dynbinary")
  • dynbinary:继承binary,强制DOCKER_STATIC=0,输出固定为dynbinary目录;
  • binary-cross:继承binary+_platforms,即“对全平台矩阵重复 binary 构建”;
  • binary-smoketest:继承_common,构建smoketest阶段且output = ["type=cacheonly"](不落盘产物,仅验证可编译可运行);
  • all:继承_common,构建all阶段(完整工具集),输出目录规则同binary
  • all-crossall+_platforms
  • bin-image:继承alldocker-metadata-action(tags 为moby-bin:local),输出type=docker(本地镜像);
  • bin-image-cross:继承bin-image,改type=image并枚举 7 个平台(含windows/amd64);
  • dind:继承_common,构建dind阶段,tag 为docker-dind,输出本地镜像;
  • dev:继承_common,构建dev阶段(即开发容器),额外透传SYSTEMD/FIREWALLD,tag 为docker-dev
  • govulncheck:使用独立的 hack/dockerfiles/govulncheck.Dockerfile,用于 Go 漏洞检查。

此外,defaultgroup 只包含binary,这就是裸docker buildx bake的默认行为来源。

深入 Dockerfile:构建阶段如何工作

Dockerfile 是 bake 命令真正执行的构建脚本,关键设计点:

1. 交叉编译基础:tonistiigi/xx

Dockerfile 第一行即FROM --platform=$BUILDPLATFORM tonistiigi/xx阶段,把xx交叉编译工具集注入base阶段。之后各组件统一使用xx-go buildxx-apt-get installxx-verify等命令完成目标平台的 Go 编译、依赖安装和产物校验——这正是binary-cross能“一次 bake 出八平台产物”的底层机制。基础镜像由GO_VERSION=1.26.8与 Debian bookworm 的 golang 官方镜像构成(Dockerfile)。

2. 配套组件的独立阶段

每个辅助工具都是一个“src + build + OS 选择”三段式:

  • containerd:Dockerfile 从 containerd v2.3.4 源码构建containerdcontainerd-shim-runc-v2ctrDOCKER_STATIC=1时以make STATIC=1CGO_ENABLED=0编译并xx-verify --static校验静态链接。Windows 平台则回退到binary-dummy空阶段。
  • runc:Dockerfile 构建 runc v1.5.1,静态时执行make static
  • tini(docker-init):Dockerfile 以 cmake 构建tini-static,供容器--init使用。
  • rootlesskit:Dockerfile 静态时用CGO_ENABLED=0纯 Go 编译,动态时-linkmode=external;同时把 contrib/dockerd-rootless.sh 与 contrib/dockerd-rootless-setuptool.sh 拷入产物目录。
  • crun / containerutility(Windows)/ criu:分别见 Dockerfile、Dockerfile、Dockerfile(criu 阶段当前因 opensuse 仓库稳定性被注释停用)。

3.buildbinaryall阶段

  • build(Dockerfile):安装 clang/lld 等依赖后,在$BUILDPLATFORM上执行PKG_CONFIG=$(xx-go env PKG_CONFIG) ./hack/make.sh "$target",其中DOCKER_STATIC=1target=binary(静态),否则target=dynbinary;构建完成后用xx-verify校验/tmp/bundles/<target>-daemon/dockerddocker-proxy,并把产物搬到/build/ENV PREFIX=/tmp是为了规避只读工作目录导致的 make.sh 输出失败。
  • binary(Dockerfile):FROM scratch,仅拷贝 dockerd + docker-proxy;
  • all(Dockerfile):FROM scratch,额外COPY --link引入 tini、runc、containerd、rootlesskit、containerutility 各阶段的/build/产物——这正是bake all所称“complete binaries”的由来。
  • smoketest(Dockerfile):在 base 上运行file dockerd && dockerd --version && docker-proxy --version,用于 CI 上binary-smoketest的冒烟验证。
  • dind(Dockerfile):基于官方docker:dind镜像,把 cli、buildx、compose 插件与all阶段产物叠加进去,产出docker-dind开发用 DinD 镜像。

4. 从 make.sh 到 go build 的真实调用链

Dockerfile 的build阶段最终落到 hack/make.sh:

  • hack/make.sh 的bundle()函数会source hack/make/<bundle名>
  • hack/make/binary-daemon 以DOCKER_STATIC=1GO_PACKAGE='github.com/moby/moby/v2/cmd/dockerd'BINARY_NAME='dockerd'调用 hack/make/.binary,并在目标平台与宿主机一致时把 runc、containerd、docker-init 等“嵌套可执行文件”一并拷入 bundle 目录;
  • hack/make/.binary 处理了动态构建的细节:非静态时在多数平台追加-buildmode=pie(mips*/ppc64 除外);hack/make/.binary 中 Windows 下移除netgotag 以使用系统 DNS 解析器;hack/make/.binary 针对 arm/v5 交叉编译补充-Wno-atomic-alignment-latomic;hack/make/.binary 最终执行带-tags "netgo osusergo static_build nri_no_wasm ..."go build,并把DOCKER_LDFLAGS拼入-ldflags

版本号方面,hack/make.sh 会解析VERSION(支持 tag/分支/PR ref 归一化),hack/make.sh 则取DOCKER_GITCOMMITgit rev-parse HEAD,工作区不干净时自动追加-unsupported后缀——这解释了 bake 变量为何要提供VERSIONDOCKER_GITCOMMIT两个注入点。

Makefile 封装与环境变量透传

不想手写docker buildx bake时,Makefile 提供了等价入口:

binary: $(BAKE_CMD) binary # 静态 linux 二进制 dynbinary: $(BAKE_CMD) dynbinary # 动态链接 linux 二进制 cross: $(BAKE_CMD) binary-cross # 全平台交叉构建 win: $(BAKE_CMD) --set *.platform=windows/amd64 binary

(见 Makefile 与 Makefile。)

Makefile 中default: binary,且BAKE_CMD := $(BUILDX) bakeDOCKER_GITCOMMIT在 Makefile 顶部即由git rev-parse HEAD计算并export,因此make binarydocker buildx bake binary完全等价。Makefile 的DOCKER_ENVS列表把DOCKER_LDFLAGSVERSIONPLATFORMPRODUCT等变量以-e形式透传进运行容器——其中注释给出了一个典型用法(Makefile):

make DOCKER_LDFLAGS="-X github.com/moby/moby/v2/daemon/graphdriver.priority=overlay2,zfs" dynbinary

即在构建期通过-ldflags -X改写内置的 graphdriver 优先级列表。

值得注意的约束:Makefile 明确说明不能把DOCKER_BUILDTAGS加入-e列表,因为即使 shell 中未设置,-e也会遮蔽 Dockerfile 内的ENV DOCKER_BUILDTAGS,而后者对官方构建很重要。

输出位置与产物结构速查

综合 bake 定义与 Dockerfile,各命令的产物去向如下:

命令构建阶段产物/输出位置
docker buildx bake/bake binarybinary./bundles/binary-daemon/DESTDIR覆盖之)
docker buildx bake dynbinarybinary(动态)./bundles/dynbinary-daemon/
DESTDIR=./bin docker buildx bakebinary./bin/
docker buildx bake binary-cross/all-cross全平台矩阵各平台对应目录(本地type=local输出)
docker buildx bake allall./bundles/{binary,dynbinary}-daemon/,含 containerd、runc 等
docker buildx bake bin-imageall本地镜像moby-bin:local(scratch 基底,不可运行)
docker buildx bake bin-image-cross --pushall多平台 manifest 推送至 registry
make winbinary(windows/amd64)dockerd.exe 等

小结

Moby 的二进制构建遵循“Dockerfile 定义能力,docker-bake.hcl 编排场景,Buildx bake 触发执行”的三层结构:DOCKER_STATICDESTDIRPLATFORMVERSION等变量贯穿 bake 与 Dockerfile 两个文件,_commontarget 保证了变量注入的一致性;binary/allscratch 阶段与tonistiigi/xx工具链支撑了八平台的静态/动态交叉编译,而 hack/make.sh 与 hack/make/.binary 则把 Go 编译细节(build tags、PIE、ARM 标志、版本注入)封装在 bake 背后。贡献者按 docs/contributing/ctn-build.md 的命令操作,即可与 CI 使用完全相同的构建链路产出可复现的引擎二进制。

【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询