
Docker Compose 停止容器docker compose stop 命令详解与源码级原理【免费下载链接】composeDefine and run multi-container applications with Docker项目地址: https://gitcode.com/GitHub_Trending/compose/composedocker compose stop用于停止 Docker Compose 项目中正在运行的容器但不会删除容器及其配置已停止的容器可以随时通过docker compose start重新启动。它是 Compose 生命周期管理中最常用、最安全的“暂停式”操作之一与down停止并移除资源、pause冻结进程等命令有本质区别。本文以 Docker Compose 官方参考文档 docs/reference/compose_stop.md 为骨架结合命令实现cmd/compose/stop.go、服务端编排逻辑pkg/compose/stop.go以及端到端测试完整解析其用法、参数、底层调用链与实战注意事项帮助你精准掌控容器停止行为。一、命令概览与选项说明根据参考文档docker compose stop的完整用法为docker compose stop [OPTIONS] [SERVICE...]命令的官方元数据见 docs/reference/docker_compose_stop.yaml定义如下NameTypeDefaultDescription说明--dry-runboolfalseExecute command in dry run mode演练模式只打印将执行的动作而不真正停止-t,--timeoutint0Specify a shutdown timeout in seconds以秒为单位的停机超时时间两个关键点SERVICE...是可选参数。不带任何服务名时停止整个项目的所有服务容器带上一个或多个服务名时只停止指定服务其依赖服务保持运行状态下文第三节会从代码层面验证这一点。--dry-run是全局持久化参数。从源码看它并非在stop命令内部单独定义而是在根命令上通过c.PersistentFlags().BoolVar(dryRun, dry-run, false, ...)注册见 cmd/compose/compose.go因此对up、down、restart、stop等命令通用。启用后后端会被包装进演练模式backendOptions.Add(compose.WithDryRun)见 cmd/compose/compose.goCompose 会输出“将要做什么”而不真正对容器下发停止指令适用于发布前核对操作范围。stop子命令的 CLI 定义位于 cmd/compose/stop.gocmd : cobra.Command{ Use: stop [OPTIONS] [SERVICE...], Short: Stop services, PreRun: func(cmd *cobra.Command, args []string) { opts.timeChanged cmd.Flags().Changed(timeout) }, RunE: Adapt(func(ctx context.Context, args []string) error { return runStop(ctx, dockerCli, backendOptions, opts, args) }), ValidArgsFunction: completeServiceNames(dockerCli, p), } flags : cmd.Flags() flags.IntVarP(opts.timeout, timeout, t, 0, Specify a shutdown timeout in seconds)可以看到ValidArgsFunction: completeServiceNames(...)让 shell 可以对服务名做自动补全。而PreRun中通过cmd.Flags().Changed(timeout)记录用户是否显式传入过--timeout——这个细节对超时语义至关重要见第三节。二、核心语义停止而不移除随时可重启参考文档对命令行为的定义非常简短但精确Stops running containers without removing them. They can be started again withdocker compose start.即stop只把运行中的容器置为exited已退出状态容器对象本身、其文件系统层、网络配置和 Compose 项目标记都原样保留。这意味着不会销毁数据容器内的写层数据不会被清除down的-v、rm的-v才会连带删除卷。可无缝恢复直接执行 docker compose start 即可把同一批容器原样启动回来无需重新up、重建或重新分配端口容器 ID 不变。ps/ls可见性差异停止后的项目在默认的docker compose ps/ls输出中不再显示为 running需要使用--all才能看到 exited 状态的旧容器。这一“同容器复活”的行为在端到端测试中有明确断言。在 pkg/e2e/start_stop_test.go 的TestStartStop中测试依次执行Step(up starts every service, ComposeCmd(up, -d), ...) Step(stop halts the containers without removing them, ComposeCmd(stop), ServiceState(simple, exited), ServiceState(another, exited)) Step(a stopped project is hidden from ls, ...) Step(ls --all still lists the stopped project, ...) Step(start brings the same containers back, ComposeCmd(start), ServiceState(simple, running), ServiceState(another, running), NotRecreated(simple, another))注意最后一步的NotRecreated(simple, another)断言——它证明start唤醒的是原有的容器实例而非新建容器。对应的最小测试项目只有两个服务pkg/e2e/fixtures/start-stop/compose.yamlservices: simple: image: nginx:alpine another: image: nginx:alpine三、服务选择与停止顺序的源码级解析stop命令最终通过后端backend.Stop(...)进入服务编排层。调用链如下CLI 层runStopcmd/compose/stop.go负责解析项目名并组装api.StopOptionsAPI 层composeService.Stoppkg/compose/stop.go通过Run(...)包装后在事件总线上广播一个名为stop的阶段事件内部实现stop(...)pkg/compose/stop.go真正执行停止逻辑。api.StopOptions的结构定义在 pkg/api/api.go// StopOptions group options of the Stop API type StopOptions struct { // Project is the compose project used to define this app. Might be nil if user ran command just with project name Project *types.Project // Timeout override container stop timeout Timeout *time.Duration // Services passed in the command line to be stopped Services []string }结合 pkg/compose/stop.go 的实现可以提炼出几个关键行为规则规则 1默认停止整个项目可精确到指定服务。当没有传入任何服务名时代码用project.ServiceNames()补全全部服务而当只指定部分服务时其余服务不受影响if len(options.Services) 0 { options.Services project.ServiceNames() }规则 2按“依赖逆序”停止且允许指定服务跳过遍历。停止通过InReverseDependencyOrder遍历依赖图先停被依赖方、再停依赖方避免服务在停止过程中请求已经不可用的上游return InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error { if !slices.Contains(options.Services, service) { return nil } ... })TestStartStopWithDependenciespkg/e2e/start_stop_test.go用“foo 依赖 bar”的项目验证了docker compose stop foo只会停掉 foo、bar 保持 running随后docker compose start foo会把依赖 bar 一并启动回来。规则 3one-off一次性/手动 run容器不受影响。在拉取容器列表时使用了oneOffExclude过滤仅处理由 Compose 管理、带com.docker.compose服务标签的常规服务容器。TestStartStopWithOneOffspkg/e2e/start_stop_test.go专门验证了docker compose run -d产生的一次性容器在stop/start/restart前后始终保持运行、不被误伤。规则 4由插件Provider管理的服务交给插件处理。对于设置了Provider的服务扩展型服务不会直接调用 Docker API而是走runPlugin(ctx, project, serv, stop)把停止操作委托给对应的插件实现。四、单容器停止与超时-t / --timeout机制最终对每个容器的停止动作在 pkg/compose/down.go 的stopContainer中完成func (s *composeService) stopContainer(...) error { eventName : getContainerProgressName(ctr) s.events.On(newEvent(eventName, api.Working, api.StatusStopping)) if service ! nil { for _, hook : range service.PreStop { err : s.runHook(ctx, ctr, *service, hook, listener) ... } } _, err : s.apiClient().ContainerStop(ctx, ctr.ID, client.ContainerStopOptions{ Timeout: utils.DurationSecondToInt(timeout), }) ... s.events.On(newEvent(eventName, api.Done, api.StatusStopped)) return nil }该函数的执行细节揭示了三个与参考文档对应的深层机制进度事件停止前后会发出StoppingWorking与StoppedDone状态事件这是docker compose stop输出进度条/状态列表的数据来源。PreStop 钩子若服务的 Compose 模型配置了x-扩展字段定义的 PreStop hooks相关测试见TestPreStopHookSuccess等 fixture会在真正调用 Docker 停止 API 之前依次执行若钩子执行时容器已经不存在或已处于停止冲突状态NotFound/Conflict该错误会被吞掉并直接返回。超时透传用户传入的--timeout秒数被换算成*time.Duration再通过ContainerStop交给容器引擎。这里需要特别解释--timeout的“默认 0”到底意味着什么。回到 CLI 层 cmd/compose/backend.go 的工具函数// optionalTimeout converts an integer timeout (in seconds) into a *time.Duration. // If changed is false, nil is returned (no timeout was explicitly set). func optionalTimeout(t int, changed bool) *time.Duration { if !changed { return nil } d : time.Duration(t) * time.Second return d }只有当用户在命令行中显式写入了-t/--timeout时PreRun阶段记录的timeChanged为true超时值才会被封装为*time.Duration传入StopOptions.Timeout否则传nil即不覆盖任何默认值交由容器引擎自身的停机超时策略决定。因此docker compose stop不传-t使用引擎默认的停止宽限期容器收到SIGTERM后若在宽限期内未自行退出才被强制SIGKILL是优雅停机的推荐姿势docker compose stop -t 30给容器 30 秒的优雅退出时间窗口docker compose stop -t 0不等待优雅退出立即强制终止。同时对同一个服务下的多个副本容器停止是并行执行的——stopContainerspkg/compose/down.go利用errgroup并发下发stopContainer因此副本数多时不会串行拖慢整体停机速度。五、实战场景与常用组合基于上面的事实docker compose stop在以下几类场景中最有价值场景 A临时下线项目、保留现场需要暂停服务但马上又要恢复如维护窗口、夜间节能用 stop start 即可“原封不动”地回来# 停掉整个项目容器保留退出码路径不变 docker compose stop # 稍后原容器恢复运行 docker compose start场景 B只停部分服务保留依赖运行对某个无状态服务做灰度下线其上游 DB 保持在线docker compose stop web docker compose ps # 观察 webexiteddb 仍 running docker compose start web注意与stop配套的语义边界docker compose restartdocs/reference/compose_restart.md会先停再启、适合快速生效配置变更而docker compose pausedocs/reference/compose_pause.md使用SIGSTOP冻结进程、容器状态仍显示为 running适合临时挂起down/rm则直接移除容器属于破坏性操作。若不确定命令是否会误删资源可以先加--dry-run演练docker compose --dry-run stop该参数会在不触碰真实容器的前提下把本次stop将要执行的操作打印出来供核对。场景 C脚本化、CI 中的幂等收尾由于停止不改变项目资源结构在 CI 中反复stop/start不会产生资源泄漏也不会像down那样需要重新走一次网络/卷的创建流程收敛速度快、副作用小。停止后如需彻底清理再显式执行docker compose down。六、相关文档与源码索引命令参考本文主体 docs/reference/compose_stop.md配套元数据 docs/reference/docker_compose_stop.yaml反向命令docker compose start、docker compose restart、docker compose pauseCLI 入口与参数绑定cmd/compose/stop.go持久化--dry-run注册于 cmd/compose/compose.go后端编排pkg/compose/stop.go依赖逆序、插件代理、one-off 过滤容器级实现stopContainer/stopContainerspkg/compose/down.goAPI 选项结构StopOptionspkg/api/api.go行为验收测试pkg/e2e/start_stop_test.go含TestStartStop、TestStartStopWithDependencies、TestStartStopWithOneOffs、TestStartStopMultipleServices等fixture 见 pkg/e2e/fixtures/start-stop/compose.yaml七、小结docker compose stop的核心价值在于“停而不删、启而原样”容器进入 exited 状态但实例、数据、网络归属全部保留配合start可实现无损的上下线循环。把握三个要点即可用好该命令不带服务名停整个项目、-t不显式传入时沿用引擎默认优雅停机窗口、one-off 与依赖服务遵循独立规则不受连带影响。如果需要在不产生任何副作用的前提下确认操作范围别忘了--dry-run这个全局演练开关。【免费下载链接】composeDefine and run multi-container applications with Docker项目地址: https://gitcode.com/GitHub_Trending/compose/compose创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考