容器内存使用率(container_memory_working_set_bytes)高问题排查

背景

五一节假日值班期间,告警群里突然告警容器内存使用率高于 90%,并且后续一直有告警出现。随即登入指标监控系统查看该告警指标,如下:

容器内存使用率
该指标是通过 container_memory_working_set_bytes / container_spec_memory_limit_bytes 相除得到的。

注:

container_memory_cache -- 缓存占用的大小, 包含(total_active_file(活跃的缓存页) 和 total_inactive_file(不活跃的缓存页))。此内存是读写文件时操作系统为了提高性能而使用的内存缓存,当操作系统认为内存使用紧张时,就会回收掉,而不是读写完文件立即回收掉。
container_memory_rss -- RSS占用的大小,常驻内存集大小,分配给进程使用的实际物理内存。
container_memory_usage_bytes -- 当前使用内存,包括所有内存,不管有没有被访问
container_memory_working_set_bytes -- 当前内存工作集使用量。需要注意的是如果该值如果大于 container_spec_memory_limit_bytes,那么容器将会被 OOMKilled。注意不是 JVM OOM。
container_spec_memory_limit_bytes -- 容器限制最大内存使用量

下面这张图展示了它们之间的关系:
引用至:https://blog.csdn.net/qq_34562093/article/details/126754502
注意页面缓存下面的不活跃的缓存页应该是total_inactive_file
在这里插入图片描述

随即dump Jvm 堆内存,保留下现场,为了防止容器被 OOMKilled ,先重启应用。

问题排查

容器内存使用率高

通过 MAT 分析堆内存对象分布,发现可达的对象最大是一个队列,里面有90多万个对象,占用380M内存。由于该服务的配置是 4C8G,最大和最小堆内存配置的是4G,直接内存最大是 1G。由此看大对象 380M 占用似乎也不大,其余对象也没有特别大的。刚开始以为上述的波浪图是由于 JVM 垃圾回收而产生的,但是有一个特别奇怪的地方是,有一个POD节点容器使用率一直维持在54%左右,其他三个 POD 容器使用率是在 52%-92%直接循环(这个后面再解释原因)。所有 POD 节点JVM内存大小都是随着垃圾回收产生波浪线图形。因此容器的内存回收应该是和 JVM 垃圾回收没有关系。从上面的图中也可以看出,JVM 内存是属于 RSS,在java程序启动后,如果 -Xmn 和 -Xmx 配置相等后,其值在 RSS 统计中是一直保持不变的。

再看 JVM 堆外内存,使用200-300M,一直维持相对稳定的状态。因此可以判断此次容器内存使用率高告警和JVM 的内存使用是没有关系的。

通过查看 container_memory_rss 指标数据也印证了上述的结论,container_memory_rss 值一直是处于相对稳定的状态。
container_memory_rss

那么猜测是和 container_memory_cache 页面缓存指标相关。container_memory_cache 主要包含total_active_file(活跃的缓存页) 和 total_inactive_file(不活跃的缓存页),Page Cache 的主要作用是提高磁盘文件的读写性能,因为系统调用 read() 和 write() 的缺省行为都会把读过或者写过的页面存放在 Page Cache 里。该值是写在 /sys/fs/cgroup/memory/memory.stat
memory.stat
我们主要关注 total_active_file、total_inactive_file 两个指标数据,可以看到 total_active_file 内存使用已经到了1.6G左右了。而且这个值是在不断的变化中的。而且通过计算,container_memory_working_set_bytes 的值基本上等于 container_memory_usage_bytes - total_inactive_file 。total_inactive_file 值也在不断变化中,但是基本上浮动不大,由于 total_active_file 值在 30M ~ 2.5G 之间上下波动,导致container_memory_usage_bytes 值也会不断上下波动,而 total_inactive_file 值变化不大,最终的结果就是 container_memory_working_set_bytes 不断上下波动,也就是第一张图片展示的效果。

解释了容器内存使用率呈锯齿状的图形形成的数据来源,那是什么原因导致 total_active_file 值在不断的增加呢?上面提到了 total_active_file 活跃的页缓存主要是和读写文件有关,容器内存使用率是每 30 分钟从最低点到最高点,刚开始以为是应用有什么 30 分钟执行一次的任务有关,经过代码查看发现并没有时间间隔 30 分钟的定时任务。然后去日志目录下查看日志,发现程序输出了大量的日志文件,基本上是按每分钟输出一个 50M左右的文件。
日志文件
到这里基本上就可以推断出,页缓存内存的使用高是和程序打印日志有关,然后再计算下 30 分钟周期内输出的所有日志文件大小总和,刚好和 total_active_file 最大值时差不多大小。然后再去排查程序打印日志的地方,发现了程序在不合理的地方打印了日志,导致很多无用的日志被打印了。

解决方案

解决方案就很简单了,需要控制减少日志的打印。也可以在节假日等流量高峰期修改日志打印级别,减少日志的打印,不仅能提高程序的性能,也能减少内存压力。

小结:

导致容器内存使用率(主要是 container_memory_working_set_bytes 指标)高的原因是:因为应用程序在不断的疯狂的输出日志,导致 total_active_file 活跃页缓存升高,即 container_memory_cache 页缓存指标升高,最终影响计算公式:

container_memory_working_set_bytes(升高) = container_memory_usage_bytes(升高)  - total_inactive_file(基本不变)

当页缓存使用到一定量时,操作系统发现内存紧张后,就会回收掉页缓存,从而造成了容器内存使用率呈锯齿状的原因。

为什么有一个 POD 内存使用率基本维持不变

此次问题排查还有一个比较奇怪的点:就是是有一个节点内存使用率一直是 54%左右,其他三个节点30分钟时间里内存使用率在 54%-90%之间循环。

在排除完程序执行不存在特别差异外,然后通过看 /sys/fs/cgroup/memory/memory.stat 文件发现了端倪。下面是特殊节点的文件内容:
特殊pod memory.stat

不知道你有没有发现,对比其他节点的 memory.stat 文件,这个特殊 pod 的 total_active_file 和 total_inactive_file 值刚好是反过来的,也就是在程序疯狂写日志时,次pod 的 total_inactive_file 在不断的变化,而 total_active_file 值反而相对稳定。套用container_memory_working_set_bytes 计算公式:

container_memory_working_set_bytes(基本不变) = container_memory_usage_bytes(升高)  - total_inactive_file(升高)

就能解释通为什么次pod 的内存使用率一直维持稳定了。那为什么此节点和其他3个节点不一样呢?把猜疑的方向转到了操作系统上去,通过查看发现两个 pod 的宿主机Node 节点的操作系统不一样,此节点是华为的 EulerOS 操作系统,其他节点用的是 CentOS 系统,和运维沟通后他们说后续会全部使用 EulerOS 系统。
宿主机操作系统信息
那也就基本断定是操作系统不一样导致的 total_inactive_file 和 total_active_file 值不一样。可能华为的 EulerOS 系统优化了写文件时使用的页缓存更多的是 total_inactive_file 。

小结

在网上很多人认为 container_memory_working_set_bytes 计算包含 total_active_file(活跃页缓存) 是不合理的,k8s 会依据 container_memory_working_set_bytes 值去 OOMKilled pod 节点(注意要和 JVM OOM 区分开),由于页缓存是可以被回收的,如果把页缓存也算进去,在大量读写文件时页缓存使用多了,操作系统如果没来得及回收,那么将发生 OOMKilled。
下面是 k8s GitHub 上的讨论:
https://github.com/kubernetes/kubernetes/issues/43916
https://github.com/kubernetes/kubernetes/issues/104533

华为的 EulerOS 的优化反而可能是人们所期望的行为,目前也不太确定次优化是否是合理的。值得注意的是指标的定义及收集是 k8s 或者其组件的行为,而页缓存是操作系统控制的,华为的优化操作并不是改变指标的定义,或者是收集指标的逻辑,而是修改了操作系统的页缓存计数,CentOS 系统在写文件时 total_active_file 值会不断变大,EulerOS 系统在写文件时 total_inactive_file 值会不断变大(仅在次问题的场景下)。

总结

  1. 要区分开 JVM OOM 和 容器 OOMKilled ,后者时 k8s 在发现 container_memory_working_set_bytes 值超过了container_spec_memory_limit_bytes limit 限制时,再申请内存时容器将会被 OOMKilled 掉。
  2. 排查容器内存使用率高时,要找到具体内存使用升高的部分,在排除掉时程序 堆外内存导致升高后(JVM 堆内存GC 是不会影响 container_spec_memory_limit_bytes 值的),就可能是页缓存导致的升高。再去排查具体原因。
  3. 页缓存的使用 k8s 是没有参数来控制的,因为这个是操作系统行为,当然可以通过修改操作系统相关配置来优化页缓存的使用。作为程序员来说,我们可以控制程序读写文件等相关操作,不要短时间内读写太多文件,特别是日志的打印。另外还要为 Java 程序设置合理的堆内存大小(-Xmn,-Xmx)和堆外内存的使用。还要预留合理的内存供操作系统用于页缓存,避免容器被 OOMKilled。
  4. 华为的 EulerOS 系统优化最起码在次场景下看起来更合理些。也许是页缓存的算法经过了优化,在其他场景下不确定此优化是否合理。

参考

  1. https://blog.csdn.net/qq_34562093/article/details/126754502
  2. https://cloud.tencent.com/developer/article/1637682
  3. https://cloud.tencent.com/developer/article/2070537

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/7525.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

44 网络基础

本章重点 了解网络发展背景,对局域网/广域网的概念有基本认识 了解网络协议的意义,重点理解TCP/IP五层结构模型 学习网络传输的基本流程,理解封装和分用 目录 1.网络发展 2.协议 3.OSI七层模型 4.TCP/IP五层模型 5.网络传输流程图 6.网络中…

WRT1900ACS搭建openwrt服务器小记

参考链接 wrt1900acs openwrt wrt1900acs openwrt 刷机 wrt1900acs原生固件刷openwrt-23.05.3-mvebu-cortexa9-linksys_wrt1900acs-squashfs-factory.img wrt1900acs openwrt更新刷openwrt-23.05.3-mvebu-cortexa9-linksys_wrt1900acs-squashfs-sysupgrade.bin 通过WEB UI来…

【数据结构】带你轻松掌握算法的复杂度

引入: 哈喽大家好,我是野生的编程萌新,首先感谢大家的观看。数据结构的学习者大多有这样的想法:数据结构很重要,一定要学好,但数据结构比较抽象,有些算法理解起来很困难,学的很累。我…

Spring Gateway的核心功能:路由、过滤、限流一网打尽

Spring Gateway的简介 在微服务架构的世界里,如同繁星点点的服务需要一个指挥家,将它们有序地组织起来,让它们能够和谐地协同工作。这个指挥家,就是Spring Gateway。它是一个基于Spring Framework 5、Project Reactor和Spring Bo…

『 Linux 』基础IO/文件IO (万字)

文章目录 🦄 什么是IO🦄 文件IO(库级别)👾 文件的打开与关闭👾 当前路径👾 文件的读写 🦄 标准输入输出流🦄 文件IO(系统级别)👾 文件的打开👾 文件的关闭👾 …

【微积分】微分方程的求解(必看)

文章目录 微分方程1.一阶微分方程1.1 可分离变量型微分方程1.2 一阶齐次微分方程1.3 一阶线性微分方程 2. 高阶微分方程2.1 可降阶的高阶微分方程求解(以二阶为例)2.2 二阶常系数线性微分方程2.2.1 二阶常系数齐次微分方程2.2.2 二阶常系数非齐次微分方程…

【操作指南】银河麒麟高级服务器操作系统内核升级——基于4.19.90-17升级

1. 升级清单 升级包及依赖包清单如下。 kernel ARM架构 kernel-core-4.19.90-23.18.v2101.ky10.aarch64.rpm kernel-modules-4.19.90-23.18.v2101.ky10.aarch64.rpm kernel-4.19.90-23.18.v2101.ky10.aarch64.rpm kernel-modules-extra-4.19.90-23.18.v2101.ky10.aarch64.r…

ASV1000视频监控平台:接入支持JT808标准的设备

目录 一、JT/T 808标准简介 (一)概述 (二)标准内容简介 1、消息分类 2、位置信息 3、报警信息 4、车辆控制 5、数据转发 二、在ASV1000上通过JT808添加设备 (一)登录视频监控平台管理端 &#x…

Coze扣子开发指南:怎样建立一个工作流?

Coze扣子的工作流支持通过可视化的方式,对插件、大语言模型、代码块等功能进行组合,从而实现复杂、稳定的业务流程编排。​ 简单的说,目前阶段,单独靠大语言模型能完成的任务还是有限的,那怎么办呢?解决方案…

电机控制系列模块解析(17)—— 速度环

一、电机转速控制 电机控制的速度环是整个电机控制系统中的外环,其主要任务是根据设定的转速指令值(目标速度)与实际电机转速之间的偏差,调整电流环的参考值(d轴电流Id或q轴电流Iq,涉及类似单电流环的弱磁…

抖音主播/电商人员有福了,利用Suno创作产品宣传,让产品动起来-小米Su7 - 第510篇

历史文章 AI音乐,8大变现方式——Suno:音乐版的ChatGPT - 第505篇 日赚800,利用淘宝/闲鱼进行AI音乐售卖实操 - 第506篇 如何让AI生成自己喜欢的歌曲-AI音乐创作的正确方式 - 第507篇 导读 现在不管是淘宝电商,还是抖音电商&a…

借助Aspose.SVG图像控件,在线将 PNG 转换为 Base64 字符串

Aspose.SVG for .NET 是用于SVG文件处理的灵活库,并且与其规范完全兼容。API可以轻松加载,保存和转换SVG文件,以及通过其文档对象模型(DOM)读取和遍历文件的元素。API独立于任何其他软件,使开发人员无需使用…

第10篇:创建Nios II工程之控制单个七段数码管

Q:还记得之前使用Verilog case语句来描述实现七段数码管的逻辑功能。本期我们创建Nios II工程用C语言代码实现相同的功能。 A:基本原理:一个七段数码管由7个发光二极管LED组成,所以控制一个数码管的显示即控制7个LED。我们在之前…

手把手教你上手开源性能监控神器Arthas

前言 在日常的工作中,对于商业项目尤其是并发量较高的项目,系统在一些情况下会莫名其妙把CPU打满并且导致服务宕机,虽然90%的情况下,是迭代发版的代码有bug,但是既然有这个情况,线上出现事故了&#xff0c…

海外大带宽服务器的带宽大小是如何定义的?

海外大带宽服务器的带宽大小通常是由提供的数据传输速率来衡量的。Rak部落小编为您整理发布海外大带宽服务器的带宽大小是如何定义的? 带宽的大小决定了服务器能够处理的数据量和传输速度,这对于确保服务器性能至关重要。在详细定义中,带宽可以根据以下…

对C语言符号的一些冷门知识运用的剖析和总结

符号 目录* 符号 注释 - 奇怪的注释 - C风格的注释无法嵌套 - 一些特殊的注释 - 注释的规则建议 反斜杠’’ - 反斜杠有续行的作用,但要注意续行后不能添加空格 * 回车也能起到换行的作用,那续行符的意义在哪? - 反斜杠的转义功能 单引号…

公钥私钥?一文搞懂非对称加密

非对称加密 非对称加密: 通信双方分别创建公钥和私钥,并且保证公钥所加密的信息,只有配对的私钥可以解密,接下来,双方公开交换公钥,通信时,使用对方的公钥进行加密,如此&#xff0…

国内小白用什么方法充值使用ChatGPT4.0?

首先说一下IOS礼品卡订阅,目前最经济实惠的订阅方式,具体操作步骤 使用IOS设备充值,用 App Stroe 兑换券 1、支付宝地址切换旧金山,在里面买app store 的兑换卷 2、美区Apple ID登陆app store ,充值兑换券 3、IOS设…

JavaScript中的事件模型

JavaScript中的事件模型分为:事件和事件流、原始事件、标准事件和IE事件。 事件与事件流 JavaScript中的事件,可以理解为HTML文档或者浏览器中发生的一种交互操作,让网页有互动的功能。常见的事件就是加载事件、鼠标事件和自定义事件。 因…

Find My资讯|苹果设备在修复期间可以保持启用“Find My“功能

iOS 17.5 中有一项名为"维修状态"的新功能,可让送修的设备保持启用"查找我的"(Find My)功能。此前,用户在送修设备时必须禁用跟踪设备位置的"查找我的"功能,但iOS 17.5发布后&#xff0…