Docker Compose `docker compose top` 命令详解:查看 Compose 服务容器中的运行进程
docker compose top 是 Docker Compose CLI 中用于查看某个 Compose 项目内正在运行的容器进程的查询命令。它会在不进入容器的情况下,以表格形式一次性展示项目(或指定服务)内所有容器中当前执行的进程列表,非常适合用于排障时快速确认某个服务的入口进程、子进程及其 PID 状态。读完本文,你将掌握 top 子命令的完整用法、输出字段含义、服务筛选方式,并通过源码剖析理解它从 CLI 到底层 Docker API 的完整调用链。
命令概览:显示运行中的进程
根据该命令的参考文档 compose_top.md 及其机器可读元数据 docker_compose_top.yaml,命令功能为:
Display the running processes(显示运行中的进程)
完整的命令语法为:
docker compose top [SERVICES...]
在 cmd/compose/top.go 中可以看到命令的定义(topCommand 实现):
topCmd := &cobra.Command{
Use: "top [SERVICES...]",
Short: "Display the running processes",
RunE: Adapt(func(ctx context.Context, args []string) error {
return runTop(ctx, dockerCli, backendOptions, opts, args)
}),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
几个值得注意的细节:
Use字段表明SERVICES...是可选参数,不传时作用于项目内所有服务的容器。ValidArgsFunction: completeServiceNames(...)表示该命令支持服务名的 Shell 自动补全,与参考文档中其他子命令保持一致。- 该子命令在 cmd/compose/compose.go 中被注册进
docker compose根命令。
命令选项
参考文档中的 Options 表如下:
| Name | Type | Default | Description |
|---|---|---|---|
--dry-run |
bool |
Execute command in dry run mode |
--dry-run 实际上是定义在 docker compose 根命令上的持久化标志(persistent flag),因此它对包括 top 在内的所有子命令生效。其定义位于 cmd/compose/compose.go:
c.PersistentFlags().BoolVar(&dryRun, "dry-run", false, "Execute command in dry run mode")
当该标志被置位时,根命令在运行前会执行 dry-run 检测,并为后端注入 compose.WithDryRun 选项(见 cmd/compose/compose.go),从而把命令置于"演练"模式而非真正执行。
top 是一个只读查询命令,本身没有额外专属参数——不存在类似 ps 的格式化选项(--format),输出固定为文本表格。
使用示例
参考文档给出的典型输出示例如下:
$ docker compose top
example_foo_1
UID PID PPID C STIME TTY TIME CMD
root 142353 142331 2 15:33 ? 00:00:00 ping localhost -c 5
指定服务查看
当项目中运行多个服务时,可以只查询其中一部分(可传多个服务名):
$ docker compose top web db
不传任何参数时,则会汇总项目内当前正在运行的所有容器的进程信息。
当前实现下的完整输出列
在 cmd/compose/top.go 的格式化逻辑中,Compose 会在每行进程数据前补充**服务名(SERVICE)与副本序号(#)**两列,随后才是容器内进程的字段。这一行为可由 top_test.go 中的测试期望输出得到确认:
SERVICE # UID PID PPID C STIME TTY TIME CMD
simple 1 root 1 1 0 12:00 ? 00:00:01 /entrypoint
multiple 1 root 1 1 0 12:00 ? 00:00:04 /entrypoint
multiple 1 root 123 1 0 12:00 ? 00:00:42 sleep infinity
这意味着即使某容器内存在多个进程(如上例 multiple 服务的 /entrypoint 与 sleep infinity),它们也会在 SERVICE/# 标识下以多行分别展示,便于快速定位"哪个服务、哪个副本"上运行了哪些进程。
输出字段解读
参考文档示例中的各列含义与 Linux ps/docker top 对齐:
| 字段 | 含义 |
|---|---|
UID |
进程所属用户的 UID/用户名(容器内通常为 root) |
PID |
进程在容器内的进程号 |
PPID |
父进程 PID |
C |
CPU 使用率 |
STIME |
进程启动时间 |
TTY |
进程关联的终端,非终端进程显示为 ? |
TIME |
进程累计占用 CPU 的时间(HH:MM:SS) |
CMD |
进程对应的完整命令行 |
在此基础上,当前版本的 Compose CLI 还会在表格最前追加两个由 Compose 计算得出的定位列(见 collectTop 实现):
SERVICE:该进程所在容器所属的 Compose 服务名;#:该容器在服务副本中的序号(replica number)。
底层实现原理
docker compose top 从命令行到 Docker Engine 的数据链路共分三层,每一层在仓库中都能找到对应的实现文件。
第一层:CLI 命令编排
runTop(见 cmd/compose/top.go)负责执行流程编排,核心步骤为:
- 通过
opts.toProjectName(ctx, dockerCli)解析出项目名; - 通过
compose.NewComposeService(...)创建 Compose 后端服务; - 调用后端
backend.Top(ctx, projectName, services)获取各容器的进程摘要; - 按容器名排序后交给
collectTop+topPrint完成格式化输出。
第二层:后端聚合实现
真正调用 Docker Engine 的是 pkg/compose/top.go 中的 Top 方法(实现代码),其流程可以概括为:
- 将项目名统一转为小写;
- 调用
s.getContainers(ctx, projectName, oneOffInclude, false)拉取项目下所有容器; - 若传入了
services参数,则通过containers.filter(isService(services...))过滤,只保留属于指定服务的容器; - 利用
errgroup并发地对每个容器调用 Docker API 的ContainerTop:每个容器一个 goroutine,最终由topContent, err := s.apiClient().ContainerTop(ctx, ctr.ID, client.ContainerTopOptions{ Arguments: []string{}, })eg.Wait()汇总等待全部完成; - 从容器的
Labels中读取api.ServiceLabel与api.ContainerNumberLabel,从而补全该进程所属的服务名与副本序号(即输出中的SERVICE、#两列的数据来源)。
第三层:返回数据结构
后端返回的类型 ContainerProcSummary 定义在 pkg/api/api.go:
// ContainerProcSummary holds container processes top data
type ContainerProcSummary struct {
ID string
Name string
Processes [][]string
Titles []string
Service string
Replica string
}
其中 Titles 即输出表格的列标题,Processes 中每行元素与 Titles 一一对应;Service/Replica 则由 getContainers 阶段读取容器标签得到。同时 Top 方法也作为 Compose 接口的一部分被声明在 pkg/api/api.go,这意味着第三方 SDK 亦可调用同一能力。
格式化输出的容错设计
docker compose top 的表格格式化逻辑(collectTop 与 topPrint)针对多容器列不一致的场景做了特殊处理,这是它在工程实现上值得关注的细节:
- 动态列合并:注释明确说明后端返回的不同容器可能带不同的列集合。
collectTop使用一个map[string]int记录"列标题 → 输出位置",以先遇到为准;若某容器多出额外字段(如GID),该列会被动态追加到表格中。 CMD恒为最右列:无论中间插入了多少额外列,代码都会把CMD重排到最右端(见 top.go)。- 缺失值用
-填充:当某个容器不具备其他容器拥有的列时(例如没有PPID或GID),对应单元格输出-,而不是错位或留空。 - 等宽对齐输出:
topPrint使用 Go 标准库text/tabwriter(最小宽度 4、padding 为 2),保证表格列对齐;当没有任何进程数据时函数直接返回空输出。
测试用例验证
cmd/compose/top_test.go 用表格驱动的方式覆盖了格式化层的核心场景,可作为理解命令行为的"行为规格":
| 测试用例 | 验证点 |
|---|---|
noprocs |
容器内无进程时输出为空 |
simple |
标准 8 列表头(UID/PID/PPID/C/STIME/TTY/TIME/CMD)下的完整输出 |
noppid |
缺少 PPID 列时动态列合并且缺失值输出为 - |
extra-hdr |
出现额外列(如 GID)时新列被动态追加 |
multiple |
同一容器多个进程分行展示,测试注释说明其仅验证 Top() 之后的格式化与打印核心逻辑 |
该测试还验证了跨容器"列并集"的行为:当多个容器列不一致时,合并后的表头顺序按首次出现排布,且缺失字段以 - 占位,最终整体输出完全对齐。
与 ps 等查询命令的配合
docker compose top 与 docker compose ps 互补:ps 回答的是"有哪些容器、状态如何",而 top 回答的是"容器内部正在跑哪些进程、资源占用如何"。典型排障流程是先用 docker compose ps 确认容器处于运行态,再对异常服务执行:
$ docker compose top <service>
从而在不执行 docker compose exec 的前提下,快速判断入口进程是否存活、是否存在多余的残留子进程,以及进程累计 CPU 时间是否异常。同时该命令为只读查询,不会对运行中的容器产生任何副作用,可放心在排查时反复使用。
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