从宿主机视角看容器进程
docker top 在宿主机上执行 ps 并按容器过滤,输出容器内运行的进程列表。它的价值在于不进容器、不依赖容器内存在 ps 或 shell,就能确认容器里到底在跑什么:PID 1 是谁、worker 有几个、有没有僵尸进程。相比之下 docker exec ps 需要容器内有可用命令,distroless 或 scratch 镜像上根本走不通。
基本信息
| 语法 | docker top CONTAINER [ps 选项] |
|---|---|
| 依赖 | 容器必须处于运行状态,宿主机需要存在 ps 命令 |
| 默认视图 | 等价于宿主机的 ps -ef 输出,列宽受 ps 默认设置影响 |
| 视角差异 | 输出的 PID 是宿主机命名空间下的进程号,不是容器内 PID |
| 权限 | 具备 Docker 访问权限的用户即可;只看本容器进程,不需要宿主 root |
典型使用场景
- 确认 PID 1 是什么:应用直接作为入口,还是被 shell 或启动脚本包了一层
- 排查僵尸进程:确认 PID 1 有没有回收子进程的能力
- 核对 worker 数量与配置是否一致,例如 Gunicorn/Node 集群模式的实际并发
- 容器内命令行为异常时,看实际启动命令与参数是否被覆盖
- 宿主整体负载高时,把高 CPU 进程对应到具体的容器
常用命令组合
常用命令组合
docker top myapp # 默认 ps -ef 视图
docker top myapp aux # 带 CPU 与内存列
docker top myapp -o pid,ppid,stat,cmd # 指定列,便于 grep 僵尸进程
docker inspect -f '{{.State.Pid}}' myapp # 取容器主进程的宿主 PID
nsenter -t $(docker inspect -f '{{.State.Pid}}' myapp) -p ps -ef # 容器内视角
ps -o pid,ppid,stat,cmd --ppid $(docker inspect -f '{{.State.Pid}}' myapp) # 宿主视角看子进程输出逐列读法
| 列 | 含义 | 异常表现 |
|---|---|---|
| PID | 宿主机命名空间下的进程号 | 与容器内 PID 不同,误用于容器内 kill 会失败或杀错 |
| PPID | 父进程号 | 大量子进程 PPID 为 1 且状态为 Z,说明 PID 1 未回收子进程 |
| STAT | 进程状态 | 长期 D 状态说明卡在 IO,Z 状态堆积说明僵尸未清理 |
| CMD | 实际启动命令 | 与 Dockerfile 的 ENTRYPOINT/CMD 不一致,说明被覆盖或被包装 |
| TIME/CPU | 累计 CPU 时间与占用 | 单进程长期接近满载,需先查该进程再看容器限制 |
PID 差异来自 PID 命名空间:容器内的 PID 1 在宿主机上是另一个较大的进程号。要在容器视角下操作,用 nsenter 指定命名空间,或直接用 docker exec 执行命令。
出现 defunct 僵尸进程说明 PID 1 没有回收子进程,常见于入口脚本没有用 exec 启动应用,或自身未处理 SIGCHLD。长期堆积会耗尽可用 PID,导致容器内无法再 fork 新进程。解法是让入口脚本 exec 应用进程,或给容器加 --init。