Linux内核参数引起的 K8s 集群故障

0    37    1

👉 本文共约5228个字,系统预计阅读时间或需20分钟。

背景说明

运行环境信息:Kubernetes + docker,应用程序:Java

问题描述

1、首先从 Kubernetes 事件中心告警信息如下,该告警集群常规告警事件(其实从下面这些常规告警信息是无法判断是什么故障问题)

图片

2、最初怀疑是 docker 服务有问题,切换至节点上查看 docker & kubelet 日志,如下:

kubelet 无法初始化线程,需要增加所处运行用户的进程限制,大致意思就是需要调整 ulimit -u(具体分析如下先描述问题)

3、于是查看系统日志,如下(本来想追踪当前时间的系统日志,但当时系统反应超级慢,但是当时的系统 load 是很低并没有很高,而且 CPU & MEM 利用率也不高,具体在此是系统为什么反应慢,后面再分析 “问题一”):

再执行查看系统命令,提示无法创建进程

4、尝试在该节点新建 Container,如下:

提示初始化线程失败,资源不够

本人提供Oracle、MySQL、PG等数据库的培训和考证业务,私聊QQ646634621或微信db_bao,谢谢!

故障分析

根据以上的故障问题初步分析,第一反应是 ulimi -u 值太小,已经被 hit(触及到,突破该参数的上限),于是查看各用户的 ulimi -u,官方描述就是 max user processes(该参数的值上限应该小于 user.max_pid_namespace 的值,该参数是内核初始化分配的)。

监控信息

查看用户的 max processes 的上限,如下:

因为 ulimit 是针对于每用户而言的,具体还要验证每个用户的 limit 的配置,如下:

根据以下配置判断,并没有超出设定的范围。

查看节点运行的进程:

从监控信息可以看到在故障最高使用 457 个进程。

图片

查看系统中的进程状态,如下:

虽然说有个 Z 状的进程,但是也不影响当前系统运行。

图片

查看系统 create 的线程数,如下:

从下监控图表,当时最大线程数是 32616。

图片

分析过程

1、从以上监控信息分析,故障时间区间,系统运行的线程略高 31616,但是该值却没有超过当前用户的 ulimit -u 的值,初步排除该线索。

2、根据系统抛出的错误提示,Google 一把 fork: Resource temporarily unavailable,找到了一个贴子:https://github.com/awslabs/amazon-eks-ami/issues/239 [1],在整个帖子看到一条这样的提示:

3、于是根据该线索,翻阅 linux 内核文档,搜索 PID 相关字段,其中找到如下相关的 PID 参数:

4、查看系统内核参数 kernel.pid_max,如下:

关于该参数的初始值是如何计算的,下面会分析的。

5、返回系统中,需要定位是哪个应用系统 create 如此之多的线程,如下(推荐安装监控系统,用于记录监控数据信息):

图片

通常网上的教程都是盲目的调整对应的内核参数值,个人认为运维所有操作都是被动的,不具备根治问题,需要从源头解决问题,最好是抛给研发,在应用系统初始化,create 适当的线程量。

具体如何优化内核参数,下面来分析。

参数分析

相关内核参数详细说明,及如何调整,及相互关系,及计算方式,参数边界,如下说明:

kernel.pid_max

概念就不详述了,参考上文(大致意思就是,系统最大可分配的 PID identify,理解有点抽象,严格意义是最大标识,每个进程的标识符,当然也代表最大进程吧)。

话不多说,分析源代码,如有误,请指出。

代码地址:https://github.com/torvalds/linux/blob/v5.11-rc1/kernel/pid.c

上面代码表示,pid_max 默认赋值等于 PID_MAX_DEFAULT 的值,但是初始创建的 PID identify 是 RESERVD_PIDS + 1,也就是等于 301,小于等于 300 是系统内核保留值(可能是特殊使用吧)

那么 PID_MAX_DEFAULT 的值是如何计算的及初始化时是如何定义的及默认值、最大值,及 LIMIT 的值的呢?具体 PID_MAX_DEFAULT 代码如下:

代码地址:linux/threads.h at v5.11-rc1 · torvalds/linux · GitHub[2]

但是翻阅 man proc 的官方文档,明确说明:如果 OS 为 64 位系统 PID_MAX_LIMIT 的边界值为 2 的 22 次方,精确计算就是 210241024*1024 等于 1,073,741,824,10 亿多。而 32BIT 的操作系统默认就是 32768

如何查看 CONFIG_BASE_SMALL 的值,如下:

0 代表未被赋值。

kernel.threads-max

参考文档:Documentation for /proc/sys/kernel/ — The Linux Kernel documentation[3]

该参数大致意思是,系统内核 fork() 允许创建的最大线程数,在内核初始化时已经设定了此值,但是即使设定了该值,但是线程结构只能占用可用 RAM page 的一部分,约 1/8(注意是可用内存,即 Available memory page), 如果超出此值 1/8 则 threads-max 的值会减少

内核初始化时,默认指定最小值为 MIN_THREADS = 20,MAX_THREADS 的最大边界值是由 FUTEX_TID_MASK 值而约束,但是在内核初始化时,kernel.threads-max 的值是根据系统实际的物理内存计算出来的,如下代码:

代码地址:linux/fork.c at v5.16-rc1 · torvalds/linux · GitHub[4]

kernel.threads-max 该参数一般不需要手动更改,因为在内核根据现在有的内存已经算好了,不建议修改

那么 kernel.threads-max 由 FUTEX_TID_MASK 常量所约束,那它的具体值是多少呢,代码如下:

代码地址:linux/futex.h at v5.16-rc1 · torvalds/linux · GitHub[5]

vm.max_map_count

参考文档:Documentation for /proc/sys/vm/ — The Linux Kernel documentation[6]

这个参数大致意思是,允许系统进程最大分配的内存 MAP 区域,一般应用程序占用少于 1000 个 map,但是个别程序,特别针对于被 malloc 分配,可能会大量消耗,每个 allocation 会占用一到二个 map,默认值为 65530。

通过设定此值可以限制进程使用 VMA(虚拟内存区域) 的数量。虚拟内存区域是一个连续的虚拟地址空间区域。在进程的生命周期中,每当程序尝试在内存中映射文件,链接到共享内存段,或者分配堆空间的时候,这些区域将被创建。调优这个值将限制进程可拥有 VMA 的数量。限制一个进程拥有 VMA 的总数可能导致应用程序出错,因为当进程达到了 VMA 上线但又只能释放少量的内存给其他的内核进程使用时,操作系统会抛出内存不足的错误。如果你的操作系统在 NORMAL 区域仅占用少量的内存,那么调低这个值可以帮助释放内存给内核用参数大致作用就是这样的。

可以总结一下什么情况下,适当的增加:

  • 压力测试,压测应用程序最大 create 的线程数量;
  • 高并发的应用系统,单进程并发非常高。

参考文档:

配置建议

参数边界

参数名称范围边界
kernel.pid_max系统全局限制
kernel.threads-max系统全局限制
vm.max_map_count进程级别限制
/etc/security/limits.conf用户级别限制

总结建议

  1. kernel.pid_max 约束整个系统最大 create 的线程与进程数量,无论是线程还是进程,都不能 hit 到此设定的值,错误有二种(create 接近抛出 Resource temporarily unavailable,create 大于抛出 No more processes...);可以根据实际应用场景及应用平台修改此值,比如 Kubernetes 平台,一个节点可能运行上百 Container instance,或者是高并发,多线程的应用。
  2. kernel.threads-max 只针对事个系统所有用户的最大他 create 的线程数量,就大于系统所有用户设定的 ulimit -u 的值,最好 ulimit -u 精确的计算一下(不推荐手动修改该参数,该参数是由在内核初始化系统算出来的结果,如果将其放大可以会造成内存溢出,一般系统默认值不会被 hit 到)。
  3. vm.max_map_count 是针对系统单个进程允许被分配的 VMA 区域,如果在压测时,会有二种情况抛出(线程不够 11=no more threads allowed,资源不够 12 = out of mem.)但是此值了不能设置的太大,会造成内存开销,回收慢;此值的调整,需要根据实际压测结果而定(常指可以被 create 多少个线程达到饱和)。
  4. limits.conf 针对用户级别的,在设置此值时,需要考虑到上面二个全局参数的值,用户的 total 值(不管是 nproc 还是 nofile)不能大于与之对应的 kernel.pid_max & kernel.threads-max & fs.file-max。
  5. Linux 通常不会对单个 CPU 的 create 线程数做上限,过于复杂,个人认为内存不好精确计算吧。

参考

https://www.cnblogs.com/apink/p/15728381.html

标签:

头像

小麦苗

学习或考证,均可联系麦老师,请加微信db_bao或QQ646634621

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

2 × 4 =

 

嘿,我是小麦,需要帮助随时找我哦
  • 18509239930
  • 个人微信

  • 麦老师QQ聊天
  • 个人邮箱
  • 点击加入QQ群
  • 个人微店

  • 回到顶部
返回顶部