kubelet非最佳配置导致的系统频繁OOM问题排查与解决
问题
开发环境集群中的业务pod每天频繁更新重建,最近发现部分节点出现以下问题:
- pod经常在创建时提示runc/pod sandbox相关的错误
- 想登陆节点时发现ssh也连不上节点
- 某个节点上的系统pod以及业务pod中的微服务均会出现不稳定的状态
- node的状态会在ready/not ready之间漂移
- 通过lens查看node的状态,发现有系统进程oom的event出现,且每次oom的进程不固定
- 登陆openstack查看虚机控制台和日志,发现大量和oom相关的日志
排查
-
首先让负责openstack的同事确定openstack底层状态正常,排除底层故障原因。巧的是我司openstack也确实偶尔出故障,导致节点状态不佳。
-
确定openstack稳定后,发现问题依旧。
-
一开始看到runc/sandbox相关的问题,搜索社区issue怀疑是docker版本问题。该集群里的docker版本确实搞出了k8s支持的docker版本,于是对部分节点docker进行了降级,发现问题依旧。
-
看到大量oom日志后,开始学习OOM相关原理,从这里作为入口点排查问题。
-
确认节点物理内存是否足够,通过top和free命令观察发现物理内存足够。
-
egrep -i 'killed process' /var/log/messages
或直接dump出/var/log/messages
,使用killed process
关键字搜索(注意大小写),可以看到系统oom的历史和具体情况。 -
使用invoke作为关键字搜索,可以看到是哪些进程触发了oom,触发了哪个cgroup组的限制,以及触发oom后进程打分的详情,以及最终内核选择了哪个进程牺牲掉的过程。比如某次oom的过程:
# containerd是kubelet和docker运行时对接的垫片,就是k8s要移除docker支持中涉及到的模块 # containerd触发了oom-killer containerd invoked oom-killer: gfp_mask=0x50, order=0, oom_score_adj=-999 # sshd服务由于/system.slice cgroup的内存限制被杀掉 Task in /system.slice/sshd.service killed as a result of limit of /system.slice # 接下来是一系列该cgroup下进程的oom打分表,[-1000,1000],优先干掉分数高的。可以看到一众系统进程以0分领先 [ pid ] uid tgid total_vm rss nr_ptes swapents oom_score_adj name Jan 5 05:40:48 gxx-ptkf3 kernel: [ 569] 0 569 12197 1250 25 0 -1000 systemd-udevd Jan 5 05:40:48 gxx-ptkf3 kernel: [ 709] 0 709 13883 179 29 0 -1000 auditd Jan 5 05:40:48 gxx-ptkf3 kernel: [ 739] 32 739 17314 217 37 0 0 rpcbind Jan 5 05:40:48 gxx-ptkf3 kernel: [ 742] 81 742 16571 418 33 0 -900 dbus-daemon Jan 5 05:40:48 gxx-ptkf3 kernel: [ 743] 0 743 48802 234 35 0 0 gssproxy Jan 5 05:40:48 gxx-ptkf3 kernel: [ 750] 0 750 6596 290 19 0 0 systemd-logind Jan 5 05:40:48 gxx-ptkf3 kernel: [ 751] 0 751 5424 278 15 0 0 irqbalance Jan 5 05:40:48 gxx-ptkf3 kernel: [ 762] 998 762 5634 269 16 0 0 chronyd Jan 5 05:40:48 gxx-ptkf3 kernel: [ 1019] 0 1019 597532 12297 129 0 -999 containerd Jan 5 05:40:48 gxx-ptkf3 kernel: [ 1191] 0 1191 22447 449 45 0 0 master Jan 5 05:40:48 gxx-ptkf3 kernel: [ 1202] 89 1202 22517 510 45 0 0 qmgr Jan 5 05:40:48 gxx-ptkf3 kernel: [ 1213] 0 1213 27552 197 11 0 0 agetty Jan 5 05:40:48 gxx-ptkf3 kernel: [ 1238] 0 1238 31598 383 19 0 0 crond Jan 5 05:40:48 gxx-ptkf3 kernel: [ 1247] 0 1247 12923 289 28 0 -1000 sshd Jan 5 05:40:48 gxx-ptkf3 kernel: [ 1498] 0 1498 27552 202 10 0 0 agetty Jan 5 05:40:48 gxx-ptkf3 kernel: [ 3200] 0 3200 27554 730 11 0 -998 containerd-shim Jan 5 05:40:48 gxx-ptkf3 kernel: [ 3254] 0 3254 27554 1602 11 0 -998 containerd-shim Jan 5 05:40:48 gxx-ptkf3 kernel: [ 3278] 0 3278 27202 1123 10 0 -998 containerd-shim Jan 5 05:40:48 gxx-ptkf3 kernel: [ 3336] 0 3336 27202 1111 10 0 -998 containerd-shim Jan 5 05:40:48 gxx-ptkf3 kernel: [ 3468] 0 3468 27202 1119 10 0 -998 containerd-shim Jan 5 05:40:48 gxx-ptkf3 kernel: [ 3547] 0 3547 27554 735 11 0 -998 containerd-shim Jan 5 05:40:48 gxx-ptkf3 kernel: [ 3597] 0 3597 27202 1630 12 0 -998 containerd-shim Jan 5 05:40:48 gxx-ptkf3 kernel: [ 3700] 0 3700 27202 1105 10 0 -998 containerd-shim
-
可以看到应该是
/system.slice
这个cgroup内存设置的值太低了,导致系统内存用量很容易达到上限,触发oom。 -
使用cgroupfs的接口,直接通过文件系统查看/system.slice cgroup的配置:
cat /sys/fs/cgroup/memory/system.slice/memory.limit_in_bytes
,同时查看当前使用量:cat /sys/fs/cgroup/memory/system.slice/memory.usage_in_bytes
。 -
可以看到集群的system.slice group的memory.limit_in_bytes被设置成了512Mb,而usage经常在400Mb上下浮动,如此一来当系统控制组下的进程消耗内存稍微一多,就可能触发OOM。
解决
取消kubelet对system.slice的硬限
那么是谁设置了system.slice的内存限制值为512Mb了呢?答案是kubelet。
如果不是特殊情况,需要严格限制system进程的资源用量,那么取消掉kubelet启动配置中enforce-node-allocatable参数中的system-reserved即可。
如果在这里配置了system-reserved,那么kubelet会使用systemReserved配置中的cpu和memory值,去设置cgroup中system.slice(这个cgroup也是参数,可以配置)对应的资源限制。
具体的修改方法,跟集群部署有关系,比如本文涉及集群的修改方式是编辑/etc/kubernetes/kubelet-config.yml
,注释掉enforce-node-allocatable
里的system-reserved即可。
重新配置system.slice的硬限
使用systemd的cgroup接口重新设置system.slice cgroup的内存上限,避免触发oom
systemctl set-property system.slice MemoryLimit=9223372036854771712
总结
根据k8s官方文档以及本次问题,kubelet的system-reserved应当谨慎配置,因为:
- 如果节点不单独作为k8s节点,有外部进程直接跑在system cgroup下,可能导致system cgroup内存超限,触发oom。
- system cgroup下的系统进程本身随着运行时间的增大,或者类似于containerd进程的频繁大量创建,内存占用量可能会增高,所以如果确定需要设置system-reserved,需要结合监控数据谨慎设定。
解决该问题的过程中,产生了一个疑问,cgroupfs是内核中cgroup暴露出的控制接口,那么systemd也有一套维护cgroup相关的接口,二者是什么关系?在k8s中这两套接口的使用是什么关系?经过查阅k8s官方文档:
- 容器运行时和kubelet可以通过配置选择两个cgroup驱动之一,推荐systemd。这样统一使用一套cgroup接口,单一视图不容易混乱。另外kubelet和容器运行时的cgroup驱动必须保持一致。
- 节点不可随意切换cgroup驱动,需要一些额外的工作才能完成节点cgroup驱动的切换。
参考
正确配置Kubelet可一定程度防止K8S集群雪崩-阿里云开发者社区 (aliyun.com)
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。