需对齐命名空间并获取容器进程在宿主机的真实PID:先用docker inspect -f '{{.State.Pid}}'查容器init进程宿主机PID,再用nsenter进入其PID命名空间执行pgrep或ps定位子进程,最后用pidstat -p指定PID监控。

直接在宿主机上用 pidstat 查容器内进程,通常看不到真实 PID 或数据不准——因为容器进程在宿主机的 PID 命名空间里是独立编号的,而应用日志、监控埋点里显示的 PID 是容器内部视角(即 init 进程 PID=1 的命名空间)。要准确监控,关键在于“对齐命名空间”和“获取正确的 PID”。
确认容器内进程在宿主机上的真实 PID
容器启动后,它的所有进程在宿主机上都有对应 PID,只是编号不同。不能直接用容器内看到的 PID(比如 Java 应用日志里写的 PID 123)去查宿主机:
• 先查容器 ID 或名称:docker ps 或 crictl ps
• 再获取该容器在宿主机的 PID 命名空间根进程 PID:docker inspect -f '{{.State.Pid}}' 容器ID或名称
• 或者更通用(兼容 containerd):cat /proc/$(docker inspect -f '{{.State.Pid}}' 容器名)/status | grep -i pidns(辅助验证)
这个输出的数字,就是容器 init 进程在宿主机的 PID,也是后续所有容器内进程的父 PID。
用 pidstat 监控容器内指定进程
拿到宿主机视角的真实 PID 后,就能正常监控:
• 如果只想看容器主进程(如 Java、Nginx 主进程):pidstat -p $(docker inspect -f '{{.State.Pid}}' myapp) -ur 1
• 如果要监控容器内某个子进程(比如一个 worker 线程),需先进入容器命名空间再找 PID:nsenter -t $(docker inspect -f '{{.State.Pid}}' myapp) -p ps -eo pid,comm --sort=-pcpu | head -n 5
然后取第一列 PID,传给 pidstat -p PID -u 1
• 更稳妥的做法是结合 pgrep 在命名空间内执行:nsenter -t $(docker inspect -f '{{.State.Pid}}' myapp) -p pgrep -f 'java.*server'
监控多进程或动态进程(如 Python 多 worker)
容器内常有多个同名进程(如 Gunicorn 的多个 worker),宿主机上它们 PID 不同但归属同一容器:
• 先批量获取所有匹配进程的宿主机 PID:pidlist=$(nsenter -t $(docker inspect -f '{{.State.Pid}}' myapp) -p pgrep -f 'gunicorn.*worker')
• 再喂给 pidstat:pidstat -p "$pidlist" -ur 2
注意:$pidlist 是空格分隔的数字串,pidstat 支持多 PID 同时监控,无需循环调用。
• 若担心进程启停导致 PID 变化,可加 -l 显示完整命令行,便于核对:pidstat -p "$pidlist" -ul 2
避免常见偏差和权限问题
• 权限:宿主机上运行 pidstat 的用户需有读取 /proc/PID/stat 和 /proc/PID/status 的权限。普通用户查不到 systemd、dbus 等高权限进程,容器进程一般无此限制,但若容器以 --user 启动且 UID 映射复杂,可能影响 nsenter 执行。
• 时间精度:容器进程生命周期短(如 FaaS 场景),建议采样间隔 ≤0.5 秒(如 pidstat -p PID 0.2),否则容易漏采。
• RSS 误判:容器启用透明大页(THP)时,RSS 会虚高。可用 pidstat -p PID -r -H 1 加高精度时间戳,并比对 /proc/PID/status 中的 MMUPageSize 字段确认是否为 THP 分配。


















