kubelet的cpu过高问题排查

问题现象

kubernetes集群收到一条,kubelet的cpu使用率较高的告警,过会就恢复了。然后又告警,这样的情况反反复复发生。

已知信息

  1. kubebernetes 1.17.2
  2. kubelet 1.17.2
  3. cpu使用率过高的情况并非持续的,这里可以初步怀疑,cpu的过高可能是被某个函数定时触发。

问题排查

先来一波常规套路,登录上服务器,执行如下操作:

## 找到进程的pid
ps -ef|grep kubelet

## 查看该进程的状态
top -p xxx

## 每隔1秒查看进程状态,总共10次的数据
pidstat 1 10

最终得到如下结果数据:

kubelet的cpu过高问题排查
发现kubelet的cpu使用情况主要使用在用户态。那我们就看看这个kubelet到底是什么情况?

参考网上的诊断文档(https://www.cnblogs.com/zerchin/p/kubernetes.html)
我进行了如下操作:

## 查看详情
strace -cp 7799

kubelet的cpu过高问题排查

发现futex这个函数占用的时间比较多,同时还发现了另外一个问题epoll_ctl的错误数也很多。咱们还是先关注时间占用的问题吧,跟此次cpu使用率相关性更高。错误数的问题,我们后面再看。

继续执行

## 查看明细
strace -tt -p <pid>

kubelet的cpu过高问题排查

发现存在很多链接超时的情况,这个貌似与网上的很多文章的问题相似,抱着严谨的态度,我们还是要继续往下看看。我们使用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

kubelet的cpu过高问题排查

执行往如上命令后,我们看下火焰图的结果,貌似存在很多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的cpu过高问题排查

从火焰图上分析,发现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命令。
kubelet的cpu过高问题排查
实际上该问题还是会存在,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

上一篇:kubernetes之节点详解


下一篇:kubernetes/k8s CRI分析-容器运行时接口分析