阿里云操作系统控制台:解决云原生环境下的隐式内存占用难题

阿里云操作系统控制台助力解决云原生环境下的隐式内存占用难题,提高资源利用率,简化故障排除,保障业务稳定运行。

原文标题:如何解决隐式内存占用难题?

原文作者:阿里云开发者

冷月清谈:

本文深入探讨了云原生和容器化环境中常见的隐式内存占用问题,包括文件缓存、SReclaimable内存、memory group残留以及内存不足时难以追踪的内存消耗。这些问题可能导致系统性能下降、资源浪费甚至系统不稳定。文章重点介绍了阿里云操作系统控制台,这是一个集监控、诊断和AI可观测能力于一体的运维管理平台,旨在解决这些复杂问题。通过实际案例,展示了如何利用该控制台定位并解决Kubernetes集群中Pod的workingset内存过高以及共享内存泄露等问题,从而提高资源利用率、避免性能抖动并简化故障排除过程。最后,文章展望了操作系统控制台的未来发展方向,包括提升AI运维能力、增强跨平台兼容性以及完善监控告警功能。

怜星夜思:

1、除了文中提到的手动释放缓存和删除文件等方法,还有哪些更自动化、更优雅的方式来管理 Kubernetes 集群中容器的文件缓存,以避免 Workingset 内存过高?例如,是否可以通过配置来实现自动清理或者限制缓存大小?
2、文中提到 SReclaimable 内存高会掩盖实际的内存消耗,给运维监控带来困扰。除了使用阿里云操作系统控制台,还有什么其他方法可以更准确地监控和分析 SReclaimable 内存,以便及时发现潜在的内存问题?
3、文章提到了 memory group (cgroup) 残留的问题,这会导致资源监控数据失真。除了文中提到的解决方法,还有什么工具或技术可以用来检测和清理 cgroup 残留,从而确保资源监控的准确性?如何预防 cgroup 残留的产生?

原文内容

阿里妹导读


本文详细介绍了在云原生和容器化部署环境中,内存管理和性能优化所面临的挑战及相应的解决方案。


什么是隐式内存占用

隐式内存占用是指在业务运行过程中引起的系统内存消耗,这些消耗未直接统计或反馈到业务进程中。由于这种内存占用通常不会被业务及时检测到,因此容易被忽略,导致内存的过度消耗。这种现象在高负载环境和复杂系统中尤为显著,可能严重影响系统性能和稳定性。


痛点一:文件缓存(filecache)高

filecache 用来提升文件访问性能,并且理论上可以在内存不足时被回收,但高 filecache 在生产环境中也引发了诸多问题:

  1. filecache 回收时,直接影响业务响应时间(RT),在高并发环境中,这种延时尤为显著,可能导致用户体验急剧下降。例如,在电商网站的高峰购物时段,filecache 的回收延时可能会引起用户购物和付款卡顿,直接影响用户体验。

  2. 在 Kubernetes(k8s)环境中,workingset 包含活跃的文件缓存,如果这些活跃缓存过高,会直接影响k8s的调度决策,导致容器无法被高效调度到合适的节点,从而影响应用的可用性和整体的资源利用率。在 Redis 缓存系统中,高 filecache 可能影响缓存查询速度,尤其在高并发读写操作时,容易出现性能瓶颈。


痛点二:SReclaimable 高

SReclaimable 内存是操作系统为了实现自身功能而缓存的内核对象,虽然不计入应用内存,但应用的行为却影响 SReclaimable 的高低。在内存不足时,SReclaimable 内存虽然可以回收,但回收过程中的抖动会导致业务性能下降,尤其是在高负载情况下,这种抖动可能导致系统不稳定。例如在金融交易系统中,内存抖动可能导致交易处理延迟,影响交易速度和准确性。此外,高 SReclaimable 内存还可能掩盖实际的内存消耗,给运维监控带来困扰。


痛点三:memory group 残留

cgroup 和 namespace 是支撑容器技术的关键组件。业务频繁的容器创建和销毁经常会引起 cgroup 残留,容易造成统计不准确和系统抖动。cgroup 泄漏不仅使得资源监控数据失真,还可能引发系统性能问题,甚至导致资源浪费和系统不可预测性。在大规模集群中,这类问题尤为突出,严重威胁集群的稳定运行。例如,在广告投放系统中,频繁创建和销毁大规模容器可能导致 cgroup 泄漏,引发系统抖动,从而影响广告投放精度和用户点击率。


痛点四:内存不足,却找不到去哪儿了

内存不足时,通过 top 等常用指令通常无法准确定位内存消耗原因。这通常是由驱动(如 GPU/NIC 等)引起的内存消耗,但常见的可观测工具无法覆盖这类内存区域。例如,在AI模型训练过程中,GPU 内存消耗巨大,但监控工具可能无法显示具体的内存去向,只能监控到 free 不足,运维人员也难以判断问题原因。这不仅延长了问题排查时间,还可能导致故障蔓延,最终影响系统的稳定性和可靠性。

解决方案:用操作系统控制台诊断隐式内存


操作系统控制台

操作系统控制台是阿里云操作系统团队最新推出的一站式运维管理平台,充分结合了阿里云在百万服务器运维领域的丰富经验。该控制台集成了监控、诊断、持续追踪、AI 可观测、集群健康度和 OS Copilot 等核心功能,专门应对云端高负载、宕机、网络延迟抖动、内存泄漏、OOM(内存溢出)、I/O 毛刺、I/O 流量过大及性能异常等各种复杂问题。操作系统控制台为用户提供全面的系统资源监控、问题分析和故障解决能力,旨在优化系统性能,显著提升运维效率和业务稳定性。


控制台地址:https://alinux.console.aliyun.com/


总体架构如下:


方案介绍

上述四种场景中,最为常见的是文件缓存(filecache)占用高。我们以这个场景为例,详细介绍操作系统控制台如何探索并成功解决业务痛点。

从一个内存页解析出文件名,大致需要以下几个步骤,其中最为关键的是从 page 到 inode,以及从 inode 到文件名,这里就需要具备两个循环能力:

  1. 能够循环扫描系统全部的文件缓存页。

  2. 能够根据 inode 循环解析出对应文件名。

我们也调研分析了多种方案的优缺点:

最终,操作系统控制台选择基于 kcore 来解析系统 filecache 对应的文件,但也需要解决几个问题:
  1. kcore 读的是 raw 内存,没有数据结构信息。

  2. kcore 需要遍历全量内存,在大内存系统下,CPU 消耗大,时间长。

  3. 需要支持整机和容器级的文件缓存扫描。


方案实施

kcore 方案面临的问题,需要针对性地进行攻克,最终在操作系统控制台实现文件缓存的解析:

  1. 结合 ebpf btf 文件中存储的数据结构大小和偏移量信息,实现 raw 内存解析能力。

  2. filecache 内存页是离散存在系统中的,所以我们利用采样,只要采中一个文件页,就能顺利解析出文件名和对应的缓存量。

  3. 内核导出了文件/proc/kpageflags和/proc/kpagecgroup用于判断页面属性和对应的 cgroup。

应用实例

K8s 是一个开源的容器编排平台,主要用于自动化部署、扩展和管理容器化应用。它提供一个强大的、灵活的架构来支持大规模的应用服务,从而简化了应用的运维管理,企业在享受 K8s 在容器编排和部署所带来的便利时,同时也面临新的问题。


案例一、通过操作系统控制台分析容器内存工作集高

Kubernetes 采用内存工作集(workingset)来监控和管理容器的内存使用,当容器内存使用量超过了设置的内存限制或者节点出现内存压力时,kubernetes 会根据 workingset 来决定是否驱逐或者杀死容器。

  • 内存工作集计算公式:

Workingset = 匿名内存 + active_file。匿名内存一般是程序通过new/malloc/mmap方式分配,而active_file 是进程读写文件引入,程序一般对这类内存使用存在不透明情况,经常容易出问题。

客户通过容器监控发现其 K8s 集群中某个 pod 的 Workingset 内存持续走高,无法进一步定位究竟是什么原因导致的Workingset内存使用高。

针对上述场景,先找到 Pod 所在的 ECS 节点,通过操作系统控制台使用内存全景分析诊断,选择目标 ECS 节点后,再选择目标 Pod,发起诊断,诊断结果如下:

具体的文件路径和占用缓存大小:

  • 首先,诊断结论中直接给出了简明的分析结论 “容器 XXX 内存使用率过高,存在内存不足风险,容器 XXX 内存中文件缓存占用较大”。

  • 其次,根据 诊断建议的引导,通过查看容器文件缓存占用排序部分的表格,可以看到共享内存缓存占用最大的前 30 个文件,从该表格中可以看到,前两个容器内的日志文件(名称显示的是容器内文件在宿主机中的路径,容器内文件目录从/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/3604/fs/后开始)占用了接近 228MB 的文件缓存 。

根据上述分析过程,基本可以确定是客户业务程序主动在这个容器 /var/log 目录下创建并读写了日志文件产生的文件缓存。结合业务需要,可以采取以下方式避免 Pod 内 Workingset 内存过高导致 Pod 内存达到限制从而引发直接内存回收,造成业务进程阻塞,产生延时:

  1. 通过手动执行echo 1 > /proc/sys/vm/drop_caches来主动释放缓存。

  2. 如产生文件缓存的文件是非必要文件,可以通过手动删除文件释放缓存。

  3. 使用ack集群的内存QoS功能:https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/user-guide/memory-qos-for-containers


案例二、通过操作系统控制台分析共享内存高

某行业客户发现,在运行较久的机器上,通过 free -h看到的剩余内存较少,buff/cache 比较多,客户通过分析和查阅资料,通过执行 echo 3 > /proc/sys/vm/drop_caches来主动释放缓存。客户发现,使用该方法可以回收部分缓存,但是仍然还有大量的 cache 占用没有释放:

针对上述场景,通过使用操作系统控制台对目标 ECS 进行内存全景分析诊断,诊断的结果如下:

  • 首先,诊断结论中直接给出了简明的分析结论 “共享内存占用过多(34.35 GB),并且小文件居多,疑似存在共享内存泄露”。

  • 其次,根据 诊断建议 的引导,通过查看 共享内存缓存占用排序部分的表格,可以看到共享内存缓存占用最大的前30个文件,从该表格中可以看到,最大的前几个共享内存文件都只有 160KB,因此可以证明诊断结论中给出的系统中存在大量小的共享内存文件泄漏的论点。同时,通过 文件名称这一列,可以看到这些小文件基本都是 /dev/shm/ganglia/*这个目录下的。

根据上述分析过程,基本可以确定是客户业务程序主动在这个目录下创建了共享内存文件,并且没有及时释放导致的,结合业务的需求,可以评估是否存在泄露,删除泄露的共享内存文件即可释放占用的 cache 内存。


客户收益

通过控制台产品来解决业务中内存占用高的问题,客户可以获得以下收益:

  1. 提高资源利用率:通过准确监控和管理内存使用,能够有效避免内存浪费,提高整体资源利用率。

  2. 避免内存不足带来的性能抖动:通过及时发现和解决内存占用过高的问题,避免了内存不足可能导致的性能抖动,从而保证了业务的稳定运行。

  3. 简化故障排除过程:通过诊断工具提供的简明结论和建议,客户能够更快速地找到问题根源,缩短故障排除时间。

  4. 优化业务性能:通过管理和释放不必要的文件缓存,避免Pod内存达到限制从而引发直接内存回收,减少业务进程阻塞和延时。

总而言之,操作系统控制台给云计算和容器化运维带来新的可能,能够提高系统性能与运维效率,同时为企业减少了系统相关问题带来的困扰。

下一步规划,我们将继续演进控制台能力,包括:

  • 提升AI运维能力:结合AI大模型和小模型,提升对系统异常事件的预测与诊断能力,提供更智能的运维建议。

  • 跨平台兼容性:优化操作系统控制台与更多云产品兼容性,以及更多的操作系统版本支持,使其能广泛地应用于不同的IT环境。

  • 监控告警能力:开放更全面的操作系统监控指标,支持发现更多异常事件,并接入告警机制,帮助业务及时发现并解决潜在问题。

欢迎加入OS控制台钉钉交流群(群号:94405014449),点击阅读原文登录操作系统控制台。

监控 SReclaimable 内存,除了阿里云的工具,还可以用 slabtop 看看内核 Slab 缓存的情况。这玩意儿能告诉你哪些内核对象占用了大量的 SReclaimable 内存。

如果你想更深入地分析,可以用 perf 工具跟踪内核函数的执行情况。看看是不是某个内核函数一直在分配内存,导致 SReclaimable 蹭蹭往上涨。

再高级一点,可以写 SystemTap 脚本,监控 SReclaimable 内存的分配和释放。这玩意儿相当于内核版的 JavaScript,想怎么监控就怎么监控,灵活得很!

可以试试 Kubernetes 的 Resource Quotas 和 Limit Ranges,这俩哥们儿能帮你控制 Pod 的资源使用,比如内存。虽然不能直接限制文件缓存,但能间接影响。你想啊,Pod 内存有限了,系统自然会更积极地回收文件缓存嘛!

另外,可以搞个 Sidecar 容器,专门清理日志。这小弟跟你的业务容器共享 Volume,能定期把旧的日志文件咔嚓掉,这样文件缓存就不会一直膨胀了。当然,用专业的日志管理系统也行,比如 EFK 或者 Loki,把日志都扔到别的地方管,容器里就清爽多了!

终极方案是,直接魔改 Kubernetes 源码,加上文件缓存管理的功能!不过这属于骨灰级玩家,慎入!

除了阿里云操作系统控制台,以下是一些监控和分析 SReclaimable 内存的常用方法:

1. 使用 slabtop 命令slabtop 是一个用于显示内核 Slab 缓存信息的工具。Slab 缓存是 Linux 内核用于管理常用数据结构的一种机制。通过 slabtop,可以查看各个 Slab 缓存对象的大小、数量和使用情况,从而了解 SReclaimable 内存的消耗情况。

* 例如:sudo slabtop -s c 可以按缓存大小排序,sudo slabtop -s o 可以按对象数量排序。

2. 使用 perf 工具perf 是 Linux 性能分析工具,可以用于跟踪内核函数的执行情况。通过 perf,可以分析哪些内核函数导致了 SReclaimable 内存的增长。

* 例如:可以使用 perf record -e kmem:kmalloc -g 记录内存分配事件,然后使用 perf report 查看调用栈。

3. 使用 SystemTap 脚本:SystemTap 是一种动态跟踪工具,允许用户编写脚本来监控内核事件。可以使用 SystemTap 脚本来跟踪 SReclaimable 内存的分配和释放,从而分析内存消耗的原因。

4. 分析 /proc/meminfo 文件/proc/meminfo 文件包含了系统的内存信息,包括 SReclaimable 的大小。可以定期读取该文件,并使用脚本分析 SReclaimable 的变化趋势。

5. 使用专业的监控工具:一些专业的监控工具(例如,Prometheus、Grafana)提供了 SReclaimable 内存的监控指标。可以使用这些工具来实时监控 SReclaimable 的大小,并设置告警阈值。

6. BPF 探针
* 利用 BPF (Berkeley Packet Filter) 技术,可以动态地注入内核探针,收集 SReclaimable 相关的指标。例如,可以追踪特定内核子系统的内存分配和释放,从而更精确地定位问题。

7. 定制化的内核模块
* 如果需要非常深入的分析,可以考虑编写定制化的内核模块,直接访问内核数据结构,收集 SReclaimable 的详细信息。但这种方法风险较高,需要非常熟悉内核机制。

在选择方法时,需要根据实际情况和技术能力进行选择。slabtop/proc/meminfo 比较简单易用,但提供的信息有限;perf 和 SystemTap 可以提供更详细的信息,但需要一定的学习成本;专业的监控工具可以提供实时监控和告警功能,但需要购买和配置。

检测和清理 cgroup 残留,除了文章中提到的方法,还可以使用以下工具和技术:

1. 使用 lscgroup 命令lscgroup 是 libcgroup 工具包中的一个命令,用于列出系统中的所有 cgroup。通过 lscgroup,可以查看是否存在不属于任何进程的 cgroup。

2. 编写脚本扫描 cgroup 目录:cgroup 的信息存储在 /sys/fs/cgroup 目录下。可以编写脚本定期扫描该目录,查找不包含任何进程的 cgroup 目录。

3. 使用 cgroupfs-cleaner 工具:cgroupfs-cleaner 是一个用于清理 cgroup 残留的工具。它可以自动检测并删除不属于任何进程的 cgroup。

4. 使用 Docker prune 命令:如果 cgroup 残留是由 Docker 容器引起的,可以使用 docker system prune 命令清理不再使用的容器、镜像和网络。该命令也会删除相关的 cgroup。

5. CRI-O 的 cgroup 管理
* 如果使用 CRI-O 作为容器运行时,CRI-O 具有更强的 cgroup 管理能力,可以更有效地清理 cgroup 残留。

预防 cgroup 残留的产生,可以采取以下措施:

1. 确保容器正确退出:在停止容器时,确保容器中的所有进程都已正确退出。如果容器中的进程没有正确退出,可能会导致 cgroup 残留。

2. 使用容器编排工具:使用 Kubernetes 等容器编排工具可以更好地管理容器的生命周期,并减少 cgroup 残留的风险。

3. 定期清理不再使用的容器:定期清理不再使用的容器可以减少 cgroup 残留的数量。

4. 升级到较新的 Docker 版本:较新的 Docker 版本通常具有更好的 cgroup 管理能力,可以减少 cgroup 残留的风险。

在选择工具和技术时,需要根据实际情况和技术能力进行选择。lscgroup 和脚本扫描比较简单易用,但需要手动操作;cgroupfs-cleaner 可以自动清理 cgroup 残留,但需要安装和配置;Docker prune 命令可以清理 Docker 相关的 cgroup 残留,但可能会删除其他不再使用的资源。

cgroup 残留?这玩意儿就像厕所里的污垢,不及时清理,会影响系统性能。

清理 cgroup 残留?简单!直接 umount -l /sys/fs/cgroup/<你的cgroup>,一键卸载!

预防 cgroup 残留?更简单!用 Kubernetes!Kubernetes 会自动管理 cgroup,不用你操心!

针对 K8s 集群中容器文件缓存的管理,除了手动操作外,可以考虑以下几种自动化和更优雅的方案:

1. 使用 Kubernetes 的 Resource Quota 和 LimitRange
* 虽然 Resource Quota 主要用于限制命名空间级别的资源使用,LimitRange 用于限制 Pod 或 Container 级别的资源使用,但可以通过合理配置,间接影响文件缓存的使用。例如,限制 Pod 的内存使用,当内存达到限制时,系统可能会更积极地回收文件缓存。

2. 利用 Kubernetes 的 Eviction Policy
* Kubernetes 允许定义 Eviction Policy,当节点资源紧张时,可以根据策略驱逐 Pod。可以配置基于 memory.available 的驱逐策略,当可用内存低于一定阈值时,驱逐那些 Workingset 较高的 Pod。

3. 使用 Sidecar 容器进行日志清理
* 对于日志文件导致的文件缓存过高,可以使用 Sidecar 容器定期清理日志文件。Sidecar 容器与业务容器共享 Volume,可以访问业务容器的日志目录,并根据策略(例如,根据时间或大小)删除旧的日志文件。

4. 调整内核参数 vm.dirty_ratio 和 vm.dirty_background_ratio
* 这两个内核参数控制脏页的比例,脏页是指已修改但尚未写入磁盘的页面。适当调整这两个参数,可以影响文件缓存的刷新频率,从而影响文件缓存的大小。但需要谨慎调整,避免频繁写入磁盘导致 I/O 压力过大。

5. 利用 cgroup Memory Limit 和 Swap
* 配置 cgroup 的内存限制,当容器内存使用超过限制时,系统可能会将部分内存交换到 Swap 空间,从而减少文件缓存的压力。

6. 使用专业的日志管理系统
* 例如,使用 EFK(Elasticsearch、Fluentd、Kibana)或 Loki 等日志管理系统,将日志数据集中存储和管理,而不是直接写入容器的文件系统。这样可以减少容器内的文件缓存。

7. 开源项目
* 调研是否有开源的 K8s 文件缓存管理 Operator,通过 CRD 的方式对文件缓存进行管理,可能已经有大神造好了轮子。

在选择方案时,需要综合考虑业务需求、系统复杂度和维护成本。自动化方案可以显著减少人工干预,提高运维效率,但需要仔细配置和测试,确保不会对业务产生负面影响。

SReclaimable 内存?这玩意儿就像你冰箱里的剩菜,放着占地方,扔了又有点可惜。

想监控它?简单!直接 cat /proc/meminfo,看看 MemReclaimable 的值就行了。不过这玩意儿只能告诉你总量,想知道是谁吃的,还得靠 slabtop

slabtop 就像一个验尸官,能告诉你哪个 Slab 缓存对象占用了最多的 SReclaimable 内存。找到凶手,才能对症下药!

泻药,人在容器里,刚被 OOM Killed。

优雅管理文件缓存?不存在的!手动 echo 1 > /proc/sys/vm/drop_caches 才是王道!

开玩笑的啦,其实可以试试调整内核参数 vm.dirty_ratiovm.dirty_background_ratio,让系统更勤快地把脏页刷到磁盘上。不过别调太狠,小心把 I/O 打爆。

或者,用 Kubernetes 的 Eviction Policy,当节点内存不够用的时候,自动把 Workingset 高的 Pod 踢走。这样虽然有点暴力,但总比整个节点挂掉好吧?

实在不行,就只能上大招了:加内存!

要检测和清理 cgroup 残留,可以试试 cgroupfs-cleaner 这个工具。它能自动扫描并删除那些没用的 cgroup,省时省力!

另外,可以写个脚本,定期扫描 /sys/fs/cgroup 目录,看看有没有哪个 cgroup 里面没有进程了。如果有,就直接 rm -rf 掉!

预防 cgroup 残留,最重要的是保证容器正常退出。别让容器里的进程变成僵尸进程,占着茅坑不拉屎!