查看容器内部进程的理论与实践
在容器化环境中,进程是应用运行的核心载体。查看容器内部进程不仅用于常规监控,更是排查故障(死锁、僵尸进程、内存泄漏)、验证资源使用和确保安全合规(如无恶意进程)的关键手段。由于容器的隔离基于 Linux 的 PID 命名空间,从宿主机视角与容器内部视角看到的进程信息有所不同,因此需要掌握多种查看方式。
一、为什么需要查看容器内进程?
| 目标 | 说明 |
|---|---|
| 健康监控 | 确认应用主进程及其子进程是否正常运行 |
| 故障排查 | 发现死锁、僵尸进程、内存泄漏、CPU 飙高的元凶 |
| 资源分析 | 将资源使用(CPU/内存)精确到进程级别,而非仅容器整体 |
| 安全审计 | 检测容器内是否运行了未授权的恶意进程(如挖矿、反弹shell) |
| JVM 诊断 | 对 Java 应用进行线程转储、堆转储等需要先获取 PID |
二、核心原理:PID 命名空间与进程可见性
Linux 内核通过PID 命名空间实现进程隔离。每个容器拥有独立的 PID 命名空间,容器内进程看到的 PID 从 1 开始。而在宿主机上,这些进程拥有全局唯一的 PID。Docker 通过/proc文件系统和 cgroups 维护进程映射关系。
- 宿主机视角:可以直接看到所有容器的进程,它们都是宿主机进程树的叶子节点。通过
ps aux或pstree可以识别出容器进程(通常其父进程为containerd-shim)。 - 容器内部视角:只能看到本命名空间内的进程,PID 是独立的,但实际共享宿主机的内核。
架构示意:
三、查看容器内进程的主要方法
3.1 从宿主机直接查看
a)docker top命令
Docker 提供了docker top <container>命令,它直接在宿主机上列出指定容器内的进程(类似于ps,但自动过滤)。其原理是读取容器 cgroup 关联的 PID 列表,并调用宿主机ps获取详细信息。输出包括宿主机 PID、用户、命令等,非常适合快速定位。
b)docker exec配合内部ps
通过docker exec <container> ps aux可以在容器内运行ps命令,获取容器内部视角的 PID 和进程信息。但需要容器镜像包含ps工具(最小化镜像如 Distroless 可能没有)。
c) 宿主机ps加过滤
直接使用宿主机ps aux | grep <容器ID>或利用 cgroup 信息(/sys/fs/cgroup/cpu/system.slice/docker-<ID>.scope/cgroup.procs)也可以找到容器进程。这种方法不依赖 Docker 命令,适合脚本化采集。
3.2 从容器内部查看
进入容器后执行ps aux或top。缺点:需要 Shell 和相应工具,而许多生产镜像已经剔除(如 Distroless)。进入方式:docker exec -it <container> /bin/sh(若有 Shell)。
3.3 编排平台(Kubernetes)
kubectl exec <pod> -- ps aux:在 Pod 容器内执行命令(类似 docker exec)。kubectl top pod:查看资源使用,不直接显示进程,但可结合kubectl exec排查。- Dashboard / 监控系统:Kubernetes 节点上的 cAdvisor 可采集容器级指标,但不暴露单个进程。进程级监控通常需要额外的 APM 或 eBPF 工具。
3.4 高级:基于 eBPF 的进程追踪
现代工具(如Falco、Cilium、Tracee)使用 eBPF 在宿主机上安全地追踪容器内的进程创建、系统调用等,无需侵入容器。这可用于实时监控进程变化,排查恶意进程注入。
四、方法对比表
| 方法 | 视角 | 是否依赖容器内工具 | 可获取PID | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|---|---|
| docker top | 宿主机 | 否 | 宿主机PID、容器内PID | 快速、无需进入容器、总是可用 | 信息有限(类似ps aux),不能交互 | 快速查看容器运行了哪些进程 |
| docker exec ps | 容器内部 | 是(需要ps) | 容器内PID | 输出与在VM中一致,可自定义参数 | 依赖镜像是否包含ps;非侵入性稍差 | 需要完整进程树或自定义过滤时 |
| 宿主机 ps + grep | 宿主机 | 否 | 宿主机PID | 无需Docker命令,适合脚本 | 需要手动映射,容器多时较乱 | 批量采集、告警脚本 |
| kubectl exec | 容器内部 | 是(需要ps) | 容器内PID | 集群统一入口 | 同docker exec,依赖网络和认证 | Kubernetes 环境 |
| eBPF 工具 | 宿主机 | 否 | 宿主机PID | 实时、无侵入、可关联容器标签 | 技术要求较高 | 安全监控、深度性能追踪 |
五、与 Java 应用的集成:获取 JVM 进程 PID
对 Java 容器进行诊断(jstack、jmap、jcmd)时,必须先获取 JVM 进程的 PID。
- 从宿主机:使用
docker top <container>或ps找到 Java 进程的宿主机 PID,然后使用jcmd <host_pid>(需要安装 JDK 且版本匹配),更推荐的方式是通过docker exec在容器内运行jps或jcmd,以避免类库不匹配。 - 在容器内:进入容器执行
jps -v或jcmd -l,然后执行诊断命令。这要求容器镜像包含 JDK 工具(通常使用完整 JDK 而非 JRE)。可分离构建和运行,调试时临时挂载 JDK。
获取PID用于分析的流程图:
六、排查僵尸进程与孤儿进程
容器内 PID 1 进程(主进程)负责回收僵尸进程。如果主进程没有正确处理 SIGCHLD 信号,僵尸进程会堆积,占用 PID 资源。通过docker top或docker exec查看进程状态(defunct)。解决方案是使用初始化进程(如tini、dumb-init)作为 PID 1,它专门负责回收。
七、思维导图总结
通过以上理论与方法,可以系统化地阐述如何查看容器内的进程,并展示在 Java 微服务故障诊断和安全监控中的实际应用。