基于containerd CRI如何查看容器的文件内容?

CRI:containerd

安装 crictl:https://github.com/kubernetes-sigs/cri-tools/releases

exec进入容器中查看

使用 crictl crictl exec -it <container-id> <shell-in-container> 需要在 Pod 所在的 Node 上执行。

1
2
3
4
5
6
7
8
ubuntu@work-1:~$ sudo crictl ps
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID POD
64aad8b603123 985a52bfdfe21 13 minutes ago Running simple-http-server 0 0c2daa092abd3 shs-5698fcd598-dpxlg

ubuntu@work-1:~$ sudo crictl exec -it 64aad8b603123 bash
bash-5.1# ls -l
total 6352
-rwxr-xr-x 1 root root 6503679 Oct 28 05:48 simple-http-server

使用 kubectl kubectl exec -it <pod-name> -- bash

1
2
3
4
5
# ubuntu @ master in ~ [23:16:17]
$ kubectl exec -it shs-5698fcd598-dpxlg -- bash
bash-5.1# ls -l
total 6352
-rwxr-xr-x 1 root root 6503679 Oct 28 05:48 simple-http-server

前提

  1. 容器中需要存在 shell 或 ls 等命令;
  2. 如果在容器中想做一些操作,它还需要存在你想使用的命令。

使用 nsenter

需要在 Pod 所在 Node 上执行

查找目标 Pod 进程 id。有两种方式

  1. 直接 ps -ef ,然后用 grep 去过滤出来 Pod 的启动命令。
1
2
3
ubuntu@work-1:~$ ps -ef | grep simple-http-server | grep -v grep
root 7630 7510 0 23:00 ? 00:00:00 /app/simple-http-server
root 8158 8103 0 23:01 ? 00:00:00 /app/simple-http-server

如果分得清楚是哪个 Pod 的进程,直接那对应进程的 pid 即可;如果分不清,那继续看下一种方法。

  1. 先用 kubectl get pod <target-pod> -oyaml | grep containerID 找目标 Pod 中业务容器的 id,然后再用 crictl inspect <container-id> 获取到容器进程的pid
1
2
3
4
5
6
7
$ kubectl get pod shs-5698fcd598-sjq5p -oyaml | grep containerID
- containerID: containerd://602515a3a5fb6e36902844a0654d82841bb565d57df9d4b0e6ecf024947b0407

$ sudo crictl inspect 602515a3a5fb6e36902844a0654d82841bb565d57df9d4b0e6ecf024947b0407 | grep pid
"pid": 7630,
"pid": 1
"type": "pid"
  1. 使用 nsenter 进入容器进程的各个 namespace 中
1
2
3
4
ubuntu@work-1:~$ sudo nsenter -t 7630 -a bash
bash-5.1# ls -lh /app
total 6M
-rwxr-xr-x 1 root root 6.2M Oct 28 05:48 simple-http-server

此种方法仍然需要在容器中有 shell 程序或者相应命令。但也可以单独只指定某个 namespace。

例如,对 CoreDNS 抓包。CoreDNS 使用了 Google distroless/static 作为基础镜像的容器,需要对其进行网卡抓包,但容器中又没有 tcpdump 命令时,便可以只进入 net namespace,同时使用宿主机的 shell 和 tcpdump 程序,对其网卡进行抓包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ kubectl -n kube-system get pod -owide | grep coredns
coredns-84b58f6b4-qd4vp 1/1 Running 11 (57m ago) 30d 172.20.251.235 192.168.64.5

$ kubectl -n kube-system exec -it coredns-84b58f6b4-qd4vp -- /bin/zsh
error: Internal error occurred: error executing command in container: failed to exec in container: failed to start exec "...": OCI runtime exec failed: exec failed: unable to start container process: exec: "/bin/zsh": stat /bin/zsh: no such file or directory: unknown

$ ps -ef | grep -i coredns | grep -v grep
root 1738 1618 0 22:45 ? 00:00:11 /coredns -conf /etc/coredns/Corefile

$ sudo nsenter -t 1738 -n /bin/zsh
work-2# ip address show dev eth0
3: eth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 66:b9:35:7b:77:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.20.251.235/32 brd 172.20.251.235 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::64b9:35ff:fe7b:7702/64 scope link
valid_lft forever preferred_lft forever
work-2# tcpdump -i eth0
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes

另外 nsenter 还可以单独进入其他的 namespace,分别如下:

1
2
3
4
5
6
7
8
9
10
11
-a, --all              enter all namespaces
-m, --mount[=<file>] enter mount namespace
-u, --uts[=<file>] enter UTS namespace (hostname etc)
-i, --ipc[=<file>] enter System V IPC namespace
-n, --net[=<file>] enter network namespace
-p, --pid[=<file>] enter pid namespace
-C, --cgroup[=<file>] enter cgroup namespace
-U, --user[=<file>] enter user namespace
-T, --time[=<file>] enter time namespace
-S, --setuid <uid> set uid in entered namespace
-G, --setgid <gid> set gid in entered namespace

单独进入某个 namespace 或多个 namespace 时,需要注意其他的 namespace 仍然是宿主机的默认 namespace,可能会产生一些有意思的现象,可能是个莫名奇妙的问题,需要适当拿捏这种情况。

在宿主机的文件系统中查找

先找到运行中的容器 ID

1
2
3
$ kubectl -n kube-system get pod coredns-84b58f6b4-qd4vp -oyaml | grep containerID
- containerID: containerd://2bf4db84e5ec38f4cd2c7aebd144258bffd9e3883fcd8741d1bc535a7438cc7d
containerID: containerd://833f8abbb616895dc8bd8fb15c2153adddd0dcbef8a51a540a53ed2f663ac3ee

这里有两个,其中下面那个是该 Pod 的上一个容器的 ID,处于异常状态,第一个才是处于 Running 状态的容器。

到该 Pod 所在的 Node 节点上,用 df -h | grep <container-id> 的方式,来获取挂载信息

1
2
# df -h | grep 2bf4db84e5ec38f4cd2c7aebd144258bffd9e3883fcd8741d1bc535a7438cc7d
overlay 20G 4.7G 15G 25% /run/containerd/io.containerd.runtime.v2.task/k8s.io/2bf4db84e5ec38f4cd2c7aebd144258bffd9e3883fcd8741d1bc535a7438cc7d/rootf

进入 /run/containerd/io.containerd.runtime.v2.task/k8s.io/<container-id> 目录,可以看到如下信息:

1
2
3
4
5
6
7
8
9
10
11
12
# ls -l
total 36
-rw-r--r-- 1 root root 89 Jan 5 22:45 address
-rw-r--r-- 1 root root 11554 Jan 5 22:45 config.json
-rw-r--r-- 1 root root 4 Jan 5 22:45 init.pid
prwx------ 1 root root 0 Jan 5 22:45 log
-rw-r--r-- 1 root root 170 Jan 5 23:45 log.json
-rw------- 1 root root 23 Jan 5 22:45 options.json
drwxr-xr-x 1 root root 4096 Jan 5 22:45 rootfs
-rw------- 1 root root 0 Jan 5 22:45 runtime
-rw------- 1 root root 37 Jan 5 22:45 shim-binary-path
lrwxrwxrwx 1 root root 121 Jan 5 22:45 work -> /var/lib/containerd/io.containerd.runtime.v2.task/k8s.io/2bf4db84e5ec38f4cd2c7aebd144258bffd9e3883fcd8741d1bc535a7438cc7d

rootfs 目录便是容器的目录,但是内容不是挂载完成之后的。

1
2
3
4
5
# ls rootfs
coredns dev etc proc sys var

# cat rootfs/etc/resolv.conf
# 无输出

也可以用 /proc/<pid>/mounts 来查看 overlay 的挂载详情(即第一条根目录/的挂载信息)

1
2
3
4
5
6
7
8
9
10
11
# cat /proc/1738/mounts
overlay / overlay ro,relatime,lowerdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/20/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/19/fs,upperdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/926/fs,workdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/926/work 0 0
...
/dev/vda1 /etc/coredns ext4 ro,relatime,discard,errors=remount-ro 0 0
/dev/vda1 /etc/hosts ext4 rw,relatime,discard,errors=remount-ro 0 0
/dev/vda1 /dev/termination-log ext4 rw,relatime,discard,errors=remount-ro 0 0
/dev/vda1 /etc/hostname ext4 ro,relatime,discard,errors=remount-ro 0 0
/dev/vda1 /etc/resolv.conf ext4 ro,relatime,discard,errors=remount-ro 0 0
shm /dev/shm tmpfs rw,nosuid,nodev,noexec,relatime,size=65536k,inode64 0 0
tmpfs /var/run/secrets/kubernetes.io/serviceaccount tmpfs ro,relatime,size=307200k,inode64 0 0
...

通过 /proc/<pid>/root 来查看

先获取到目标 Pod 中容器的进程 pid,然后到 Pod 所在的 Node 上,直接 cd & ls & cat 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
root@work-2:/home/ubuntu# cd /proc/1738/root
root@work-2:/proc/1738/root# cat etc/coredns/Corefile
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
reload
loadbalance
}

Reference

基于containerd CRI如何查看容器的文件内容?

https://eucham.me/2023/01/06/f1a244286fc0.html

作者

遇寻

发布于

2023-01-06

更新于

2023-01-06

许可协议

评论