docker compose build 完整指南:镜像构建、标签策略与底层实现解析
2026/9/9 20:00:27 网站建设 项目流程

docker compose build 完整指南:镜像构建、标签策略与底层实现解析

【免费下载链接】composeDefine and run multi-container applications with Docker项目地址: https://gitcode.com/GitHub_Trending/compose/compose

本指南基于 Docker Compose 官方命令参考文档 docs/reference/compose_build.md,深入讲解docker compose build的命令语义、镜像命名与标签规则、全部可用的命令行选项,并结合本仓库的 CLI 解析源码与构建后端实现,说明该命令从"解析 Compose 文件"到"产出镜像"的完整底层链路。读完本文,你将能准确掌握如何为单个或多个服务构建镜像、按需命中缓存、携带 SSH/构建参数、输出 Bake 文件以及配合 CI 进行推送与安全检查。

命令概览与核心语义

docker compose build用于构建(或重新构建)Compose 文件中声明了build段的服务镜像,其命令形式为:

docker compose build [OPTIONS] [SERVICE...]

其核心语义可以概括为三点(均出自官方参考文档原文):

  1. 每个服务只构建一次,然后被打上标签(tag),默认标签为project-service,即"项目名-服务名"。
  2. 如果 Compose 文件中为服务显式指定了image名称,则构建产物会以该image名称打标签;image字段中出现的环境变量在打标签之前会先进行变量插值。
  3. 当你修改了某个服务的Dockerfile或它构建目录(build context)中的内容时,需要重新执行docker compose build才能让镜像包含这些变更——普通docker compose up不会因为源码文件变更而自动触发重建。

这一语义也在源码的 API 注释中被完整保留。docker compose build在 CLI 层映射到Compose接口的Build方法,见 pkg/api/api.go 中 "Build executes the equivalent to acompose build"。

默认镜像名的来源

"project-service" 并不是一句笼统的说法,而是由仓库中一个明确实现的函数决定的。在 pkg/api/api.go 中:

// Separator is used for naming components var Separator = "-" // GetImageNameOrDefault computes the default image name for a service, used to tag built images func GetImageNameOrDefault(service types.ServiceConfig, projectName string) string { imageName := service.Image if imageName == "" { imageName = projectName + Separator + service.Name } return imageName }

即:服务没有显式配置image时,镜像名 =项目名 + "-" + 服务名。例如项目名为myapp、服务名为web,构建出的镜像默认叫myapp-web。项目名默认取自 compose 文件所在目录名,也可通过-p/--project-nameCOMPOSE_PROJECT_NAME环境变量等方式显式指定(相关标志定义见 cmd/compose/compose.go)。若在兼容模式下运行(--compatibility),Separator会被替换为_,以尽量贴近 Compose v1 的命名习惯,见 cmd/compose/compose.go。

完整的命令选项参考

官方参考文档给出以下选项表,其中部分选项为其他命令继承的全局选项:

名称类型默认值说明
--build-argstringArray为服务设置构建期变量
--builderstring指定要使用的 builder(构建器)
--checkboolfalse检查构建配置
--dry-runboolfalse以 dry run(演练)模式执行命令
-m,--memorybytes0为构建容器设置内存上限。BuildKit 不支持
--no-cacheboolfalse构建镜像时不使用缓存
--printboolfalse打印等效的 bake 文件
--provenancestring附加 provenance 来源证明(attestation)
--pullboolfalse总是尝试拉取镜像的更新版本
--pushboolfalse构建后推送服务镜像
-q,--quietboolfalse抑制构建输出
--sbomstring附加 SBOM 软件物料清单证明
--sshstring构建服务镜像时使用的 SSH 认证(传default使用默认 SSH Agent)
--with-dependenciesboolfalse连带构建(可传递的)依赖服务

上表与命令行标志实现一一对应:--dry-run定义在根命令的PersistentFlags上(cmd/compose/compose.go),因此对所有 compose 子命令生效;其余选项在buildCommand中注册(cmd/compose/build.go),并集中保存在buildOptions结构体中。

需要留意的隐藏(DEPRECATED)选项

在 cmd/compose/build.go 中还注册了一批被标记隐藏的选项,它们仅保留用于兼容旧版脚本,功能已不再有意义

  • --parallel(已弃用,默认true
  • --compress(已弃用,使用 gzip 压缩构建上下文)
  • --force-rm(已弃用,总是删除中间容器)
  • --no-rm(已弃用,成功构建后不删除中间容器)

以及一个同为隐藏的--progress子选项。源码注释建议改用全局的docker compose --progress xx build,若仍以子命令形式传参,CLI 会向 stderr 打印迁移提示(cmd/compose/build.go)。因此,参考文档的选项表只列出仍然有效的公开选项,你在实际使用中不必关心这些隐藏标志。

构建流程的两条后端路径

从源码结构看,docker compose build在执行时会根据环境自动选择两条不同的底层构建路径之一。命令入口runBuild最终调用backend.Build(ctx, project, apiBuildOptions)(cmd/compose/build.go),随后进入composeService.build做路径分流(pkg/compose/build.go):

bake, err := buildWithBake(s.dockerCli) if err != nil { return nil, err } if bake { return s.doBuildBake(ctx, project, serviceToBuild, options) } return s.doBuildClassic(ctx, project, serviceToBuild, options)

buildWithBake的判定条件是(pkg/compose/build_bake.go):

  1. Docker 引擎启用了 BuildKit(即没有把DOCKER_BUILDKIT显式设置为 0);
  2. 系统安装了buildxDocker CLI 插件

满足以上两点就走BuildKit + buildx bake路径(doBuildBake),否则回退到经典构建器(classic builder,doBuildClassic)。当 buildx 缺失时,CLI 会打印一条告警:使用经典构建器将无法获得多架构构建、secrets、ssh、additional contexts 等 BuildKit 专属能力(pkg/compose/build_bake.go)。需要注意的是,buildx 插件还有最低版本要求,版本过低会直接报错(compose build requires buildx x.y.z or later,见 pkg/compose/build_bake.go)。

经典构建器路径(doBuildClassic)

在经典路径下,服务会按依赖顺序逐个调用 Docker 引擎的传统ImageBuild接口构建(pkg/compose/build_classic.go)。其特点包括:

  • 本地目录上下文会被打包成 tar 并压缩后上传给 daemon,过程中遵守.dockerignore(pkg/compose/build_classic.go);
  • 也支持 Git URL、远程 URL 上下文(pkg/compose/build_classic.go);
  • 不支持多平台(multi-arch)构建、privileged 模式、additional contexts、SSH keys、secrets 等特性,一旦 compose 文件中出现这些配置会直接报错并提示改用 BuildKit(pkg/compose/build_classic.go);
  • -m/--memory内存限制选项仅在经典路径生效(这正是选项表中标注 "Not supported by BuildKit" 的原因),构建选项会把它传入ImageBuildOptions(pkg/compose/build_classic.go)。

BuildKit + bake 路径(doBuildBake)

在现代默认路径下,compose 会先把项目里的构建配置翻译成一个bake 文件(JSON),再调用buildx bake执行(pkg/compose/build_bake.go)。翻译时每个服务的构建参数(contextdockerfileargstagscache-from/cache-toplatformssecretssshentitlements等)都会映射为 bake target,见 pkg/compose/build_bake.go。bake target 名称由服务名转换而来:其中的.会被替换为_,若发生碰撞则追加_直至唯一(pkg/compose/build_bake.go)。

bake 的输出类型根据场景自动选择(pkg/compose/build_bake.go):

  • 默认单平台且未要求推送时,输出type=docker(导入本地 Docker 引擎);
  • 显式--push且服务配置了image时,输出type=registry(直接推送仓库);
  • 服务声明了多个platforms时,输出type=image,push=<true|false>

构建哪些服务:目标选择、依赖与缓存跳过

docker compose build可以不带任何服务名,也可以携带一个或多个服务名作为参数,例如只构建项目里的两个服务:

docker compose build web api

服务选择与依赖关系

在 pkg/compose/build.go 中可以看到服务选择逻辑:

  • 不传服务名时默认选择项目内全部声明了build的服务options.Services = project.ServiceNames());
  • --with-dependencies(对应结构体字段Deps)会把服务选择策略从IgnoreDependencies切换为IncludeDependencies,于是会连带构建所选服务的依赖服务,且依赖关系是可传递的(transitively);
  • 服务内部的additional_contexts: service:xxx引用也会被解析为构建顺序上的依赖(先构建作为上下文的那个服务),对应源码是 pkg/compose/build_classic.go 中的WithServicesTransform处理,以及build()里的addBuildDependencies

因此当你的镜像依赖另一个 compose 服务(例如需要先把依赖服务构建出的镜像作为additional_context),直接执行docker compose build <svc>也会自动保证底层服务先被构建。

构建时不会盲目全量重建

还有一层自动跳过逻辑需要特别说明:在up --buildrun --build等需要"先确保镜像存在"的复合场景中,build()会先查本地是否已存在对应镜像——若镜像已存在于本地且服务的拉取策略不是build,则该服务会被跳过而不重建(pkg/compose/build.go)。当你希望无条件强制重建时,应显式执行docker compose build(必要时加--no-cache--pull)。E2E 测试也验证了这一点:例如 pkg/e2e/build_test.go 中,"再次up不再重建、up --build才触发重建"。

若最终没有发现任何需要构建的服务,composeService.Build会打印一条"No services to build"警告(pkg/compose/build.go),这通常意味着你选中的服务都没有配置build段。

构建选项在源码中的实际映射

本小节把上表选项落到源码实现上,帮助你理解每个开关最终变成了什么。

--build-arg:构建期变量

--build-arg KEY=VALUE可重复传入,收集进opts.args,经types.NewMappingWithEquals(opts.args)转为带覆盖语义的映射(cmd/compose/build.go)。之后会与 Docker 代理环境变量(HTTP_PROXY等)及 compose 文件中build.args合并解析,最终既传入 bake target 的args(pkg/compose/build_bake.go),也传入经典构建的BuildArgs(pkg/compose/build_classic.go)。注意 bake 路径会把参数中的${转义为$${,以避免传给 buildx 时被二次插值。

--no-cache--pull

两者均采用"命令行或文件配置任一为真即启用"的合并语义,见 pkg/api/api.go:

service.Build.Pull = service.Build.Pull || o.Pull service.Build.NoCache = service.Build.NoCache || o.NoCache
  • --no-cache:禁用 BuildKit 层缓存,适合排查"明明改了源码但镜像没变"的问题;
  • --pull:构建前总是尝试拉取基础镜像(Dockerfile 中FROM引用的镜像)的最新版本;
  • 同时,compose 文件里每个服务也可以分别声明build.pull/build.no_cache,两者会合并生效。

--ssh:构建时使用 SSH Agent

--ssh支持两种写法:只写--ssh default使用当前默认的 SSH Agent;或写--ssh <id>=<path>指定一个具体私钥路径。CLI 解析时会以第一个=为界拆成 id 与路径,若既没有=且 id 也不是default,则报invalid ssh key(cmd/compose/build.go)。解析结果既会传给 bake 的 ssh 列表,也会追加到 compose 文件中服务声明的build.ssh之后(pkg/compose/build_bake.go)。

另外有一个易踩的坑:只传空的--ssh也等价于--ssh default——源码中若检测到该 flag 被显式使用但值为空,会自动把值设为default(cmd/compose/build.go)。对应的 E2E 测试 pkg/e2e/build_test.go 验证了:在未设置SSH_AUTH_SOCKbuild --ssh会失败并提示invalid empty ssh agent socket,而从 CLI 指定具体私钥(--ssh fake-ssh=./path/fake_rsa)或从 compose 文件配置则能成功。

--push:构建并推送

--push在 bake 路径下把输出从本地type=docker切换为type=registry,构建完成后镜像会被推送到image指定的仓库。如果服务没有配置image(因而没有可推送的目标仓库),--push会被静默忽略,E2E 中专门有一个用例build --push ignored for unnamed images验证这一点(pkg/e2e/build_test.go)。在经典构建器路径中,--push则表现为构建完每个服务后立即调用push(pkg/compose/build_classic.go)。

-q, --quiet:抑制构建输出

--quiet会把全局display.Mode切换为 quiet,并把os.Stdout重定向到/dev/null(cmd/compose/build.go)。E2E 用例build --quiet断言其 stdout 为空(pkg/e2e/build_test.go),适合在 CI 脚本中只关心构建结果而不关心日志。

--print:打印等效 bake 文件

--print不会真正执行构建,而是把翻译好的 bake 配置(group/target结构)以缩进 JSON 形式打印到 stdout 后直接返回(pkg/compose/build_bake.go)。这是调试构建参数、排查"compose 到底给 buildx 传了什么"的最直接手段。同样,由于 bake 路径下runBuild会在 print 时挂上安静的事件处理器,输出不会被多余日志污染(cmd/compose/build.go)。

示例输出结构大致如下(具体内容随 compose 文件不同而变):

{ "group": { "default": { "targets": ["web"] } }, "target": { "web": { "context": ".", "dockerfile": "web/Dockerfile", "tags": ["myapp-web"], "platforms": ["linux/amd64"], "outputs": ["type=docker"] } } }

--check:只校验不构建

--check让 bake 以call=lint模式运行(pkg/compose/build_bake.go),即让 BuildKit 的 linter 检查 Dockerfile/构建配置,而不实际产出镜像。与--print类似,它适合接入 CI 的静态检查阶段。注意该能力只在 bake(BuildKit)路径下可用。

--dry-run:全局演练模式

--dry-run是全局标志。bake 路径下的 dry run 会模拟构建事件:为每个 target 生成dryRun-<sha1>形式的假镜像 ID,并输出==> writing image ...==> naming to <tag>等事件(pkg/compose/build_bake.go),便于在不实际构建的情况下预览会发生什么。dry run 通过compose.WithDryRun注入后端(cmd/compose/compose.go)。

--provenance/--sbom:供应链证明

Compose 可以在构建产物上附加证明(attestation)

  • --provenance:SLSA provenance(来源证明),记录镜像"由谁、用什么方式构建";
  • --sbom:SBOM(软件物料清单),列出镜像包含的软件组件。

CLI 会将其追加为 buildx 的命令行参数--sbom=...--provenance=...(pkg/compose/build_bake.go)。同时在runBuild中会无条件开启apiBuildOptions.Attestations = true(cmd/compose/build.go),保证即使只使用 compose 文件里的build.attest/build.provenance/build.sbom配置也能正常生成证明。参数值支持true/false或更细粒度配置,例如--provenance=mode=max(解析逻辑见 pkg/compose/build_bake.go)。

一个完整的实战示例

以仓库 E2E 测试数据 pkg/e2e/testdata/TestBuildTags/compose.yaml 为参照,一个带显式image和多个自定义 tag 的服务可以写成:

services: nginx: image: ${TAG_IMAGE} build: context: . tags: - docker.io/docker/${TAG_IMAGE}:1.0.0 - ${TAG_IMAGE}-other:v1.0.0

在项目目录执行构建:

# 构建全部服务(默认标签 <project>-<service>) docker compose build # 只构建 nginx,并连带其依赖 docker compose build --with-dependencies nginx # 禁用缓存强制重建,同时注入构建期变量 docker compose build --no-cache --build-arg VERSION=1.2.3 nginx # 构建并推送到 image 指定的仓库 docker compose build --push # 不真正构建,只查看将要执行的 bake 配置 docker compose build --print # 校验构建配置(仅 BuildKit/bake 路径支持) docker compose build --check # 演练模式,观察将会产生的镜像名与标签事件 docker compose --dry-run build

构建完成后,可用docker compose images查看本项目的服务镜像,或在构建时通过自定义 tag 的方式为镜像额外命名(compose 会把image名与build.tags全部合并为镜像 tag,见 pkg/compose/build_bake.go)。

常见问题速查

改了代码再up,为什么容器里还是旧镜像?普通up不会因为源码变更重建镜像。需要显式docker compose build,或使用docker compose up --build

为什么-m/--memory不生效?该选项只作用于经典构建器;在默认的 BuildKit 路径下会被忽略(选项表已明确标注 "Not supported by BuildKit")。如需限制资源,请改用 Docker/buildx 层面的资源限制。

--push明明传了却不推送?只有服务配置了显式image时才有可推送的仓库目标;未配置image的服务会被自动忽略推送。

构建很慢,如何确认缓存命中情况?先查看是否命中了自动跳过逻辑(本地已有镜像且非build拉取策略),必要时使用--no-cache强制全量构建排查,并可用--print检查 bake 中的cache-from/cache-to配置是否正确传递。

参考文档本体与对应源码入口:

  • 命令参考:docs/reference/compose_build.md
  • CLI 标志与解析:cmd/compose/build.go
  • 构建 API 与默认命名:pkg/api/api.go、pkg/api/api.go
  • 构建编排与路径选择:pkg/compose/build.go
  • BuildKit/bake 后端:pkg/compose/build_bake.go
  • 经典构建后端:pkg/compose/build_classic.go
  • E2E 验证用例:pkg/e2e/build_test.go

【免费下载链接】composeDefine and run multi-container applications with Docker项目地址: https://gitcode.com/GitHub_Trending/compose/compose

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

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

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

立即咨询