使用 Systemtap 定位疑难杂症
安装
Ubuntu
安装 systemtap:1 | apt install -y systemtap |
运行 <font style="color:rgb(28, 30, 33);">stap-prep</font>
检查还有什么需要安装:
1 | $ stap-prep |
提示需要 dbgsym 包但当前已有软件源中并不包含,需要使用第三方软件源安装,下面是 dbgsym 安装方法(参考官方wiki: https://wiki.ubuntu.com/Kernel/Systemtap):
1 | sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C8CAB6595FDFF622 |
配置好源后再运行下 <font style="color:rgb(28, 30, 33);">stap-prep</font>
:
1 | $ stap-prep |
提示需要装这两个包,我们安装一下:
1 | apt install -y linux-image-4.4.0-104-generic-dbgsym |
CentOS
安装 systemtap:1 | yum install -y systemtap |
默认没装 <font style="color:rgb(28, 30, 33);">debuginfo</font>
,我们需要装一下,添加软件源 <font style="color:rgb(28, 30, 33);">/etc/yum.repos.d/CentOS-Debug.repo</font>
:
1 | [debuginfo] |
执行 <font style="color:rgb(28, 30, 33);">stap-prep</font>
(会安装 <font style="color:rgb(28, 30, 33);">kernel-debuginfo</font>
)
最后检查确保 <font style="color:rgb(28, 30, 33);">kernel-debuginfo</font>
和 <font style="color:rgb(28, 30, 33);">kernel-devel</font>
均已安装并且版本跟当前内核版本相同,如果有多个版本,就删除跟当前内核版本不同的包(通过<font style="color:rgb(28, 30, 33);">uname -r</font>
查看当前内核版本)。
重点检查是否有多个版本的 <font style="color:rgb(28, 30, 33);">kernel-devel</font>
:
1 | $ rpm -qa | grep kernel-devel |
如果存在多个,保证只留跟当前内核版本相同的那个,假设当前内核版本是 <font style="color:rgb(28, 30, 33);">3.10.0-862.9.1.el7.x86_64</font>
,那么使用 rpm 删除多余的版本:
1 | rpm -e kernel-devel-3.10.0-327.el7.x86_64 kernel-devel-3.10.0-514.26.2.el7.x86_64 |
使用 systemtap 揪出杀死容器的真凶
Pod 莫名其妙被杀死? 可以使用 systemtap 来监视进程的信号发送,原理是 systemtap 将脚本翻译成 C 代码然后调用 gcc 编译成 linux 内核模块,再通过 `modprobe` 加载到内核,根据脚本内容在内核做各种 hook,在这里我们就 hook 一下信号的发送,找出是谁 kill 掉了容器进程。首先,找到被杀死的 pod 又自动重启的容器的当前 pid,describe 一下 pod:
1 | ...... |
拿到容器 id 反查容器的主进程 pid:
1 | $ docker inspect -f "{{.State.Pid}}" 5fb8adf9ee62afc6d3f6f3d9590041818750b392dff015d7091eaaf99cf1c945 |
通过 <font style="color:rgb(28, 30, 33);">Exit Code</font>
可以看出容器上次退出的状态码,如果进程是被外界中断信号杀死的,退出状态码将在 129-255 之间,137 表示进程是被 SIGKILL 信号杀死的,但我们从这里并不能看出是被谁杀死的。
如果问题可以复现,我们可以使用下面的 systemtap 脚本来监视容器是被谁杀死的(保存为<font style="color:rgb(28, 30, 33);">sg.stp</font>
):
1 | global target_pid = 7942 |
- 变量
<font style="color:rgb(28, 30, 33);">pid</font>
的值替换为查到的容器主进程 pid
运行脚本:
1 | stap sg.stp |
当容器进程被杀死时,脚本捕捉到事件,执行输出:
1 | pkill(23549) send SIGKILL to server(7942) |
通过观察 <font style="color:rgb(28, 30, 33);">task_ancestry</font>
可以看到杀死进程的所有父进程,在这里可以看到有个叫 <font style="color:rgb(28, 30, 33);">vGhyM0</font>
的奇怪进程名,通常是中了木马,需要安全专家介入继续排查。