文 / 零碎运维 SIG
在《AK47 所向无敌,内存透露一网打尽》一文中,咱们分享了 slab 内存透露的排查形式和工具,这次咱们分享一种更加隐秘且更难排查的 ” 内存透露 ” 案例。
一、问题景象
客户收到零碎告警,K8S 集群某些节点 used 内存继续升高,top 查看过程应用的内存并不多,残余内存不足却找不到内存的使用者,内存神秘隐没,须要排查内存去哪儿了。
执行 top 指令并按内存排序输入,内存应用最多的过程才 800M 左右,加起来远达不到 used 9G 的使用量。
二、问题剖析
2.1 内存去哪儿了?
在剖析具体问题前,咱们先把零碎内存分类,便于找到内存应用异样的中央,从内存应用性质上,能够简略把内存分为利用内存和内核内存,两种内存使用量加上闲暇内存,应该靠近于 memory total,这样辨别可能疾速定位问题的边界。
其中 allocpage 指通过 __get_free_pages/alloc_pages 等 API 接口间接从搭档零碎申请的内存量(不蕴含 slab 和 vmalloc)。
2.1.1 内存剖析
依据内存大图别离计算利用内存和内核内存,就能够晓得是哪局部存在异样,但这些指标计算比拟繁琐,很多内存值还存在重叠。针对这个痛点,SysOM 运维平台的内存大盘性能以可视化的形式展现内存的应用状况,并间接给出内存是否存在透露,本案例中,应用 SysOM 检测,间接显示 allocpage 存在透露,使用量靠近 6G。
2.1.2 allocpage 内存
那既然是 alloc page 类型的内存占用多,是否能够间接从 sysfs、procfs 文件节点查看其内存应用了?很遗憾,这部分内存是内核 / 驱动间接调用 __get_free_page/alloc_pages 等函数从搭档零碎申请单个或多个间断的页面,零碎层面没有接口查问这部分内存应用详情。如果这类内存存在透露,就会呈现 ” 内存凭空隐没 ” 的景象,比拟难发现,问题起因也难排查。针对这个难点,咱们的 SysOM 零碎运维可能笼罩这类内存统计和起因诊断。
所以须要进一步通过 SysOM 的诊断利器 SysAK 动静抓取这类内存的应用状况。
2.2 allocPage 类型内存排查
2.2.1 动静诊断
对于内核内存透露,咱们间接能够应用 SysAK 工具来动静追踪,启动命令并期待 10 分钟。
sysak memleak -t page -i 600
诊断结果显示 10 分钟内 receive_mergeable 函数调配的内存有 4919 次没有开释,内存大小在 300M 左右,剖析到这里,咱们就须要联合代码来确认 receive_mergeable 函数的内存调配和开释逻辑是否正确。
2.2.2 调配和开释总结
1)page_to_skb 每次会调配一个线性数据区为 128 Byte 的 skb。
2)数据区调用 alloc_pages_node 函数,一次性从搭档零碎申请 32k 内存(order=3)。
3)每个 skb 会对 32k 的 head page 产生一次援用计数,也就是只有当所有 skb 都开释时,这 32k 内存才开释回搭档零碎。
4)receive_mergeable 函数负责申请内存,但不负责开释这部分内存,只有当利用从 socket recvQ 中把数据读走才会对 head page 援用计数减一,当 page refs 为 0 时,开释回搭档零碎。
当利用生产数据比较慢,可能会导致 receive_mergeable 函数申请的内存开释不及时,而且最坏状况一个 skb 会占用 32k 内存,应用 sysak skcheck 查看 socket 接管队列和发送队列残留状况。
从输入能够晓得,零碎中只有 nginx 过程的接管队列有残留数据,socket fd=11 的 Recv-Q 有靠近 3M 的数据没有接管,通过间接 kill 146935,零碎内存恢复正常了,所以问题根本原因就是 nginx 没有及时收走数据了。
三、问题论断
通过与业务方沟通,最终确认是业务配置问题,导致 nginx 有一个线程没有解决数据,从而导致网卡驱动申请的内存没有及时开释,而 allocpage 内存又是无奈统计的,从而呈现内存凭空隐没的景象。
论断验证
接管队列真的有数据残留吗,这里联合 crash 工具的 files 指令通过 fd 找到对应的 sock:
socket = file->private_data
sock = socket->sk
通过屡次察看,发现 sk_receive_queue 上的 skb 长时间没有变动,这也证实了 nginx 没有及时处理接管队列上的 skb,导致在网卡驱动中调配的内存没有开释。
四、内存透露疑点
在排查过程还遇到一个十分较困惑的中央,sockstat 和 slabtop 看查看 tcp mem 和 skbuff_head_cache 应用都很失常,导致进一步覆盖了网络占用的内存。
tcp mem = 32204*4K=125M
skb 数量在 1.5 万~3 万之间。
依照后面剖析,一个 skb 最坏状况占用 32k 内存,那么 2 万个 skb 最大也就占 600M 左右,怎么会占用几个 G 了,难道剖析有问题?如下图所示,skb 的非线性区可能还存在若干个 frag page,而每个 frag page 又可能由 compund page 组成。
用 crash 理论读取 skb 内存发现,有些 skb 存在 17 个 frag page,并且数据大小只有 10 Byte。
解析 frag page 的 order 为 3,意味着一个 frag page 占用 32k 内存。
极其状况下,一个 skb 可能占用(1+17)8=144 页,上图 slabinfo 中 skbuff_head_cache 沉闷 object 数量为 15033 个,所以实践最大总内存 =14415033*4K = 8.2G,而咱们当初遇到的场景耗费 6G 的内存是齐全有可能的。
—— 完 ——
退出龙蜥社群
退出微信群:增加社区助理 - 龙蜥社区小龙(微信:openanolis_assis),备注【龙蜥】与你同在;退出钉钉群:扫描下方钉钉群二维码。欢送开发者 / 用户退出龙蜥社区(OpenAnolis)交换,独特推动龙蜥社区的倒退,一起打造一个沉闷的、衰弱的开源操作系统生态!
对于龙蜥社区
龙蜥社区(OpenAnolis)由企事业单位、高等院校、科研单位、非营利性组织、集体等在被迫、平等、开源、合作的根底上组成的非盈利性开源社区。龙蜥社区成立于 2020 年 9 月,旨在构建一个开源、中立、凋谢的 Linux 上游发行版社区及翻新平台。
龙蜥社区成立的短期指标是开发龙蜥操作系统 (Anolis OS) 作为 CentOS 停服后的应答计划,构建一个兼容国内 Linux 支流厂商的社区发行版。中长期指标是摸索打造一个面向未来的操作系统,建设对立的开源操作系统生态,孵化翻新开源我的项目,凋敝开源生态。
目前,Anolis OS 8.6 已公布,更多龙蜥自研个性,反对 X86_64、RISC-V、Arm64、LoongArch 架构,欠缺适配 Intel、兆芯、鲲鹏、龙芯等芯片,并提供全栈国密反对。
欢送下载:https://openanolis.cn/download
退出咱们,一起打造面向未来的开源操作系统!https://openanolis.cn