kubernetes集群收到一条,kubelet的cpu使用率较高的告警,过会就恢复了。然后又告警,这样的情况反反复复发生。
先来一波常规套路,登录上服务器,执行如下操作:
## 找到进程的pid ps -ef|grep kubelet ## 查看该进程的状态 top -p xxx ## 每隔1秒查看进程状态,总共10次的数据 pidstat 1 10
最终得到如下结果数据:
发现kubelet的cpu使用情况主要使用在用户态。那我们就看看这个kubelet到底是什么情况?
参考网上的诊断文档(https://www.cnblogs.com/zerchin/p/kubernetes.html)
我进行了如下操作:
## 查看详情 strace -cp 7799
发现futex这个函数占用的时间比较多,同时还发现了另外一个问题epoll_ctl的错误数也很多。咱们还是先关注时间占用的问题吧,跟此次cpu使用率相关性更高。错误数的问题,我们后面再看。
继续执行
## 查看明细 strace -tt -p <pid>
发现存在很多链接超时的情况,这个貌似与网上的很多文章的问题相似,抱着严谨的态度,我们还是要继续往下看看。我们使用perf和FlameGraph的工具,来看下具体函数占用的cpu时间的情况。
perf record -F 99 -p 391280 -g -- sleep 30 perf script -i perf.data &> perf.unfold git clone https://github.com/brendangregg/FlameGraph.git cp perf.unfold FlameGraph/ cd FlameGraph/ ./stackcollapse-perf.pl perf.unfold &> perf.folded ./flamegraph.pl perf.folded > perf_kubelet.svg
执行往如上命令后,我们看下火焰图的结果,貌似存在很多file相关的函数占用时间,初步怀疑是不是跟主机上的文件系统有关系。不着急,咱么继续再往下看。执行kubelet的火焰图的生成命令。
## 在apiserver服务器上执行代理命令 kubectl proxy --address='0.0.0.0' --accept-hosts='^*$' ## 在任意一台能够访问k8s的客户端上创建一个go环境 docker run -d --name golang-env --net host golang:latest sleep 3600 ## 获取性能数据,这里注意下,可能会发生502的错误,这里可以将seconds设置小点再试试 go tool pprof -seconds=60 -raw -output=kubelet.pprof http://APIserver:8001/api/v1/nodes/<node_name>/proxy/debug/pprof/profile ## 转换成火焰图 ./stackcollapse-go.pl go_kubelet.pprof > go_kubelet.out ./flamegraph.pl go_kubelet.out > go_kubelet.svg
从火焰图上分析,发现kubelet的fs.GetDirUsage导致的。这样与我刚才的猜想是一致的。大概原因找到了,那我们就看看kubelet为什么会因为这个函数而cpu如此过高?
问题到这里,大概导致这个情况我已经猜到了。这里有个信息就是,我们微服务用到了dubbo-minitor这款工具,它会产生大量的图表文件。在问题排查之前呢,我也通过kubectl describe node-name 查看了下当前节点上运行了哪些pod。
接下来我找到dubbo-monitor对应的容器volume的路径,执行目录遍历命令。最终确实耗时很长。
为什么cadvisor的fs.GetDirUsage的这么耗cpu,它的实现原理是怎样的?
我查看了kubenetes-1.20的源码,发现引入的cadvisor中没有这个方法。然后我直接去翻看cadvisor的fs.go的commit记录,发现GetDirUsage的代码在2016年10月13日被修改了。当时的具体实现是使用的du命令。
实际上该问题还是会存在,du命令会对待统计文件逐个调用fstat这个系统调用,获取文件大小。它的数据是基于文件获取的,所以有很大的灵活性,不一定非要针对一个分区,可以跨越多个分区操作。如果针对的目录中文件很多,du速度就会很慢了。
为什么kubelet的cpu的使用率是时高时低呢?
这里我们就要看下kubelet的检查文件系统使用率的机制了。默认情况下,kubelet的–node-status-update-frequency 配置,指定 kubelet 向主控节点汇报节点状态的时间间隔,默认是10s。这样意味着每10s将触发一次文件系统使用率检查。cadvisor的函数也设置了超时时间,默认是60s。
问题原因找到了,那这个问题要怎么解决呢?
其实我目前还没有一个很好的方式来解决这个问题,但是我尝试去解决。我使用了LocalVolume来进行数据的存储。kublet的imagefs和nodefs的计算不会计算volume的磁盘使用大小。但是实际上volume还是会被监控获取volume的磁盘大小,实际上还是用到的cadvisor。差别只是获取监控的数据间隔时间相比于node-status-update-frequency时间较长,相对来说cpu使用的时间变长。
这里也希望各位大佬能够提供一个好的解决思路,咱们一起探讨下。
文章中必然会有一些不严谨的地方,还希望大家包涵,大家吸取精华(如果有的话),去其糟粕。如果大家感兴趣可以关我的公众号:gungunxi。我的微信号:lcomedy2021