记一次Gitlab Runner卡顿的详情分析
- 该问题排查耗时数周,下面分享一下整体的排查过程,仅供大家参考。
问题的背景
-
最近
Gitlab Runner
服务偶尔接到用户反馈build
超时和卡顿。下面是某个用户提供的问题截图。比如build
超时: -
用户反馈卡顿,比如:
- 由于该问题具有普遍性,不同的用户在不同的
repo
进行ci
均会偶发性产生问题,于是着手做一次深入的排查。
故障机器的配置说明
- 机器为
KVM
虚拟机,配置如下。
1 | 系统版本:Debian9.7 |
故障的分析详情
第一次排查
-
在初次遇到这个问题结合用户反馈的截图,怀疑看
docker.sock
不响应。所以,当时尝试在事故发生现场,努力的strace
追踪docker.sock
的系统调用情况,下面是某次现场分析的截图,发现请求socket
确实会有卡住的现象。然而这一次过了以后,没有深入找到原因,因为现场没有保存。下面是故障时的strace
截图。
第二次排查
- 接着某天再此收到用户报障,这次又深入了一点,找到了系统层的
CPU
异常,且IO
使用率高。在这里看到cpu
的st
占用高,就怀疑是虚拟机的资源复用太严重,所以就让同事把这台机器迁移到了独占宿主机,目的是排除CPU的影响。其实这里忽略了io高的原因,当时被st高的现象给迷惑了。-
系统
top
分析截图
-
io
监控图数据
-
第三次排查
- 迁移了宿主以后,发现没过几天又接到用户的报障。由于已经排除了
CPU
的影响。思路就集中在了其他资源上,这次定位发现系统io
存在可疑,如下图:-
系统整体使用情况
-
iotop
定位到loop
内核线程io
高,其实内核线程的祖先是pid
为2的kthreadd
进程。
-
loop
内核线程确实有读发生,且在进程分布中读的量最大。
-
- 到了第三次排查,大概定位到了瓶颈为
loop
内核线程,但是loop
内核线程到底在做什么事情呢,这个问题值得思考,读到这里,您会有哪些思考呢?其实要知道内核线程具体的内部调用栈情况,需要使用到动态追踪工具,那业界优秀的动态追踪工具有哪些呢?下文就为你揭晓答案。
第四次排查
- 这一次排查使用
perf
工具保存了现场,并利用perf
保存的数据生成了火焰图,定位到热点函数是loop_queue_work
,其对应的内核源码定义如下
- 从
perf
火焰图可知,热点函数是loop_queue_work
。这里普及一下火焰图的查看技巧,火焰图一般是从下往上看,横轴表示采样数和采样比例,一个函数占用的横轴越宽就代表它的执行时间越长。纵轴表示调用栈,上下相邻的2个函数中,下面的函数是上面函数的父函数。
- 从热点函数名中可知,含有队列(queue)2个字,于是想到了磁盘调度算法,接着尝试调整虚拟机的磁盘调度算法。发现虚拟机使用了
Ceph RBD
磁盘,不支持修改磁盘调度算法。于是这里卡住了继续下一步的线索。
第五次排查
-
在这一次排查中,终于拨开了迷雾。在一次偶然的机会,有一个用户提供了一个和前几次不一样的报错截图,如下图:
-
排查到
gitlab runner
机器上的devmapper
存储驱动目录确实挺大,但是为啥镜像清理了以后,空间依旧没释放呢。于是网上查了一番,发现这个存储驱动已经不推荐使用了,有性能问题。关于Docker
如何选择存储驱动,请参考这里。
-
至于我们这里为什么有使用
devmapper
这个驱动,是因为在一次升级系统内核时做的变更导致。我们查看到线上docker
运行到存储驱动正是devmapper
。
-
这一次机智的用
perf
保存了现场,再次分析热点函数,看到和xfs
文件系统相关。
-
在2个月前升级系统内核时,由于在xfs文件系统下没有打开下面这个选项(
ftype=1
才代表打开)导致只能使用devmapper
驱动。从这里再联想到热点函数也是显示xfs文件系统相关,不得不考虑是存储驱动导致的性能问题了。
- 在没有打开这个选项(ftype=1)的情况下使用overlay存储驱动会报错如下:
1 | could not use snapshotter overlayfs in metadata plugin" error="/home/gitlab/var/lib/docker/containerd/daemon/io.containerd.snapshotter.v1.overlayfs does not support d_type. If the backing filesystem is xfs, please reformat with ftype=1 to enable d_type support |
-
而
Docker
有在源码里面也有定义使用的存储驱动优先级。
-
于是备份数据后使用
mkfs.xfs -n ftype=1 /dev/vdc1
命令重新格式化磁盘,打开ftype
选项。接着强制配置Docker
进程使用overlay2
的存储驱动,再次模拟用户触发ci
任务,观察发现loop
内核线程不会有io
产生。整个系统的io
也降低了。再更改回devmapper
以后,loop
内核线程又变高了,由此定位到了最终问题。
自测结果
- 下面是使用同一个
repo
,在同一个gitlab
站点测试使用devmapper
和overlay2
不同存储驱动的表现情况。-
使用
overlay2
存储驱动的io
数据
-
使用
iotop
看不到loop
内核线程有io情况,更不会排在首位。
-
使用
devmapper
存储驱动的io
数据
-
使用
iotop
查看到loop
内核线程io
消耗且排在了首位。
-
思路总结
- 从这一次的故障诊断分析来看,耗时较久,从一开始的现象到最终的分析,都是逐步进行的。故障发生的时刻往往短暂,所以想办法保存现场数据或者复现故障显得尤为重要。对于性能优化,基本是长期跟踪,动态调整,大部分的分析是从系统到进程或者线程,再到内核热点函数,再到操作系统原理。 最后期待本文能对你以后的故障排查提供帮助,知识在于分享,更希望你可以把此文分享给你的朋友。
资料参考
- 优秀的动态追踪工具参考:https://zhuanlan.zhihu.com/p/24124082
- 火焰图使用参考:https://github.com/brendangregg/FlameGraph
- perf工具使用参考:http://www.brendangregg.com/perf.html
推荐阅读
赞赏一下