用眼睛确认命名空间
本实验在真正的 VM 中运行
这个环境不是 Pod,而是由 KubeVirt 启动的虚拟机。Linux 内核独立运行,systemd 真正管理服务,docker 也不是模拟工具,而是真正的 Docker 引擎。通过 docker run 启动的容器会成为实际进程,docker exec 和 docker logs 也都能照常工作。
过去,这个实验在 Pod 中运行。由于该环境放弃了所有内核权限,启动容器的步骤无法进行,因此只能学习直接解开镜像归档的变通方法。现在不再需要绕路了。
有两点需要了解。
- **首次启动大约需要 1 分钟。**因为 VM 需要启动并安装 Docker,比 Pod 实验(通常 40 秒)更慢。
- **没有浏览器预览。**进入 VM 的连接只开放评分端口。如果启动了 Web 服务器,请在 VM 内使用
curl检查。
目标
亲自测量七类命名空间中的 pid、uts、mnt、net、user 五类,并通过 inode 编号证明,容器不是“箱子”,而是视野受限的普通进程。最后,只使用 Docker 重现两个容器共享网络命名空间的 Kubernetes Pod 结构。
为什么重要
容器故障排查之所以卡住,大多是因为不知道“这个资源是否被隔离”。端口为何冲突、容器内看到的文件在主机上的什么位置、Sidecar 为何通过 localhost 连接应用——答案都归结为一句话:“该资源属于哪个命名空间?”命名空间具有 inode 编号,并可通过 /proc/<pid>/ns/* 读取,因此这个问题可以通过测量而非猜测来回答。编号相同就是同一个命名空间,编号不同就是不同的命名空间,仅此而已。
步骤
- 创建
/root/int1目录,在主机上将/proc/self/ns/pid链接所指向的值保存到/root/int1/host-pid-ns.txt。文件内容应只有一行pid:[숫자]。 - 使用
alpine:3.20镜像创建名为dk-ns的容器,添加--hostname labhub-uts,以sleep infinity在后台运行;将该容器内部读取的/proc/self/ns/pid值保存到/root/int1/ctr-pid-ns.txt。该值必须与第 1 步不同。 - 在
dk-ns内部以显示 PID 列的格式导出进程列表,保存到/root/int1/pid1.txt(docker exec dk-ns ps -o pid,comm)。PID 1 应为sleep——即启动容器时给出的命令。重点在于它既不是 systemd,也不是 init。直接在这台 VM 中运行ps -e时,PID 1 是 systemd,可用于对照。 - 将
dk-ns的主机名保存到/root/int1/uts.txt。内容应只有一行labhub-uts。 - 将此环境的
/etc/os-release和 alpine 镜像中的/etc/os-release追加到同一个文件/root/int1/mnt-proof.txt中。文件内必须同时出现主机侧的ubuntu和容器侧的alpine。 将docker run --rm alpine:3.20 cat /etc/os-release的结果与这台 VM 中同一文件的内容追加到一个文件即可。也请一并输出uname -r——**内核相同,只有文件树不同。**挂载命名空间所做的,归根结底只是把进程的/换成另一棵树,这两行便体现了这一事实。 - 将在
dk-ns内读取的/proc/net/dev保存到/root/int1/ctr-net.txt。其中必须有lo:行,而且内容必须与主机上的同一文件不同——接口配置和收发统计在每个命名空间中分别管理。 - 在后台启动一个加入
dk-ns网络命名空间的dk-sidecar容器;将在dk-ns内读取的/proc/self/ns/net保存到/root/int1/ns-a.txt,将在dk-sidecar内读取的同一值保存到/root/int1/ns-b.txt。两个值都必须采用net:[숫자]格式,且彼此相同。 - 将主机的
/proc/self/uid_map保存到/root/int1/host-uidmap.txt,将在dk-ns内的/proc/self/uid_map保存到/root/int1/ctr-uidmap.txt。两份映射内容必须不同。
参考
- 命名空间标识符需要像
readlink /proc/self/ns/pid一样跟随链接才能得到,无法使用cat读取。 - 在容器内读取内容时,请使用
docker exec <이름> <명령>,并将其输出重定向到主机侧路径。 - 加入其他容器网络命名空间的选项是
--network container:<이름>。 - 追加使用
>>,覆盖写入使用>。如果在第 5 步混淆两者,前面的内容会消失。 - 常见错误 1:如果在主机 Shell 中执行第 2、6、8 步,保存的都会是主机值,并因“相同”而失败。
- 常见错误 2:如果使用
--rm启动容器,下一步中dk-ns会消失,导致评分失败。 - 常见错误 3:第 4 步中,
hostname输出除换行外不得混入其他字符。只能留下labhub-uts。
读取主机的 PID 命名空间标识符
创建 /root/int1 目录,在主机上将 /proc/self/ns/pid 链接所指向的值保存到 /root/int1/host-pid-ns.txt。文件内容应只有一行 pid:[숫자]。
命名空间标识符通过 /proc/self/ns/ 下的符号链接公开。链接指向的字符串本身就是答案,因此不能用 cat,而要使用跟随链接的命令。文件中只能留下一行 pid:[숫자]。
与容器的 PID 命名空间比较
使用 alpine:3.20 镜像创建名为 dk-ns 的容器,添加 --hostname labhub-uts,以 sleep infinity 在后台运行;将该容器内部读取的 /proc/self/ns/pid 值保存到 /root/int1/ctr-pid-ns.txt。该值必须与第 1 步不同。
必须在容器内部读取同一路径,才能得到不同的编号。如果在主机 Shell 中读取,就会得到与第 1 步完全相同的值并导致失败。后续步骤还要继续使用该容器,因此请用不会立即退出的命令启动,使其持续运行。
确认容器内的 PID 1
在 dk-ns 内部以显示 PID 列的格式导出进程列表,保存到 /root/int1/pid1.txt(docker exec dk-ns ps -o pid,comm)。PID 1 应为 sleep——即启动容器时给出的命令。重点在于它既不是 systemd,也不是 init。直接在这台 VM 中运行 ps -e 时,PID 1 是 systemd,可用于对照。
导出进程列表时必须同时显示 PID 列。容器的主命令就是 1 号进程。如果在主机上导出,1 号会是另一个进程。
由 UTS 命名空间隔离的主机名
将 dk-ns 的主机名保存到 /root/int1/uts.txt。内容应只有一行 labhub-uts。
此步骤不是修改主机名,而是确认创建容器时指定的主机名借助 UTS 命名空间与主机彼此独立。请在容器内查询主机名,并只保存该值。
挂载命名空间——相同内核,不同文件树
将此环境的 /etc/os-release 和 alpine 镜像中的 /etc/os-release 追加到同一个文件 /root/int1/mnt-proof.txt 中。文件内必须同时出现主机侧的 ubuntu 和容器侧的 alpine。
将 docker run --rm alpine:3.20 cat /etc/os-release 的结果与这台 VM 中同一文件的内容追加到一个文件即可。也请一并输出 uname -r——**内核相同,只有文件树不同。**挂载命名空间所做的,归根结底只是把进程的 / 换成另一棵树,这两行便体现了这一事实。
只保存一方就无法比较。请将主机读取的内容与容器内读取的内容追加到同一个文件。追加时不要覆盖写入,应使用追加重定向。
网络命名空间的接口列表
将在 dk-ns 内读取的 /proc/net/dev 保存到 /root/int1/ctr-net.txt。其中必须有 lo: 行,而且内容必须与主机上的同一文件不同——接口配置和收发统计在每个命名空间中分别管理。
接口列表也可以通过 /proc/net/dev 读取(在此环境中比 ip/ifconfig 更可靠)。从容器内读取时,应只能看到 lo 和容器接口等少数条目,行数应少于主机。
让两个容器共享命名空间
在后台启动一个加入 dk-ns 网络命名空间的 dk-sidecar 容器;将在 dk-ns 内读取的 /proc/self/ns/net 保存到 /root/int1/ns-a.txt,将在 dk-sidecar 内读取的同一值保存到 /root/int1/ns-b.txt。两个值都必须采用 net:[숫자] 格式,且彼此相同。
有一个选项可以让容器加入其他容器的命名空间,而不是创建新的网络命名空间。这与 Kubernetes Pod 中 pause 容器所做的事情相同。如果加入成功,两个容器的 net inode 编号必须完全相同。
用户命名空间的 UID 映射
将主机的 /proc/self/uid_map 保存到 /root/int1/host-uidmap.txt,将在 dk-ns 内的 /proc/self/uid_map 保存到 /root/int1/ctr-uidmap.txt。两份映射内容必须不同。
/proc/self/uid_map 由 컨테이너안UID 호스트UID 개수 三列组成。此环境采用 rootless 模式,因此主机侧 Shell 与容器内部的映射会不同——请思考它为何与阅读材料中的实测表(Docker 默认设置下 user 命名空间相同)不同。