Docker Compose `docker compose up` 命令完全指南:构建、创建、启动与重建的编排核心
本文基于本仓库(Docker Compose)的官方命令参考文档 docs/reference/compose_up.md 编写,并结合
cmd/、pkg/下的命令实现与测试用例展开源码级解析。
docker compose up 是 Docker Compose 中使用频率最高的单一入口命令:它负责构建镜像、拉取镜像、创建并启动服务容器,并在前台模式下汇总聚合各容器的日志输出。本指南将系统讲解该命令的完整行为模型、全部选项参数、重建/级联/等待策略以及 pre_start 生命周期钩子的故障排查流程,帮助你掌握"一条命令拉起整个应用栈"背后可精确控制的分阶段语义。
docker compose up 的职责与完整生命周期
官方文档对 docker compose up 的定义是:为服务构建、创建、启动并附着到容器(Builds, (re)creates, starts, and attaches to containers for a service)。如果相关联的服务尚未运行,它也会顺带启动这些关联服务(dependency),因此它天然具备"从零拉起整个应用"的能力。
从源码调用链看,命令的核心实现在 cmd/compose/up.go,其底层最终落到 pkg/compose/up.go 的 composeService.Up:
func (s *composeService) Up(ctx context.Context, project *types.Project, options api.UpOptions) error {
err := Run(ctx, ..., func(ctx context.Context) error {
err := s.create(ctx, project, options.Create) // 阶段一:创建容器
if err != nil {
return err
}
if options.Start.Attach == nil { // 阶段二:后台模式直接启动
return s.start(ctx, project.Name, options.Start, nil)
}
return nil
}, "up", s.events)
...
return s.runInteractiveUp(ctx, project, options) // 前台模式:附着日志 + 交互
}
由此可以总结出 docker compose up 内部依次经历的阶段:
- 构建 / 拉取:若配置需要且策略允许,先执行镜像 build 或 pull;
- 创建(Create):创建各服务容器(此时并不启动,等价于
docker compose create); - 启动(Start):启动已创建的容器及其依赖服务;
- 附着(Attach,前台模式):聚合各容器输出,行为类似
docker compose logs --follow;当命令退出时,所有容器被停止。
命令格式为:
docker compose up [OPTIONS] [SERVICE...]
不指定 SERVICE 时作用于 compose 文件中的全部服务;指定一个或多个服务名时,默认仍会连带其依赖服务(除非传入 --no-deps)。实现上通过 upOptions.apply 完成服务子集筛选(cmd/compose/up.go),若结合 --no-deps 则使用 types.IgnoreDependencies 忽略依赖。
全量选项速查表
下表完整收录自 docs/reference/compose_up.md,与 cmd/compose/up.go 中实际注册的 cobra flags 一一对应:
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--abort-on-container-exit |
bool |
任一容器停止时停止所有容器,与 -d 不兼容 |
|
--abort-on-container-failure |
bool |
任一容器以失败退出时停止所有容器,与 -d 不兼容 |
|
--always-recreate-deps |
bool |
重建依赖容器,与 --no-recreate 不兼容 |
|
--attach |
stringArray |
仅附着到指定服务,与 --attach-dependencies 不兼容 |
|
--attach-dependencies |
bool |
同时附着到依赖服务的日志输出 | |
--build |
bool |
启动容器前先构建镜像 | |
-d, --detach |
bool |
分离模式:后台运行容器 | |
--dry-run |
bool |
以演练(dry run)模式执行命令 | |
--exit-code-from |
string |
返回指定服务容器的退出码,隐含 --abort-on-container-exit |
|
--force-recreate |
bool |
即使配置与镜像未变化也重建容器 | |
--menu |
bool |
前台附着时启用交互快捷键,与 --detach 不兼容;也可由环境变量 COMPOSE_MENU 控制 |
|
--no-attach |
stringArray |
不附着(不流式输出日志)到指定服务 | |
--no-build |
bool |
即使策略允许也不构建镜像 | |
--no-color |
bool |
单色输出 | |
--no-deps |
bool |
不启动关联服务 | |
--no-log-prefix |
bool |
日志中不打印前缀 | |
--no-recreate |
bool |
容器已存在则不重建,与 --force-recreate 不兼容 |
|
--no-start |
bool |
只创建不启动服务 | |
--pull |
string |
policy |
运行前拉取镜像("always"|"missing"|"never") |
--quiet-build |
bool |
抑制构建输出 | |
--quiet-pull |
bool |
拉取时不打印进度信息 | |
--remove-orphans |
bool |
移除不在 compose 文件中定义的服务容器 | |
-V, --renew-anon-volumes |
bool |
重建匿名卷,而非从旧容器继承数据 | |
--scale |
stringArray |
将 SERVICE 扩缩到 NUM 个实例,覆盖 compose 文件中的 scale 设置 |
|
-t, --timeout |
int |
0 |
附着或容器已运行时用于关停容器的超时秒数 |
--timestamps |
bool |
显示时间戳 | |
--wait |
bool |
等待服务处于 running/healthy,隐含分离模式 | |
--wait-timeout |
int |
0 |
等待项目达到 running/healthy 的最大秒数 |
-w, --watch |
bool |
监听源码,文件更新时重建/刷新容器 | |
-y, --yes |
bool |
对所有提示默认回答 yes,非交互式运行 |
标志间冲突校验:不可组合的选项
这些"不兼容"并非口头约定,而是在 PreRunE 阶段由 cmd/compose/up.go 的 validateFlags 强校验的。部分典型约束包括:
--detach不能与--abort-on-container-exit、--abort-on-container-failure、--attach、--attach-dependencies、--watch组合;--wait会自动隐含分离模式(up.Detach = true),因此同样不能再与上述附着类选项组合;--force-recreate与--no-recreate、--always-recreate-deps与--no-recreate、--no-recreate与--renew-anon-volumes、--build与--no-build、--no-build与--watch两两互斥;--exit-code-from与--abort-on-container-failure可叠加;--abort-on-container-exit与--abort-on-container-failure不可同时使用;--wait-timeout必须是非负整数。
前台 vs 后台:attach / detach 的输出控制模型
官方文档明确了三种常见运行形态:
$ docker compose up # 前台:聚合日志,Ctrl+C 退出后停止全部容器
$ docker compose up --detach # 后台:容器持续运行,命令立即返回
$ docker compose up --no-start # 只创建不启动(等价 create 阶段)
前台的"附着输出"默认包含所有被启动的服务(含依赖),因此默认输出形态等同于 docker compose logs --follow。当某些服务日志过于冗长时,可用三组标志精细裁剪:
--attach <service>:只附着指定服务(可重复),此时无法再附着依赖,故与--attach-dependencies互斥;--attach-dependencies:连依赖服务的日志一起附着;--no-attach <service>:从附着集合中剔除指定服务,保留其余服务的输出。
实现细节(cmd/compose/up.go):--attach 给出的服务名必须是被启动项目的一部分,否则直接报错 cannot attach to services not included in up;--no-attach 则作为过滤器在运行时从集合中 RemoveAll。另外,compose YAML 中服务若声明 attach: false,该服务默认就不会被自动附着(除非显式 --attach)。--no-log-prefix 关闭每行日志的 <service> | 前缀,--timestamps 追加时间戳,--no-color 关闭 ANSI 彩色输出,这三者共同决定日志渲染外观(对应 pkg/compose 的日志消费者构建处)。
重建策略:diverged / force / never 三种模式的取舍
docker compose up 在已有旧容器时,默认会根据"服务的配置或镜像自容器创建以来是否发生变化"来决定是否重建。变化的容器会被停止并重建,且保留已挂载的卷(mounted volumes);未变化的容器则保持原样。
从源码看,三种策略被建模为常量(pkg/api/api.go):
RecreateDiverged = "diverged":默认策略——仅当容器配置与 compose 模型出现分歧(diverges)时才重建;RecreateForce = "force":无条件重建;RecreateNever = "never":绝不重建。
CLI 层将用户标志翻译为这些策略(cmd/compose/create.go):
func (opts createOptions) recreateStrategy() string {
if opts.noRecreate {
return api.RecreateNever
}
if opts.forceRecreate {
return api.RecreateForce
}
if opts.noInherit { // -V / --renew-anon-volumes
return api.RecreateForce
}
return api.RecreateDiverged
}
func (opts createOptions) dependenciesRecreateStrategy() string {
if opts.noRecreate {
return api.RecreateNever
}
if opts.recreateDeps {
return api.RecreateForce // --always-recreate-deps
}
return api.RecreateDiverged
}
日常典型用法:
$ docker compose up # 只在配置/镜像变化时重建
$ docker compose up --no-recreate # 已存在容器一律不重建(例如只是想补启动缺失的服务)
$ docker compose up --force-recreate # 强制重建全部容器(如想应用运行时的环境变更)
$ docker compose up --always-recreate-deps # 每次重建依赖容器(常用于 CI 确保依赖最新)
注意 --renew-anon-volumes(-V)会强制重建并从策略上丢弃旧匿名卷数据,因其与"保留数据"的继承语义冲突,故与 --no-recreate 互斥。--timeout(-t)则作用于关停容器时的宽限期(秒),仅当用户在命令行显式指定时才会覆盖默认值(GetTimeout 依据 timeChanged 判断)。
Build 与 Pull 的精细控制
docker compose up 的拉取默认策略是 policy,即由镜像的 pull_policy 决定是否/何时拉取(注册默认值 "pull" 为 "policy")。若显式传入 --pull,则只接受三个取值之一:always、missing、never,且会把所选策略应用到项目全部服务(cmd/compose/create.go 中 Apply 遍历服务覆写 PullPolicy)。无效取值会报 invalid --pull option。
镜像构建相关的标志分三档:
--build:强制在启动前构建所有带build上下文的服务(实现上等效于把这些服务的pull_policy置为build,见 Apply);--no-build:关闭构建——即使某个服务按策略需要本地构建(如pull_policy: build)也跳过;该标志与--watch互斥;--quiet-build/--quiet-pull:分别抑制构建进度与拉取进度条输出。
实际构建以 api.BuildOptions 形式合并进 api.CreateOptions,且会覆盖"显式指定的服务 + 其依赖"这一完整集合(cmd/compose/up.go),保证了依赖镜像缺位时 up 仍能自举构建。
退出码、信号处理与级联停止
官方文档对退出语义给出明确的契约:
- 命令执行过程中遇到错误,退出码为
1; - 前台运行中被
SIGINT(Ctrl+C)或SIGTERM中断时,所有容器被停止,退出码为0。
升级版的级联(cascade)行为由三个标志提供,其中 --exit-code-from <service> 特别适合"启动完就跑"的任务型编排——它返回所选服务容器的退出码,并隐含启用 --abort-on-container-exit,即任何容器退出都会停止整栈:
func (opts upOptions) OnExit() api.Cascade {
switch {
case opts.cascadeStop:
return api.CascadeStop // --abort-on-container-exit
case opts.cascadeFail:
return api.CascadeFail // --abort-on-container-failure
default:
return api.CascadeIgnore
}
}
(见 cmd/compose/up.go;--exit-code-from 会把 cascadeStop 置真,见 validateFlags。)
典型场景示例——构建测试矩阵后按被测服务退出码判定 CI 成败:
$ docker compose up --exit-code-from tests --abort-on-container-failure
$ echo $? # 拿到 tests 服务容器的真实退出码
同时,级联停止与"前台附着输出"天然绑定,因此它们全部与 -d/--wait 互斥。
等待就绪:--wait / --wait-timeout
--wait 让 docker compose up 在容器启动后继续等待,直到项目内服务达到 running/healthy 状态(结合服务 healthcheck 与依赖条件判定),隐含分离模式,适合自动化脚本在 up 之后立即消费服务。等待时长上限由 --wait-timeout(秒)控制,超过则报错;两个参数在源码中分别落到 api.StartOptions.Wait 与 WaitTimeout(cmd/compose/up.go)。仓库中的端到端样例 pkg/e2e/testdata/TestUpWait/compose.yaml 展示了配合 depends_on: condition: service_completed_successfully 使用一次性任务的典型形态。
源码热更新:-w / --watch
-w, --watch 将 up 转为开发模式:附着输出的同时监听项目源码目录,文件变更即触发对应服务的镜像重建或容器刷新(refresh)。实现上由 pkg/compose/up.go 中创建的 Watcher 驱动,并可与交互菜单联动(见下文)。注意 watch 的语义要求可构建,因此与 --no-build 互斥;更多细节可参考仓库中的 compose_watch.md 与 pkg/watch/ 目录下的 watcher 实现。
扩缩容与孤儿容器清理
--scale SERVICE=NUM:命令行级扩缩容,覆盖 compose 文件中的scale/deploy.replicas;格式错误(缺少=或非数字)会由applyScaleOpts直接报错(cmd/compose/create.go);--remove-orphans:清理"属于当前项目但未在 compose 文件中定义"的孤儿容器。
值得一提的是这些行为同样受到环境变量的影响(定义见 cmd/compose/compose.go):
COMPOSE_REMOVE_ORPHANS:当命令行未显式指定--remove-orphans时,取其布尔值作为默认行为(up.go 的 PreRunE);COMPOSE_IGNORE_ORPHANS:从项目环境读取,若与--remove-orphans同时为真会直接报错冲突(up.go)。
交互式导航菜单:--menu 与 COMPOSE_MENU
前台附着模式下可启用交互式快捷键菜单(--menu)。其默认开启逻辑较为讲究(resolveNavigationMenu,cmd/compose/up.go):
- 输出非 TTY(例如被管道化)时强制关闭;
- 未显式传
--menu时读取环境变量COMPOSE_MENU(取值true/false); - 两者都未提供时默认
true。
最终菜单是否真正生效还要求当前 display 模式非 plain 且 stdin 为终端(见 cmd/compose/up.go 的组合条件)。由于它服务于前台附着,与 --detach 不兼容。启用后可通过快捷键在附着日志中执行暂停、终止、切换时间戳等操作(菜单与 watcher、detach 能力的接线在 pkg/compose/up.go)。
pre_start 生命周期钩子:失败时的保留与排查
Compose 支持通过 compose 文件中的 pre_start 钩子在服务容器真正启动前运行一次性任务容器。官方文档明确了失败语义:当某个 pre_start 钩子以非零码退出时,Compose 会中止该服务的启动,并保留(retain)这个钩子容器以便排查。参考 e2e 样例 pkg/e2e/testdata/TestPreStartHookSuccess/compose.yaml:
services:
sample:
image: alpine
command: sh -c 'cat /shared/init.txt && sleep 5'
volumes:
- data:/shared
pre_start:
- image: alpine
command: sh -c 'echo "initialized" > /shared/init.txt'
volumes:
data:
失败后的三条排查命令
钩子容器带有 com.docker.compose.hook=pre_start 标签,因此可精确过滤定位:
$ docker ps -a --filter label=com.docker.compose.hook=pre_start
$ docker logs <container-id>
钩子容器的自动清理机制
源码中的钩子实现位于 pkg/compose/pre_start.go:
- 每次
runPreStart开始时(pre_start.go),会先校验钩子配置(当前不支持per_replica: true,会直接报错并不触发任何 I/O),随后按声明的顺序顺序执行各钩子,任一失败即中断并向外抛错门控服务启动; - 每个钩子以临时容器形式运行,通过
VolumesFrom共享服务容器的卷、接入同一网络;数据写入请使用命名卷或 bind mount(匿名卷与 tmpfs 按副本隔离,不会共享给钩子); - 执行前会自动
removeOrphanPreStartContainers:把上一次失败遗留的同项目同服务HookLabel=pre_start钩子容器清理掉,避免累积。因此下一次docker compose up前会自动清掉旧钩子残留; docker compose down同样负责清理——pkg/compose/down.go 的removePreStartHookContainers会按项目名 + 服务名 + 钩子标签强制删除保留容器,对应测试TestDownRemovesRetainedPreStartHookContainers(pkg/compose/down_test.go)。
总结:一次 docker compose up 的决策路径
把以上要素串起来,一次不带参数的前台 docker compose up 大致走完这样一条决策链:
- 解析 compose 文件与选择的服务集合(校验
--exit-code-from服务存在性、--no-deps依赖裁剪、空集合时报no service selected,见 up.go); - 按
--pull/--build/--no-build决定拉取与构建动作; - 进入 create 阶段:依据
--no-recreate/--force-recreate/--always-recreate-deps/--renew-anon-volumes决定每类容器的重建策略; - 进入 start 阶段(除非
--no-start),前台则构造日志消费者并按--attach/--no-attach/--attach-dependencies/attach: false决定附着集合; - 若任一服务的
pre_start钩子失败,中止启动并保留钩子容器供排查; - 依据
--abort-on-container-exit/--abort-on-container-failure/--exit-code-from设定级联退出语义,配合--wait/--wait-timeout决定命令何时返回及以何退出码返回。
理解这条路径后,无论是日常本地开发(up --watch)、后台常驻(up -d)、CI 就绪等待(up --wait --wait-timeout 60)还是任务退出码透传(up --exit-code-from job),都能准确选对参数组合并预判行为。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00