Kubelet 组件占用 CPU 过高的解决思路

问题发现

  • 最近通过监控发现,我们容器云平台其中一个 kubernetes 集群的所有 worker 节点的 kubelet 进程均长时间占用 CPU 过高,奇怪的是 master 节点跟 monitor 节点都没有这个问题。
    • 我们的第一反应是跟负载有关系,这些 worker 节点都运行了很多我们的业务 Pod
    • 幸运的是,这个集群是我们的开发集群,并且这些 worker 节点都是物理机,资源是充足的,还没影响到业务,所以我们有充足的排查处理时间

环境信息

Kubernetes: v1.18.8
操作系统:CentOS Linux release 7.6.1810 (Core) 
kernel:4.4.227-1.el7.elrepo.x86_64

排查过程

  1. 使用工具

    • sysstat version 11.5.5
    • perf
    • go tool
    • FlameGraph
    • htop
  2. 分析过程

  • 首先我们到 worker 节点使用 htop 查看 cpu 使用情况,很容易就发现了 kubelet 的 cpu 使用情况是异常的。


    kubelet_cpu.png
  • 使用 pidstat 同样可以明显发现 kubelet 占用 cpu 的时间都是耗在了内核态
# pidof kubelet
391280
# pidstat -p 391280 1 5
Linux 4.4.227-1.el7.elrepo.x86_64 (rancher-dg-tn9)      11/20/2020      _x86_64_        (72 CPU)

07:54:45 PM   UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
07:54:46 PM     0    391280    4.95  100.00    0.00    0.00  100.00    34  kubelet
07:54:47 PM     0    391280    0.00  100.00    0.00    0.00  100.00    34  kubelet
07:54:48 PM     0    391280    0.00  100.00    0.00    0.00  100.00    34  kubelet
07:54:49 PM     0    391280    3.00  100.00    0.00    0.00  100.00    34  kubelet
07:54:50 PM     0    391280    3.00  100.00    0.00    0.00  100.00    34  kubelet
Average:        0    391280    4.30  100.00    0.00    0.00  100.00     -  kubelet
  • 因为所有的 worker 节点都是这个情况,所以我们想着先在一个节点重启 kubelet 试试,结果重启完 kubelet 之后问题依然还在。到这里就想着事情可能没这么简单了,需要进一步深入分析 Linux 的性能问题。
  • 所以我们 strace 了 kubelet 的进程,发现 syscall 有大量的 futex error,cpu 时间都耗在这里,说明了它可能在等待资源,而资源还没准备好。
# pidof kubelet
391280
# strace -cp 391280
strace: Process 391280 attached
strace: Process 391280 detached
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 99.80 2623.789827       41644     63005     11889 futex
  0.10    2.721893      544378         5           epoll_wait
  0.08    1.989361        1572      1265           epoll_pwait
  0.01    0.156758          19      8173         2 newfstatat
  0.00    0.109730          32      3354       339 read
  0.00    0.101180          21      4602         1 readlinkat
  0.00    0.059750          20      2972       526 epoll_ctl
  0.00    0.039303          16      2422           fcntl
  0.00    0.036715          24      1477           openat
  0.00    0.030070          44       674           write
  0.00    0.027179         204       133           nanosleep
  0.00    0.025634          17      1484           close
  0.00    0.016708          36       457           getdents64
  0.00    0.015895          13      1169           fstat
  0.00    0.015259         206        74           sched_yield
  0.00    0.002846         218        13         7 connect
  0.00    0.001864          38        49           setsockopt
  0.00    0.000938          72        13           socket
  0.00    0.000820          48        17           madvise
  0.00    0.000518          34        15           getpeername
  0.00    0.000470          52         9           getsockopt
  0.00    0.000450          30        15           getsockname
  0.00    0.000427          35        12           rt_sigreturn
  0.00    0.000319          22        14           lseek
  0.00    0.000049          24         2         2 unlinkat
  0.00    0.000007           7         1           getrandom
------ ----------- ----------- --------- --------- ----------------
100.00 2629.143970                 91426     12766 total
  • strace -tt -p Pid 也只是发现了一些 FUTEX_WAIT_PRIVATE 的 Connection timed out,还是没能太好的说明问题。
# strace -p 391280 -T -v -e trace=all -ff 2>&1 | egrep '<[1-9]\.[1-9]'

10:39:35.050828 futex(0x7027320, FUTEX_WAIT_PRIVATE, 0, {tv_sec=3, tv_nsec=984755078}) = -1 ETIMEDOUT (Connection timed out) <3.984847>
10:39:39.035764 futex(0xc0021d4148, FUTEX_WAKE_PRIVATE, 1) = 1 <0.000012>
10:39:39.035822 futex(0xc002ab12c8, FUTEX_WAKE_PRIVATE, 1) = 1 <0.000011>
10:39:39.035861 futex(0x7027320, FUTEX_WAIT_PRIVATE, 0, {tv_sec=8, tv_nsec=935276896}) = -1 ETIMEDOUT (Connection timed out) <8.935371>
10:39:47.971297 futex(0x7022130, FUTEX_WAKE_PRIVATE, 1) = 1 <0.000009>
10:39:47.971337 futex(0x7022030, FUTEX_WAKE_PRIVATE, 1) = 1 <0.000008>
10:39:47.971364 futex(0xc00084c148, FUTEX_WAKE_PRIVATE, 1) = 1 <0.000008>
10:39:47.971388 futex(0xc0002024c8, FUTEX_WAKE_PRIVATE, 1) = 1 <0.000008>
10:39:47.971411 futex(0x7027320, FUTEX_WAIT_PRIVATE, 0, {tv_sec=2, tv_nsec=65115694}) = -1 ETIMEDOUT (Connection timed out) <2.065206>
  • 这个时候我们想到了 perf,很多同学应该都很了解,perf 是非常好用的 Linux 性能分析工具,所以我们想着用 perf 来收集一些数据,再把它生成火焰图来分析。
    • perf 的用法跟参数可以使用 perf --help 查看帮助文档,还是比较简单的
    • 先收集数据 perf.data
    • 然后使用 perf script 对 perf.data 进行解析
    • 最后用 FlameGraph 工具生成火焰图
# 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
perf_kubelet.png
  • 到这里我们稍微有点眉目了,sys_read 函数执行的时间非常长,再看到顶层的两个函数 mem_cgroup_read_events/mem_cgroup_read_stat,占据的宽度最大,说明了这两个函数可能存在性能问题。而这两个函数就是 cgroup 相关的,所以很自然我们就会想到可能是内核的 cgroup 有问题了。
  • 再来看看使用 go pprof 工具通过 APiserver 得到的节点的 kubelet 火焰图
    • 需要在节点安装 golang 环境,这里我们使用 golang 容器
    • 在 master 节点启动 kubectl proxy 代理 APIserver
    • 使用 FlameGraph 工具生成火焰图
# kubectl proxy --address='0.0.0.0'  --accept-hosts='^*$'
# docker run -d --name golang-env --net host golang:latest sleep 3600
# 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
go_kubelet.png
  • 看到 go pprof 工具收集的数据生成的火焰图之后,再结合前面的分析,我们基本可以串起来了
    • kubelet 内置的 cAdvisor 在调用 read() 函数非常慢,从 read 读取的内容和前面两个 mem_cgroup 函数来看,那些 read() 调用是在读取 cgroup 的 memory.stat 文件
    • 这个文件用于描述系统的内存使用以及 cgroup 的限制。cAdvisor 通过轮询这个文件来获取容器所使用的资源情况
    • 我们尝试手工读取一下这个文件
      • 好家伙,用时 9s 多
# time cat /sys/fs/cgroup/memory/memory.stat > /dev/null
real    0m9.115s
user    0m0.000s
sys     0m9.112s
  • 这个时候,我们也搜索到了一个 issue,参考 issue 我们也更深入了解了一下 cgroup
  • 关于内存的 cgroup,它负责管理与统计命名空间(容器)内的内存使用情况。当该 cgroup 中的所有进程退出时,内存 cgroup 会被 Docker 释放。 但是,“内存”不仅是进程的内存,而且虽然进程内存的使用量已经消失,但事实证明,内核还为缓存空间分配了内存,例如 dentries 和 inode(目录和文件元数据),这些内容被缓存到内存 cgroup 中。从这个问题可以看出:
    • 僵尸 cgroup:那些没有进程运行并被删除的 cgroup 仍然持有一定得内存空间
    • 与其在 cgroup 释放的时候遍历所有的缓存页(这也可能很慢),内核会惰性地等待这些内存需要用到的时候再去回收它们,当所有的内存页被清理以后,相应的 cgroup 才会最后被回收。在被回收之前,这些 cgroup 仍然会被计入统计信息中。
    • 从性能的角度来看,它们通过分期回收每个页面来摊销直接整体回收的巨大耗时,选择快速地进行初始的清理,但这种方式会保留一些缓存在内存中。但这也没什么问题,当内核回收缓存中的最后一页内存时,cgroup 最终会被清理,因此这并不是一个“泄漏”。
    • 不幸的是,我们遇到的这个问题在于 memory.stat 执行搜索的方式,比如在我们的服务器内核时 4.4,这个版本的实现是递归的算法进行查询,再加上我们服务器一般都有大量的内存空间,这意味着最后一次内存缓存回收并清理僵尸 cgroup 可能要花很长时间。

处理方法

  • 了解到这里,我们遇到的这个问题的临时解决方法是:
    • 释放节点系统范围内的 dentries 和 inode 缓存;这并不是最终的解决方案,但可以验证问题的原因。
    • 最终的解决方案,还是要升级内核,在社区 kernel 5.2+ 的版本有个 commit 优化了 cgroup stats 查询性能
# 目前处理
echo 2 > /proc/sys/vm/drop_caches

# 后续继续观察是否需要升级 kernel

安信证券容器云团队

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容