Systemtap 揪出杀死 Pod 的真凶
安装
CentOS安装 systemtap:
yum install -y systemtap
默认没装 debuginfo
,我们需要装一下,添加软件源 /etc/yum.repos.d/CentOS-Debug.repo
:
[debuginfo]
name=CentOS-$releasever - DebugInfo
baseurl=http://debuginfo.centos.org/$releasever/$basearch/
gpgcheck=0
enabled=1
protect=1
priority=1
执行 stap-prep
(会安装 kernel-debuginfo
)
最后检查确保 kernel-debuginfo
和 kernel-devel
均已安装并且版本跟当前内核版本相同,如果有多个版本,就删除跟当前内核版本不同的包(通过uname -r
查看当前内核版本)。
重点检查是否有多个版本的 kernel-devel
:
$ rpm -qa | grep kernel-devel
kernel-devel-3.10.0-327.el7.x86_64
kernel-devel-3.10.0-514.26.2.el7.x86_64
kernel-devel-3.10.0-862.9.1.el7.x86_64
如果存在多个,保证只留跟当前内核版本相同的那个,假设当前内核版本是 3.10.0-862.9.1.el7.x86_64
,那么使用 rpm 删除多余的版本:
rpm -e kernel-devel-3.10.0-327.el7.x86_64 kernel-devel-3.10.0-514.26.2.el7.x86_64
使用 systemtap 揪出杀死pod的真凶
Pod 莫名其妙被杀死? 可以使用 systemtap 来监视进程的信号发送,原理是 systemtap 将脚本翻译成 C 代码然后调用 gcc 编译成 linux 内核模块,再通过 modprobe
加载到内核,根据脚本内容在内核做各种 hook,在这里我们就 hook 一下信号的发送,找出是谁 kill 掉了容器进程。
首先,找到被杀死的 pod 又自动重启的容器的当前 pid,describe 一下 pod:
$ kubectl describe pod
Name: lxcfs-demo
Namespace: default
Priority: 0
Node: docker-desktop/192.168.10.4
Start Time: Mon, 18 Jul 2022 10:28:16 +0800
Labels: <none>
Annotations: <none>
Status: Running
IP: 10.1.0.54
IPs:
IP: 10.1.0.54
Containers:
busybox:
Container ID: docker://d93975478041e56f137d44700f081d68415327cb2ccf51e20d1ff4d9e8e038f6
拿到容器 id 反查容器的主进程 pid:
$ docker inspect -f "{{.State.Pid}}" d93975478041e56f137d44700f081d68415327cb2ccf51e20d1ff4d9e8e038f6
97495
通过 Exit Code
可以看出容器上次退出的状态码,如果进程是被外界中断信号杀死的,退出状态码将在 129-255 之间,137 表示进程是被 SIGKILL 信号杀死的,但我们从这里并不能看出是被谁杀死的。
如果问题可以复现,我们可以使用下面的 systemtap 脚本来监视容器是被谁杀死的(保存为sg.stp
):
global target_pid = 97495
probe signal.send{
if (sig_pid == target_pid) {
printf("%s(%d) send %s to %s(%d)\n", execname(), pid(), sig_name, pid_name, sig_pid);
printf("parent of sender: %s(%d)\n", pexecname(), ppid())
printf("task_ancestry:%s\n", task_ancestry(pid2task(pid()), 1));
}
}
- 变量
pid
的值替换为查到的容器主进程 pid 运行脚本:
stap sg.stp
当容器进程被杀死时,脚本捕捉到事件,执行输出:
pkill(23549) send SIGKILL to server(97495)
parent of sender: bash(23495)
task_ancestry:swapper/0(0m0.000000000s)=>systemd(0m0.080000000s)=>kubelet(19491m2.579563677s)=>sh(33473m38.074571885s)=>bash(33473m38.077072025s)=>bash(33473m38.081028267s)=>bash(33475m4.817798337s)=>pkill(33475m5.202486630s)
通过观察 task_ancestry
可以看到杀死进程的所有父进程,在这里可以看到是 kubelet
杀死
「如果这篇文章对你有用,请随意打赏」