Cra*_*ger 4 mount namespaces linux-kernel docker kubernetes
我正在测试 k8s 调试功能,包括调试 pod 和临时容器,但我无法弄清楚如何将“目标”pod 的文件系统正确映射到调试容器中。
我想使用递归绑定挂载 * 链接两个不相交的挂载命名空间,以便容器 A 将容器 B 的根视为容器 B 的根,/containerB反之亦然。包括所有卷和其他安装。
目标是让目标 pod 的完整文件系统树,包括映射到调试容器的子目录(例如 )的卷和其他挂载/run/target。如果目标容器挂载持久卷,则应映射这些挂载点,因此,例如,如果目标容器具有,/data则调试容器应具有已安装的/run/target/data.
或者,可以将调试容器文件系统树“注入”到目标容器中,因此可以在调试容器/run/debug时公开可用的调试容器根。nsenter包括它的像 procfs 这样的挂载,所以它的功能齐全。
我希望能够例如调试容器提供的gdb -p $target_pid位置。为此,必须能够从目标容器中找到进程可执行文件。gdbgdb
我探索了一些解决方法。但我真正想做的是将mount --rbind目标容器 FS 树转移到来宾上,反之亦然。给定一个定制的特权调试容器,例如:
apiVersion: v1
kind: Pod
metadata:
name: debugcontainer
namespace: default
spec:
nodeName: TARGET_NODE_NAME_HERE
enableServiceLinks: true
hostIPC: true
hostNetwork: true
hostPID: true
restartPolicy: Never
containers:
- image: DIAG_CONTAINER_IMAGE_HERE # you can experiment using something like ubuntu:20.04
name: debugger
stdin: true
tty: true
volumeMounts:
- mountPath: /target
name: target
#- mountPath: /host
# mountPropagation: None
# name: host-root
securityContext:
privileged: true
runAsGroup: 0
runAsUser: 0
volumes:
- emptyDir: {}
name: target
#- hostPath:
# path: "/"
# type: ""
# name: host-root
Run Code Online (Sandbox Code Playgroud)
当调试容器启动到与目标容器相同的节点时,我可以:
psstrace,gdb因为特权调试容器具有CAP_SYS_PTRACEnsenter -t $some_target_container_pid --all“成为”目标容器中的一个过程,就像我所做的那样kubectl exec。我无法再“查看”或访问调试容器文件/工具。nsenter -t $some_target_container_pid -m --root=/ --wd=/进入目标进程的挂载命名空间,但保留调试容器的权限。我无法再“查看”或访问调试容器文件/工具。但是我不能:
gdb找不到正在调试的可执行文件有没有公认的方法可以做到这一点?
这并不完全是 k8s 特有的:同样的问题也适用于 Docker、containerdrunc等。
您可能希望通过使用withmount --rbind经由主机容器命名空间将调试容器“注入”到目标容器中来实现这一点。但安装容器根映像,将安装传播设置为私有,然后安装内部卷。因此,主机挂载命名空间看不到容器根映像内进行的挂载,并且容器中的进程看不到容器第一个进程启动后主机添加的新挂载。有关详细信息,请参阅https://man7.org/linux/man-pages/man7/mount_namespaces.7.html。hostPath volumemountPropagation: Bidirectionalcontainerd
我尝试过使用nsenter“跨”挂载命名空间,但我无法让绑定挂载工作。例如在调试容器中我可以
nsenter -t $some_target_container_pid --root=/ -m /bin/bash
Run Code Online (Sandbox Code Playgroud)
这给了我一个 shell,其中.(CWD) 是调试容器 rootfs,并且/是目标容器rootfs。但我似乎无法绑定安装它们:
$ mkdir /run/debug
$ mount --rbind . /run/debug
mount: /run/debug: wrong fs type, bad option, bad superblock on ., missing codepage or helper program, or other error.
Run Code Online (Sandbox Code Playgroud)
如果我使用nsenter --wd=/without --root, 并尝试 to ,也会发生同样的情况mount --rbind / ./run/debug。
我尝试unshare -m首先创建一个新的内部安装命名空间。我mount --make-rprivate /在绑定安装之前尝试过调试容器树。同样的交易。
我不明白为什么: dmesg 中没有任何内容,并且错误非常普遍。我猜这是由于不相交的根和/或不相交的安装命名空间造成的。这似乎不是由于内核对绑定安装循环的保护。而且我使用的是递归绑定,所以这不应该是由于对 Linux 用户命名空间中的安装树转义的保护。
--rbind如果我有办法通过挂载 ID 来mount --bind替代FS树,如图所示/proc/$target_pid/mountinfo。然后我可以将目标 pid 中的所有挂载克隆到调试容器的挂载命名空间中。但我无法mount --bind使用正常的绝对路径,因为目标和调试容器的挂载命名空间是不相交的,并且两者都具有具有私有传播的挂载子树。
我尝试过使用目标进程的/proc/$pid/ns/mnt安装命名空间,因为我已经看到了使用它进行绑定安装的参考。但在我的内核 5.16 上,它是一棵假符号链接树,而不是 fs 树:
$ readlink /proc/self/ns/mnt
mnt:[4026531840]
$ ls /proc/self/ns/mnt/
ls: cannot access '/proc/self/ns/mnt/': Not a directory
Run Code Online (Sandbox Code Playgroud)
目前我最接近的解决方法是nsenter对工作目录进行黑客攻击。这提供了非常有限的工具注入到目标容器中。其中 pid 1055 是目标容器中的 pid:
# nsenter -t 1055 -p -m --wd=/ /bin/bash
shell-init: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
# ls /
...target container rootfs contents here...
# ls .
...debug container rootfs here...
# ls ..
...debug container rootfs here too because . is a root...
# pwd
pwd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
# ls usr/bin/gdb
usr/bin/gdb
# ls /usr/bin/gdb
ls: cannot access '/usr/bin/gdb': No such file or directory
Run Code Online (Sandbox Code Playgroud)
但我无法像我想要的那样在同一个 nsenter 会话中绑定安装:
# mkdir /run/debug
# mount --rbind . /run/debug
mount: /run/debug: wrong fs type, bad option, bad superblock on ., missing codepage or helper program, or other error.
Run Code Online (Sandbox Code Playgroud)
提示?
参考链接:
可以通过 建立到目标容器上下文的符号链接/proc/${target_container_pid}/root。
ln -s /proc/$pid/root /target
Run Code Online (Sandbox Code Playgroud)
/proc/$pid/root 看起来像一个符号链接。如果你readlink /proc/$pid/root它指向/. 但它是目标进程的根,如果您在内核 vfs 层中取消引用它,您将看到目标进程的根。如果您解析用户空间中的符号链接,您将看到执行取消引用的处理的根源。
我无法绑定挂载树 -mount -o bind /proc/$pid/root/ /target将进程本身的 rootfs 绑定mount到/target,而不是目标进程的 rootfs 。但这并不重要,因为符号链接就足够了。
(我会为文档编写一个补丁kubectl debug,但我无法让我的组织同意即使是简单的文档补丁所需的强制性 CLA...)
| 归档时间: |
|
| 查看次数: |
1137 次 |
| 最近记录: |