LabHub
学习 学习路径 课程

容器内部原理

证明容器存在的四个文件

在 LabHub 中继续学习

一句话总结

内核中不存在名为“容器”的数据结构。相反,每个进程的 /proc/<pid>/ 下都记录着命名空间、cgroup、挂载和权限。读取这四类文件,就能完整看出该进程处于怎样的隔离环境中。

概念图: 命名空间、cgroup、挂载和权限 · inode 编号 · 包含容器 ID · 镜像层

为什么需要它

要用 Docker 命令回答“这个进程是否位于容器内”,前提是系统中装有 Docker。但故障调查时遇到的情形通常正好相反:在主机执行 ps 后发现陌生进程正在消耗 CPU,需要知道它属于哪个容器,以及受到哪些限制。

无论使用 Docker、containerd 还是 Podman,/proc 都会给出相同答案,因为我们不是询问运行时,而是直接询问内核。

文件 1 — /proc/PID/ns/:能看到什么

$ ls -l /proc/1/ns/
mnt -> 'mnt:[4026532187]'
net -> 'net:[4026532190]'
pid -> 'pid:[4026532188]'

方括号中的数字是inode 编号,编号相同就表示处于同一命名空间。将其与主机的 /proc/1/ns/net 比较:不同表示网络已隔离,相同则表示使用了 --network host。比较两个容器,还能知道它们共享了哪些命名空间。

文件 2 — /proc/PID/cgroup:能使用多少资源

0::/kubepods.slice/kubepods-burstable.slice/.../cri-containerd-9f3c1a....scope

cgroup v2 中只有一行。该路径中包含容器 ID,这是在主机上确认陌生进程身份的最快方法。沿着该路径进入 /sys/fs/cgroup/,还可以读取实际限制值。

/sys/fs/cgroup/<경로>/memory.max      # 메모리 상한
/sys/fs/cgroup/<경로>/memory.current  # 현재 사용량
/sys/fs/cgroup/<경로>/cpu.max         # "200000 100000" = 2 코어

文件 3 — /proc/PID/mountinfo:文件来自哪里

容器的根文件系统通常是 overlay。

... / overlay rw,lowerdir=/var/lib/.../l/ABC:/var/lib/.../l/DEF,upperdir=...

lowerdir 中由冒号连接的列表是镜像层upperdir 是容器的可写层。这里也能看到卷挂载,可用于确认哪个主机路径挂到了什么位置。

文件 4 — /proc/PID/status:能够做什么

CapEff:	00000000a80425fb
NoNewPrivs:	1
Seccomp:	2

CapEff有效 capability 位掩码。值为 0 表示没有任何特权;值为 0000003fffffffff 则几乎拥有全部特权,是 --privileged 留下的典型痕迹。NoNewPrivs: 1allowPrivilegeEscalation: false 的结果,Seccomp: 2 表示已应用 seccomp 过滤器。

使用 capsh --decode=00000000a80425fb 可以把它解码成人类可读形式。

总结——四类文件回答的问题

文件 回答的问题
ns/ 看到什么(PID、网络、挂载隔离)
cgroup 能使用多少资源,以及属于哪个容器
mountinfo 文件来自哪里(镜像层、卷)
status 能做什么(capability、seccomp)

使用这些文件可以回答的实际问题

学会读取 /proc 后,即使环境中没有专用工具,也能回答更多问题。下面列出四个常见问题。

“这两个容器共享网络吗?”——比较命名空间的 inode 编号。编号相同,就处于同一命名空间。

readlink /proc/$A/ns/net /proc/$B/ns/net
# net:[4026532567] 이 같으면 같은 네트워크

Sidecar 看不到主容器网络、临时容器看不到进程的问题,都能用这一行确认。

“内存限制真的生效了吗?”——在 cgroup v2 中沿路径读取文件。

cat /proc/$PID/cgroup                       # 0::/kubepods/.../<id>
cat /sys/fs/cgroup/kubepods/.../memory.max  # 한도
cat /sys/fs/cgroup/kubepods/.../memory.current
cat /sys/fs/cgroup/kubepods/.../memory.events  # oom_kill 횟수가 여기 있다

如果 memory.events 中的 oom_kill 不为 0,说明该容器已经至少被杀死过一次。查找 Pod 重启原因时,应首先查看这里。

“这个文件来自哪里?”——查看 mountinfo,就能知道卷实际挂载到了哪里,以及是否为只读。ConfigMap 未更新的问题(通过符号链接挂载的结构)也会在这里显现。

grep -E ' /etc/config | /data ' /proc/$PID/mountinfo

“这个进程能够做什么?”——把 status 中的 CapEff 解码成人类可读形式。

grep CapEff /proc/$PID/status
capsh --decode=00000000a80425fb

如果 CapEff0000000000000000,就表示已经丢弃全部权限,这正是我们期望的状态。反之,如果看到 0000003fffffffff,该容器实际上正在特权模式下运行。

从主机反向寻找容器也很常用。 在主机上发现高 CPU 进程时,它属于哪个 Pod,就写在 cgroup 路径中。

实际现场中的情况

下一项检查要关注什么

接下来的测验将检查 /proc 中的命名空间 inode、cgroup 路径与限制、capability 位掩码分别能证明什么。请区分前一实验读取到的内核值与文档中的容器配置后作答。