在管理 Kubernetes 集群的过程中,我们经常会遇到这样一种情况:在某台节点上发现某个进程资源占用量很高,却又不知道是哪个容器里的进程。有没有办法可以根据 PID 快速找到 Pod 名称呢?
假设现在有一个 Prometheus 进程的 PID 是 14338:
为了进一步挖掘信息,有两种思路,一种是挖掘 PID 对应的容器的信息,另一种是挖掘 PID 对应的 Pod 的信息。
1. Container ID
要获取容器的 ID,可以查看 PID 对应的 cgroup 信息:
1 2 3 4 5 6 7 8 9 10 11 12 13
| $ cat /proc/14338/cgroup
11:blkio:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 10:cpuset:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 9:freezer:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 8:hugetlb:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 7:perf_event:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 6:cpuacct,cpu:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 5:pids:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 4:devices:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 3:net_prio,net_cls:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 2:memory:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 1:name=systemd:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c
|
可以看到该进程对应的容器 ID 为 d6f24b62…,可以再优化一下上面的命令,直接获取容器 ID:
1 2 3
| $ CID=$(cat /proc/14338/cgroup | awk -F '/' '{print $5}') $ echo ${CID:0:8} d6f24b62
|
最后一步根据容器 ID 获取 Pod 名称,如果你的容器运行时是 containerd 或 crio,可以使用 crictl 来获取容器信息:
1 2 3 4 5 6 7 8 9
| # 使用 Go template 或 jq 都能获取 Pod 名称,看个人喜好。
# Go Template $ crictl inspect -o go-template --template='{{index .status.labels "io.kubernetes.pod.name"}}' d6f24b62 prometheus-k8s-0
# jq $ crictl inspect d6f24b62|jq '.status.labels["io.kubernetes.pod.name"]' "prometheus-k8s-0"
|
如果你的容器运行时是 Docker,可以使用命令行工具 docker 来获取,方法和上面类似。
1 2
| $ docker inspect d6f24b62 | jq '.[0].Config.Labels."io.kubernetes.pod.name"' "prometheus-k8s-0"
|
一种特殊情况的处理,上面的方法适用于大多数场景。但有可能你的 cat /proc/14338/cgroup
输出的结果是这样的:
1 2 3 4 5 6
| $ cat /proc/14338/cgroup
11:blkio:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/docker-d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 10:cpuset:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/docker-d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 9:freezer:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/docker-d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c ......
|
这时你就需要将上面取容器 ID 的方法稍做修改:
1 2 3 4
| $ cat /proc/d6f24b62/cgroup | awk -F '/' '{print $5}'|sed 's/docker-//g' 11:blkio:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 10:cpuset:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c 9:freezer:/kubepods/burstable/pod8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/d6f24b62ea28e9e67f7bc06f98de083cc49454f353389cd396f5d3ac6448f19c
|
2. Pod UID
下面来看看第二种方法,先根据 PID 直接获取 Pod UID:
1 2
| $ cat /proc/14338/mountinfo | grep "etc-hosts" | awk -F / {'print $6'} 8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1
|
然后根据 Pod UID 获取 Pod 名称:
1 2
| $ crictl ps -o json | jq '.[][].labels | select (.["io.kubernetes.pod.uid"] == "8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1") | .["io.kubernetes.pod.name"]'|uniq "prometheus-k8s-0"
|
3. 整合
方法是有了,怎么才能将所有的步骤合并成一个步骤,一步到位获取 Pod 名称呢?可以在 ~/.bashrc 中添加一个 shell 函数,选择上面的方法 1,并使用 go template 来格式化(你也可以使用上面提到的其他方法,但需要安装 jq):
1 2 3 4 5
| podinfo() { CID=$(cat /proc/$1/cgroup | awk -F '/' '{print $5}') CID=$(echo ${CID:0:8}) crictl inspect -o go-template --template='{{index .status.labels "io.kubernetes.pod.name"}}' $CID }
|
执行下面的命令使修改立即生效:
然后就可以使用该函数来获取 Pod 名称啦:
1 2
| $ podinfo 14338 prometheus-k8s-0
|
4. 举一反三
这个思路也可以用来解决其他问题,大家要学会举一反三,我举个例子。Kubernetes 中的很多组件都是通过 HTTPS 协议来暴露指标,比如 kubelet,那么如何使用 API 来访问这些指标呢?
先选取一个容器,比如 prometheus,找到它的 PID:
1 2 3 4 5
| $ ps -ef|grep "/bin/prometheus"
1000 14338 14246 4 7月10 ? 04:29:02 /bin/prometheus --web.console.templates=/etc/prometheus/consoles --web.console.libraries=/etc/prometheus/console_libraries --config.file=/etc/prometheus/config_out/prometheus.env.yaml --storage.tsdb.path=/prometheus --storage.tsdb.retention.time=24h --web.enable-lifecycle --storage.tsdb.no-lockfile --web.route-prefix=/ 1000 14402 14246 0 7月10 ? 00:00:10 /bin/prometheus-config-reloader --log-format=logfmt --reload-url=http://localhost:9090/-/reload --config-file=/etc/prometheus/config/prometheus.yaml.gz --config-envsubst-file=/etc/prometheus/config_out/prometheus.env.yaml root 15956 555 0 18:19 pts/0 00:00:00 grep --color=auto /bin/prometheus
|
根据 PID 找到 Pod UID:
1 2
| $ cat /proc/14338/mountinfo | grep "etc-hosts" | awk -F / {'print $6'} 8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1
|
根据 Pod UID 找到 Service Account 的 token 挂载目录:
1 2 3 4 5 6
| $ ll /var/lib/kubelet/pods/8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/volumes/kubernetes.io~secret/prometheus-k8s-token-p7bgb/
总用量 0 lrwxrwxrwx 1 root root 13 7月 10 21:24 ca.crt -> ..data/ca.crt lrwxrwxrwx 1 root root 16 7月 10 21:24 namespace -> ..data/namespace lrwxrwxrwx 1 root root 12 7月 10 21:24 token -> ..data/token
|
获取 token 信息:
1
| $ export TOKEN=$(cat /var/lib/kubelet/pods/8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/volumes/kubernetes.io~secret/prometheus-k8s-token-p7bgb/token)
|
通过 curl 直接访问指标:
1
| $ curl -s -H "Authorization: Bearer $TOKEN" --cacert /var/lib/kubelet/pods/8e018a8e-4aaa-4ac6-986a-1a5133a4bcf1/volumes/kubernetes.io~secret/prometheus-k8s-token-p7bgb/ca.crt --insecure https://127.0.0.1:10250/metrics/cadvisor
|
当然,如果你能找到集群管理员的证书、密钥和 CA 证书,也可以直接使用它们来访问,我就不展开说了。
延伸阅读
上面的神技是不是很好用呢?一步步执行,你肯定觉得还是有点麻烦。最后在给大家提供两个另一位大神 YP 写的 Shell 脚本,使用它可以帮助你快速找到 Pid 和 Pod 的对应关系,助你定位问题哟!
根据 Pid 获取 K8s Pod 名称
1 2
| # 通过 Pid 获取 Pod 名称 $ ./pod_name_info.sh Pid
|
注:该 Shell 脚本需要服务器上安装 jq 命令,因为脚本依赖 jq 来处理 json 格式。
根据 Pod 名称获取 Pid
1 2
| # 通过 Pod名称 获取 Pid $ ./pod_pid_info.sh Pod名称
|
这么棒的神技,难道你不想掌握吗?欲下载此脚本,只需在公众号对话框内回复 「k8s-pid-podname
」,即可获取下载地址。
参考文档
- https://mp.weixin.qq.com/s/77v46oIraV22acC8eYqC5A
- https://mp.weixin.qq.com/s/HF5rzr5fULiMWq1NPe780g