当前位置: 首页 > Linux

网易蜀扇内核团队:内存Cgroup泄漏分析与解决

时间:2023-04-07 00:40:14 Linux

内存cgroup泄漏是K8s(Kubernetes)集群中的常见问题,会导致节点内存资源不足,或者导致节点无响应而不得不重启服务器恢复;大多数开发人员会使用常规丢弃缓存或关闭内核kmem记帐来规避。本文以网易书扇内核团队的实际案例为基础,分析内存cgroup泄漏问题的根源,并提供在内核层面修复问题的解决方案。后台运维监控发现部分云主机计算节点和K8s(Kubernetes)节点出现负载异常高的问题。具体表现为系统运行很卡,负载持续40+,部分kworker线程cpu占用率高或者处于D状态,业务已经受到影响,具体原因需要分析。问题定位现象分析Perf是cpu使用异常的必备工具。通过perftop观察热点函数,发现内核函数cache_reap的使用率会间歇性偏高。打开对应的内核代码,cache_reap函数的实现如下:不难看出,这个函数主要是遍历一个全局的slab_caches链表,记录了系统上所有slab内存对象的信息。通过分析slab_caches变量相关的代码流程,发现每个内存cgroup对应一个memory.kmem.slabinfo文件。该文件记录了每个内存cgroup组进程申请的slab相关信息。同时,内存cgroup组的slab对象也会被统一加入到全局slab_caches链表中。会不会是slab_caches链表数据太多,导致遍历时间比较?长,然后导致CPU高?slab_caches链表数据太多,前提肯定是内存cgroups多。自然地,我们也想统计一下系统中存在多少个内存cgroup,但是当我们去统计/sys/fs/cgroup/memory目录下的内存时,查看cgroup组的数量时,发现有只有不到一百个内存cgroup,每个内存cgroup中的memory.kmem.slabinfo文件最多只有几十条记录,所以slab_caches链表的成员数最多不会计算。一万多,怎么看都没有问题。最后,我们还是从最原始的函数cache_reap入手。由于这个函数比较耗cpu,我们可以直接trace这个函数来分析代码中哪里执行时间比较长。确认根本原因通过一系列工具对cache_reap函数进行追溯,发现slab_caches链表的成员数量达到了惊人的几百万,与我们实际计算出的数量相差甚远。然后通过cat/proc/cgroup查看系统当前的cgroup信息,发现内存cgroup数量已经累计到20w+了。云主机计算节点上有这么多cgroup,显然不是正常情况。即使在K8s(Kubernetes)节点上,容器服务也无法正常生成这种数量级的cgroup。那么为什么/sys/fs/cgroup/memory目录下统计的内存cgroup数量和/proc/cgroups文件中的记录数量相差这么大呢?由内存cgroup泄漏引起!详细解释如下:系统上的很多操作(如创建和销毁容器/云主机、登录主机、cron定时任务等)都会触发临时内存cgroups的创建。这些memorycgroups中的进程在运行过程中(比如访问文件、创建新文件等)可能会产生cachememory,cachememory会和memorycgroup相关联。当内存cgroup组中的进程退出时,/sys/fs/cgroup/memory目录下的cgroup组对应目录会被删除。但是,内存cgroup产生的缓存内存不会被主动回收。由于一些缓存内存仍然引用内存cgroup对象,所以内存cgroup中的对象不会被删除。在定位的过程中,我们发现内存cgroups的累计数量每天还在缓慢增长,于是跟踪节点内存cgroup目录的创建和删除,发现以下两个触发源会导致内存cgroup泄漏:具体内存cgroup泄漏的原因是cron定时任务执行,用户频繁登录和退出节点这两个触发源,与systemd-logind登录服务有关。当执行cron定时任务或登录主机时,systemd-logind服务会创建临时内存cgroup,当cron任务执行完毕或用户登出后,临时内存cgroup会被删除。在文件操作的情况下,内存cgroup会泄漏。递归方式分析清楚了内存cgroup泄漏的触发场景,问题重现就容易多了:核心递归逻辑是创建一个临时内存cgroup,并进行文件操作生成缓存内存,然后删除临时内存内存cgroup的目录,通过上面的方式,可以在测试环境中快速重现40w内存cgroup的剩余场景。解决方案通过对内存cgroup泄漏问题的分析,基本弄清楚了问题的根源和触发场景,那么如何解决泄漏问题呢?解决方案一:dropcache由于cgroupleak是由于cache内存回收失败导致的,最直接的解决方案就是通过“echo3>/proc/sys/vm/drop_caches”清除系统缓存。但是清理缓存只能缓解,以后还是会出现cgroup泄露的情况。一方面,每天需要配置定时任务来执行dropcache。同时,dropcache动作本身也会消耗大量CPU,影响业务。对于已经形成大量cgroupleaks的节点,dropcache的动作可能会卡在清理缓存的过程中,从而产生新的问题。方案二:nokmemkernel提供cgroup.memory=nokmem参数关闭kmemaccounting功能。配置该参数后,内存cgroup将不会有单独的slabinfo文件,这样即使内存cgroup泄漏,也不会导致kworker线程的CPU高冲上去。但是该方案需要重启系统才能生效,会对业务产生一定的影响,并且该方案不能从根本上解决内存cgroup泄漏问题,只能在一定程度上缓解问题。方案三:排除触发源上面分析中发现的两个导致cgroup泄露的触发源可以排除。第一种情况,与对应的业务模块通信,确认cron任务可以关闭;对于第二种情况,可以通过loginctlenable-lingerusername将相应用户设置为后台常驻用户来解决问题。设置为常驻用户后,当用户登录时,systemd-logind服务会为该用户创建一个常驻内存cgroup。用户每次登录都可以复用这个内存cgroup,用户退出后也不会删除,所以不会有泄露。至此,看似完美解决了cgroup内存泄漏的问题,但实际上,上述方案只能覆盖已知的两种触发场景,并没有解决cgroup资源无法完全清理回收的问题.后续可能还有新内存cgroup泄漏的触发场景。内核常规解决方案中的解决方案在问题定位的过程中,通过Google发现了很多容器场景下cgroup泄露导致的问题。centos7系列和4.x内核都有报告案例,主要是内核对cgroup内核内存核算特性的支持不完善。K8s(Kubernetes)/RunC使用这个特性时,会出现内存cgroup泄露的问题。主要的解决方案无非是以下几种规避方案:定期执行dropcachekernelconfigurationnokmemdisablekmemaccountingfunctionK8s(Kubernetes)disableKernelMemoryAccountingfunctiondocker/runcdisableKernelMemoryAccountingfunction我们正在考虑是否有更好的解决方案,能否解决这个问题cgroup泄漏是否在内核级别“完全”解决?内核回收线程通过对memoycgroupleak问题的深入分析,我们可以看出核心问题是systemd-logind临时创建的cgroup目录虽然会自动销毁,但是由于cgroup内存泄漏产生的cache内存和相关slab内存文件读取和写入不是。由于这些内存页的存在,无法清除cgroup管理结构的引用计数。因此,虽然删除了cgroup挂载的目录,但相关的内核数据结构仍保留在内核中。根据对社区相关问题解决方案的跟踪分析,以及阿里云linux提供的思路,我们实现了一个简单直接的解决方案:在内核中运行一个内核线程,分别对这些剩余内存cgroups进行内存回收,以及将他们持有的内存页释放给系统,让这些残留的内存cgroup可以被系统正常回收。这个内核线程有以下特点:只回收剩余的内存cgroups。该内核线程的优先级设置为最低。每完成一轮内存cgroup回收,就会主动cond_resched(),防止CPU长时间占用。回收线程的核心流程如下:FunctionVerify集成到内核回收线程中的内核的功能和性能测试,结果如下:在测试环境开启回收线程,系统剩余内存cgroups可以及时清理;模拟清理400,000个泄漏内存cgroups,恢复线程的cpu使用率最大资源使用率是可以接受的;测试大规格的剩余内存cgroup,回收了一个20G内存的内存cgroup。核心回收函数执行时间分布基本不超过64us;不会对其他服务造成影响;启用内核回收线程后,内核LTP稳定性测试正常通过,不会增加内核稳定性风险。可以看出,通过增加一个新的内核线程来回收cgroup的剩余内存,能够以较小的资源占用率有效解决cgroup泄露的问题。该方案已大量应用于网易私有云,有效提升了网易容器业务的稳定性。综上所述,以上就是我们分享的内存cgroup泄漏问题的分析定位过程,并给出了相关的解决方案。同时提供了一种在内核层面解决这个问题的方法。在长期的业务实践中,深刻体会到K8s(Kubernetes)/容器场景对Linux内核的使用和需求是全方位的。一方面,整个容器技术主要是基于内核提供的能力构建的。要提高内核的稳定性,相关模块的bug定位和修复能力必不可少;另一方面,内核针对容器场景的优化/新特性层出不穷。我们也持续关注相关技术的发展,例如使用ebpf优化容器网络,增强内核监控能力,使用cgroupv2/PSI提高容器资源隔离和监控能力等。作者简介:张亚斌,网易书凡内核专家