首页
/ 掌握 docker compose pause:精准冻结与恢复 Compose 服务容器的原理与实践

掌握 docker compose pause:精准冻结与恢复 Compose 服务容器的原理与实践

2026-09-05 23:39:04作者:蔡丛锟

docker compose pause 是 Docker Compose CLI 提供的服务级冻结命令,可在不停止、不重建容器的前提下暂停目标服务的所有运行中容器,并可通过配对的 docker compose unpause 命令原位恢复。本文基于官方参考文档 docs/reference/compose_pause.md 展开,结合 CLI 入口、API 定义与服务层源码实现,完整讲解该命令的选项、执行流程、行为边界与端到端测试验证,帮助你在故障隔离、维护窗口等场景中安全使用容器暂停能力。

命令概览与官方选项

docker compose pause 的官方描述为:

Pauses running containers of a service. They can be unpaused with docker compose unpause.

即:暂停某服务当前正在运行的容器;这些容器之后可以使用 docker compose unpause 恢复。命令的完整用法为:

docker compose pause [SERVICE...]

要点如下:

  • 位置参数 [SERVICE...]:可指定一个或多个服务名。省略时作用于项目内所有服务;指定时只冻结对应服务的容器,其余服务不受影响。
  • --dry-run 选项:唯一的命令级专属选项,bool 类型,无默认值,作用是 “Execute command in dry run mode”,即以干跑模式执行,不产生真实的容器状态变更,便于在执行前预检将要影响哪些容器。
Name Type Default Description
--dry-run bool Execute command in dry run mode

与之配对的 docker compose unpause [SERVICE...] 命令用于解冻已暂停的容器,其官方文档见 docs/reference/compose_unpause.md,描述为 “Unpauses paused containers of a service”,同样支持 --dry-run

典型使用场景

在实际运维中,pause/unpause 组合适合以下场景:

# 启动应用(假设 compose.yaml 定义 a、b 两个服务)
docker compose up -d

# 仅冻结服务 a:a 的进程被内核停止,b 继续正常运行
docker compose pause a

# 恢复服务 a:容器原位恢复运行,不会被重建
docker compose unpause a

docker compose stop 相比,pause 的语义更轻:

  • stop 会发送终止信号并删除容器,再次运行需要 start/up 重新启动,容器资源(网络命名空间内的进程态、内存态等)全部丢失;
  • pause 只冻结容器内进程(内核级暂停),容器本身、网络与卷挂载保持不变,unpause 后进程从冻结点继续执行,适合“临时让某个依赖静默”而不破坏运行状态的操作。

CLI 入口:从 cobra 命令到 API 调用

命令在 cmd/compose/pause.go 中注册。pauseCommandunpauseCommand 共用几乎对称的结构:

func pauseCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *BackendOptions) *cobra.Command {
	opts := pauseOptions{
		ProjectOptions: p,
	}
	cmd := &cobra.Command{
		Use:   "pause [SERVICE...]",
		Short: "Pause services",
		RunE: Adapt(func(ctx context.Context, args []string) error {
			return runPause(ctx, dockerCli, backendOptions, opts, args)
		}),
		ValidArgsFunction: completeServiceNames(dockerCli, p),
	}
	return cmd
}

执行链路为 runPausecmd/compose/pause.go):

  1. 调用 opts.projectOrName(ctx, dockerCli, services...) 解析 Compose 项目,得到项目模型 project 与项目名 name
  2. 通过 withBackend 获取当前后端实现,调用 backend.Pause(ctx, name, api.PauseOptions{Services: services, Project: project})

unpause 侧的 runUnPausecmd/compose/pause.go)结构完全对称,调用 backend.UnPause,参数同样是 api.PauseOptions

API 层定义:Pause 与 UnPause 接口

后端需要实现的接口声明在 pkg/api/api.go

Pause(ctx context.Context, projectName string, options PauseOptions) error
// UnPause executes the equivalent to a `compose unpause`
UnPause(ctx context.Context, projectName string, options PauseOptions) error

两者共用的参数结构 PauseOptionspkg/api/api.go):

// PauseOptions group options of the Pause API
type PauseOptions struct {
	// Services passed in the command line to be started
	Services []string
	// Project is the compose project used to define this app. Might be nil if user ran command just with project name
	Project *types.Project
}

其中 Project 可能为 nil——当用户只提供项目名而未加载完整模型时即为空,服务层会据此决定是否做二次过滤。

服务层实现:容器筛选与并发暂停

核心实现位于 pkg/compose/pause.gocomposeService.Pause 的完整流程:

func (s *composeService) Pause(ctx context.Context, projectName string, options api.PauseOptions) error {
	return Run(ctx, func(ctx context.Context) error {
		return s.pause(ctx, strings.ToLower(projectName), options)
	}, "pause", s.events)
}

func (s *composeService) pause(ctx context.Context, projectName string, options api.PauseOptions) error {
	containers, err := s.getContainers(ctx, projectName, oneOffExclude, false, options.Services...)
	if err != nil {
		return err
	}

	if options.Project != nil {
		containers = containers.filter(isService(options.Project.ServiceNames()...))
	}

	return forEachContainerConcurrent(ctx, containers, func(ctx context.Context, ctr container.Summary) error {
		_, err := s.apiClient().ContainerPause(ctx, ctr.ID, client.ContainerPauseOptions{})
		if err == nil {
			s.events.On(newEvent(getContainerProgressName(ctr), api.Done, "Paused"))
		}
		return err
	})
}

实现细节可以拆解为四点:

  1. 项目名统一转小写strings.ToLower(projectName),保证标签匹配的大小写一致性。
  2. 容器筛选getContainers(ctx, projectName, oneOffExclude, false, options.Services...) 按项目名和命令行传入的服务名取容器,oneOffExclude 表示排除一次性(one-off)容器;随后若 options.Project != nil,再用 isService(...) 过滤出确实属于模型中服务名的容器,防止项目名恰好命中的无关容器被误操作。
  3. 并发执行forEachContainerConcurrent 对每个容器并发调用 Docker Engine 的 ContainerPause API(即引擎级 POST /containers/{id}/pause,其语义是暂停容器内所有进程)。
  4. 进度事件:每成功暂停一个容器,就通过 s.events.On(...) 发出 “Paused” 完成事件,供 TTY 进度展示层渲染逐容器状态;UnPausepkg/compose/pause.go)同理,调用 ContainerUnpause 并发出 “Unpaused” 事件。

UnPausePause 的唯一差别在于引擎调用是 ContainerUnpause 且事件文案为 “Unpaused”,其余筛选逻辑完全一致,这保证了“暂停谁、恢复谁”的目标集合严格对应。

行为边界:e2e 测试验证的关键语义

仓库的端到端测试 pkg/e2e/pause_test.go 用真实场景固化了四个重要行为,这些结论比文档描述更具体,值得作为使用时的行为契约:

1. 只冻结目标服务,且 unpause 原位恢复TestPause

Step("up starts both services",
	ComposeCmd("up", "-d"),
	ServiceState("a", "running"),
	ServiceState("b", "running")).
Step("pause freezes the targeted service, the other keeps running",
	ComposeCmd("pause", "a"),
	ServiceState("a", "paused"),
	ServiceState("b", "running")).
Step("unpause resumes the paused service without recreating anything",
	ComposeCmd("unpause", "a"),
	ServiceState("a", "running"),
	ServiceState("b", "running"),
	NotRecreated("a", "b"))

测试夹具 pkg/e2e/testdata/TestPause/compose.yaml 定义了 ab 两个 alpine 服务。NotRecreated("a", "b") 断言确认 unpause 不会触发容器重建——进程与容器 ID 均保持原位。

2. 对无容器的服务执行 pause 是成功的 no-opTestPauseServiceNotRunning):服务没有运行中容器时,pause 不报错直接成功。测试中还留有一处 TODO,指出 docker pause 在该场景下会报错,Compose 是否与其保持一致仍是开放问题。

3. 重复暂停会失败TestPauseServiceAlreadyPaused):对已暂停的服务再次执行 pause,命令以退出码 1 失败,输出包含 already paused。这与底层引擎 API 的行为一致(暂停已暂停容器返回冲突错误),提示在脚本中应先检查状态或容忍该错误。

4. 未知服务名会被拒绝TestPauseServiceDoesNotExist):docker compose pause does_not_exist 以退出码 1 失败,输出包含 no such service: does_not_exist,说明服务名必须存在于 Compose 模型中,而不是任意容器标签。

与干跑模式的配合

--dry-run 选项允许在执行前预检暂停操作的影响面。仓库在 pkg/dryrun/dryrunclient.go 中提供了干跑客户端实现,从源码结构看,干跑模式下引擎 API 调用会被替换为只读模拟,命令走完项目解析与容器筛选流程但不真正调用 ContainerPause。在批量操作前使用:

# 预检将受影响的容器,不产生实际变更
docker compose pause --dry-run a

有助于确认筛选范围符合预期。

小结

docker compose pause 是一条语义清晰、行为边界明确的服务级冻结命令:

  • CLI 层(cmd/compose/pause.go)负责解析项目与 SERVICE... 参数,将操作收敛为 api.PauseOptions{Services, Project}
  • 服务层(pkg/compose/pause.go)完成“按项目与服务名筛选容器 → 排除 one-off 容器 → 并发调用引擎 Pause/Unpause API → 发出进度事件”的完整链路;
  • e2e 测试(pkg/e2e/pause_test.go)固化了“精确作用于目标服务”“恢复不重建”“重复暂停报 already paused”“未知服务报 no such service”等关键契约。

理解这些实现细节后,你可以放心地将 pause/unpause 用于故障隔离、维护窗口和依赖静默等场景,并准确预判重复操作、未知服务名等边界情况下的命令行为。

登录后查看全文
热门项目推荐
相关项目推荐