资讯动态

Kubernetes 节点压力:在首次驱逐容器组前7分钟捕捉到内存下降

发布时间:2026/10/17 19:42:42 来源:尧图企业网站定制
作者来自 Elastic Jeffrey Rengifo被驱逐的容器组往往并不是内存占用增长的那个。本演示从一条针对单个节点的异常检测告警出发一路追踪到工作集发生变化的容器组。在 EKS、 GKE、 AKS 和 OpenShift 中结合完整的应用上下文监控集群、容器组和节点。观看我们的点播网络研讨会使用 Elastic 和智能体人工智能管理 Kubernetes提升你的技能。你也可以开始免费云试用或在本地运行 Elasticsearch。在一次分阶段加载的负载测试中一个 Elastic 异常检测作业触发了告警比 kubelet 设置MemoryPressure内存压力早2分31秒比首次 HTTP 请求失败早7分29秒。当MemoryPressure变为真时 kubelet 已经开始选择要驱逐哪些容器组而且它选择的并不是消耗了那些内存的容器组。在本次测试中它驱逐了一个内存占用不足9 MiB 的 NGINX 容器组导致服务随之中断。当节点上的可用内存或磁盘空间越过 kubelet 的驱逐阈值时 Kubernetes 节点压力就会出现而针对该状态的告警触发时留给你采取行动的时间已经很少。异常检测会学习每个节点正常的可用内存水平在节点仍有余量时标记内存下降但并非每次下降都能被标记。在第二次测试中相同的总负载一次性到达异常得分为4.2完全没有触发告警。因此本演示将low_mean作业与余量规则以及 [Kubernetes OTel] 压力规则模板配合使用。节点指标来自 Elastic 的 OpenTelemetry 发行版 EDOT 收集器排查流程则从实际触发的告警出发追踪到发生变化的容器组。前提条件 Elastic、 Kubernetes 和 EDOT 版本启用了机器学习 ML 的 Elastic 部署并具备异常检测所需的权限和订阅。本演示使用 Elastic 托管云9.5.4。一个允许你安装 Helm 图表的 Kubernetes 集群。示例使用 EDOT 收集器9.5.4和 kube-stack 图表0.16.0。Kubernetes OpenTelemetry 资产集成本例为2.6.0用于提供下文使用的仪表板和告警规则模板。你可以在这里找到配套笔记本。它复现了本文的整个实验包括异常检测作业、驱逐检查以及中间的各个步骤并在最后执行清理。Kubernetes 节点压力如何触发驱逐kubelet 会将memory.available可用内存和nodefs.available节点文件系统可用空间等驱逐信号与阈值进行比较。在 Linux 上默认硬阈值为memory.available100Mi和nodefs.available10%。达到硬阈值会立即驱逐容器组而达到软阈值则会先启动宽限期。一旦越过其中任一阈值 kubelet 就会设置节点的MemoryPressure内存压力或DiskPressure磁盘压力状态。kubelet 确定容器组驱逐顺序时首先考虑其资源用量是否超过请求量其次考虑容器组优先级最后考虑用量超出请求量的程度。被驱逐的容器组可能并不是消耗了那些内存的容器组。下表按从早到晚的顺序列出了本演示使用的三个信号、各自说明的情况以及触发后应采取的行动信号说明的情况下一步行动可用内存偏低异常节点的余量低于其自身基线所预测的水平查找发生了什么变化可用内存低于你设置的预警水平剩余容量已接近驱逐阈值与工作负载负责人商定缓解措施MemoryPressure内存压力或DiskPressure磁盘压力kubelet 已越过驱逐阈值检查驱逐情况并升级处理在一个工作节点上分阶段增加内存占用测试集群是一个双节点的 kind 集群运行在内存约为8 GiB 的 Docker 虚拟机上。工作节点的 kubelet 使用适合该虚拟机容量的软驱逐阈值通过 kind 集群配置中的 kubeadm 配置补丁设置kubeletExtraArgs: eviction-soft: memory.available6Gi eviction-soft-grace-period: memory.available5m工作节点上运行三个工作负载一个单副本的 NGINX 部署没有设置内存请求量且容器组优先级较低。 k6 探针约每秒向其发起一次请求并通过 OpenTelemetry OTel 导出结果。一个压力测试容器组在两小时的正常基线运行后分配256 MiB 内存。第二个压力测试容器组约四分半钟后分配1 GiB 内存。这些内存阈值适用于笔记本电脑规模的实验环境。请根据节点容量和你的 kubelet 配置设置自己的阈值。第1步使用 EDOT 收集器收集 Kubernetes 节点指标按照 Kubernetes 快速入门指南通过托管 OpenTelemetry 协议 mOTLP 端点安装 EDOT 收集器。在文档中的 Helm 安装配置中添加以下覆盖配置clusterName: node-pressure-v2 collectors: cluster: config: receivers: k8s_cluster: collection_interval: 10s node_conditions_to_report: [Ready, MemoryPressure, DiskPressure] daemon: config: receivers: kubeletstats: collection_interval: 10s该覆盖配置将DiskPressure磁盘压力添加到上报的节点状态中并每10秒采样一次。在此处及后续所有示例中将node-pressure-v2替换为你的集群名称。在发现页面中确认以下两个数据流metrics-kubeletstatsreceiver.otel-*中每个k8s.node.name都有最新的k8s.node.memory.available值。metrics-k8sclusterreceiver.otel-*中包含k8s.node.condition_memory_pressure和k8s.node.condition_disk_pressure其中0表示假1表示真。在发现页面中运行以下 Elasticsearch 查询语言 ES|QL 查询查看所选时间范围内每个节点两种状态的最新值FROM metrics-k8sclusterreceiver.otel-* | WHERE k8s.cluster.name node-pressure-v2 | STATS memory_pressure MAX(k8s.node.condition_memory_pressure), disk_pressure MAX(k8s.node.condition_disk_pressure), latest_sample MAX(timestamp) BY k8s.cluster.name, k8s.node.name | LIMIT 1000k8s.node.memory.available跟踪 kubelet 据以采取行动的数值。在测试中该指标读数为6,093,116 KiB一秒后 kubelet 记录了一次驱逐其中包含available: 6092844Ki。第2步为节点内存创建按节点进行异常检测的作业在开发工具控制台中创建作业及其数据馈送PUT _ml/anomaly_detectors/node-pressure-memory-v2 { analysis_config: { bucket_span: 1m, detectors: [{ function: low_mean, field_name: k8s.node.memory.available, partition_field_name: k8s.node.name }], influencers: [k8s.node.name] }, analysis_limits: {model_memory_limit: 32mb}, data_description: {time_field: timestamp}, model_plot_config: {enabled: true}, datafeed_config: { datafeed_id: datafeed-node-pressure-memory-v2, indices: [metrics-kubeletstatsreceiver.otel-*], query_delay: 20s, frequency: 10s, query: {bool: {filter: [ {term: {k8s.cluster.name: node-pressure-v2}}, {exists: {field: k8s.node.memory.available}} ]}} } }以下设置最为关键low_mean仅对平均值异常偏低的时间桶评分因此节点释放内存不会触发异常。partition_field_name为每个节点建立独立的基线。64 GiB 的节点和8 GiB 的节点不会共用模型。1分钟的bucket_span时间桶跨度适合小型集群中每10秒一次的采样。5分钟或15分钟的跨度成本更低也能平滑节点指标中的噪声但告警会更晚触发。20秒的query_delay查询延迟为指标到达留出时间然后再分析时间桶。model_plot_config存储模型边界单指标查看器会将其绘制为预期范围。打开作业并从节点历史数据的起点启动数据馈送。将起始值替换为第一条 kubeletstats 文档的时间戳POST _ml/anomaly_detectors/node-pressure-memory-v2/_open POST _ml/datafeeds/datafeed-node-pressure-memory-v2/_start {start:2026-09-19T18:53:00Z}在机器学习管理作业中确认已处理记录数持续增加。如果通过 API 创建的作业未出现在列表中请选择同步已保存对象。均值检测器至少需要八个非空时间桶或两小时的数据以要求更高者为准。在生产环境中应让模型学习完整的工作负载周期包括预期的繁忙时段。第3步基于异常得分创建告警规则打开堆栈管理规则创建规则选择异常检测将节点压力第2版规则命名为“内存余量异常”并选择作业。使用以下设置设置值原因结果类型记录评估每个节点自身的异常严重程度阈值25异常浏览器中25–50区间的下限包含临时结果关闭仅评估已最终确定的时间桶检查间隔1分钟与作业的时间桶跨度一致异常得分介于0到100之间用于衡量某个时间桶对该节点而言有多异常。请根据集群中的日常变化调整阈值。添加一个连接器和一个操作让告警发送到 Slack、电子邮件或 PagerDuty。在消息中包含异常检测操作变量中的{{context.anomalyExplorerUrl}}和{{context.topRecords}}使通知能够直接链接到受影响的节点。第4步添加针对 Kubernetes 余量和压力状态的告警规则以正常速度增长的工作负载也可能耗尽节点资源而不会产生较高的异常得分。使用采用以下 ES|QL 查询的 Elasticsearch 查询规则来覆盖这种情况FROM metrics-kubeletstatsreceiver.otel-* | WHERE k8s.cluster.name node-pressure-v2 AND k8s.node.memory.available IS NOT NULL | STATS available_gib MIN(k8s.node.memory.available) / 1073741824.0 BY k8s.cluster.name, k8s.node.name | WHERE available_gib 6.5将规则命名为“节点压力第2版内存余量预警”将时间字段设为timestamp选择为每一行创建告警并使用两分钟的时间窗口每分钟检查一次。6.5 GiB 的预警水平比该场景的软驱逐阈值高0.5 GiB。请根据两次规则运行之间内存可能下降的速度设置自己的余量。然后在创建规则模板中创建状态规则[Kubernetes OTel] 节点内存压力和 [Kubernetes OTel] 节点磁盘压力。在每个模板的 TS 数据源行之后添加以下行使规则仅评估你的集群| WHERE k8s.cluster.name node-pressure-v2模板默认等待连续三次匹配后才触发告警。本演示将告警延迟设为一次匹配因为软驱逐宽限期可能短于三次规则运行所需的时间。Elastic 集成中的告警规则模板更详细地介绍了时间窗口、运行计划和告警延迟设置。第5步在驱逐发生前排查 Kubernetes 节点压力预警当异常告警或余量告警触发时按以下流程处理查看异常确定节点和内存下降幅度。找出内存发生变化的容器组。检查 kubelet 会优先驱逐哪些容器组。与工作负载负责人一起采取缓解措施。从节点和服务两方面验证是否恢复。查看异常记录找出受影响的节点打开机器学习异常浏览器选择作业和告警的时间范围。然后在异常记录中找到k8s.node.name。比较实际值和典型值列。将表格时间间隔设为显示全部以便每个时间桶显示一行。在该场景中20:56时间桶的平均可用内存为6.76 GiB典型值为7.02 GiB规则评估出的异常得分为30。此时内存压力状态仍为假节点可用内存也高于6.5 GiB 的余量预警水平。所有 HTTP 请求均成功。这张运行结束后截取的截图中同一时间桶的得分列显示为2。随着模型接收更多数据异常得分会重新归一化结果部分将对此进行说明。哪个容器组导致了 Kubernetes 节点压力打开 [Kubernetes OTel] 节点详情仪表板按告警对应的集群和节点进行筛选。然后按平均内存工作集对容器组表格排序。内存占用最大的容器组和内存发生变化的容器组可能并不是同一个。以下查询将每个容器组最近五分钟的数据与其先前的基线进行比较FROM metrics-kubeletstatsreceiver.otel-* | WHERE k8s.cluster.name node-pressure-v2 AND k8s.node.name node-pressure-v2-worker AND k8s.pod.memory.working_set IS NOT NULL AND timestamp NOW() - 1 hour | STATS recent AVG(k8s.pod.memory.working_set) WHERE timestamp NOW() - 5 minutes, baseline AVG(k8s.pod.memory.working_set) WHERE timestamp NOW() - 15 minutes BY k8s.namespace.name, k8s.pod.name | EVAL recent_mib ROUND(recent / 1048576, 1), baseline_mib ROUND(COALESCE(baseline, 0) / 1048576, 1) | EVAL change_mib ROUND(recent_mib - baseline_mib, 1) | WHERE recent_mib IS NOT NULL | SORT change_mib DESC | LIMIT 5两个时间窗口之间留出10分钟的间隔可以避免将新容器组最初几分钟的数据纳入其基线。在该场景的告警触发两分钟后查询返回memory-change其change_mib为246.8而节点上其他所有容器组与各自基线的差值都在1 MiB 以内。接下来列出节点上的容器组及其服务质量 QoS 类别和优先级kubectl get pods -A --field-selector spec.nodeNamenode-pressure-v2-worker \ -o custom-columnsNS:.metadata.namespace,NAME:.metadata.name,QOS:.status.qosClass,PRIORITY:.spec.priorityBestEffort尽力而为容器组以及资源用量超过请求量的低优先级容器组会被优先驱逐。如果其中某个容器组承载的是单副本服务宽限期结束时该服务就会中断。与工作负载负责人一起采取缓解措施并验证恢复情况如果余量稳定且变化符合预期请继续观察节点并审查告警阈值。如果可用内存持续下降、逼近驱逐阈值请与工作负载负责人商定以下措施之一停止可选工作负载或缩减其规模。回滚导致内存用量增加的部署变更。增加节点容量并以受控方式迁移工作负载。然后从两方面确认恢复情况可用内存回到典型范围服务请求成功。此外还应确认响应过程中没有容器组被驱逐。以下查询读取由k8s_events接收器收集的驱逐事件FROM logs-k8seventsreceiver.otel-* | WHERE k8s.cluster.name node-pressure-v2 AND k8s.event.reason Evicted | KEEP timestamp, k8s.object.name, k8s.event.reason, body.text | SORT timestamp | LIMIT 1000在不采取干预措施的情况下运行该场景时 kubelet 首先驱逐 NGINX 容器组消息为“容器 nginx 使用了8884 Ki请求量为0”。一秒后分配了1 GiB 内存的 memory-escalation 容器组也被驱逐。被驱逐的 NGINX 容器组内存占用不足9 MiB但其驱逐导致了服务中断。压力状态的解除晚于内存恢复。在信号回升至阈值以上后 kubelet 仍会维持MemoryPressure内存压力状态持续时间由eviction-pressure-transition-period驱逐压力状态转换期决定默认为五分钟。异常检测能在容器组驱逐前多久发出预警异常告警比MemoryPressure内存压力出现早2分31秒比首次请求失败早7分29秒但只有在内存占用逐步增长时才会如此。该场景运行了两次。第一次运行中负载持续存在直到 kubelet 驱逐容器组。第二次运行中两次内存分配同时开始由于没有触发异常告警因此在余量告警触发后移除了负载。事件第一次运行负载持续第二次运行移除负载负载开始20:56:03256 MiB 21:00:291 GiB 21:12:35两者同时首个最终确定的异常得分30.14.2异常告警20:58:09无内存压力状态变为真21:00:4021:12:45余量告警21:01:2121:13:21首次驱逐和请求失败21:05:38无移除负载21:06:5421:15:52内存压力状态解除21:10:5321:20:45同一个仪表板还嵌入了异常检测作业的单指标查看器显示两次运行的模型边界和异常标记。配套笔记本会导入此仪表板因此你可以用同样的方式查看自己的运行结果。有三个结果值得关注异常告警比MemoryPressure内存压力早2分31秒。它标记了256 MiB 的变化早于使节点越过阈值的1 GiB 内存分配。首次请求失败前的7分29秒中有五分钟来自配置的软驱逐宽限期。两次运行中余量告警都晚于MemoryPressure内存压力分别晚41秒和36秒。1 GiB 的突增会在同一个规则检查间隔内越过0.5 GiB 的余量和驱逐阈值。在第二次运行中宽限期提供了采取行动的时间。异常得分取决于内存如何变化。相同的总负载一次性到达时得分为4.2没有触发异常告警。随着模型接收更多数据异常得分会重新归一化。1 GiB 的突增发生后第一次运行的记录在异常浏览器中的得分从30降至2.7而其 initial_record_score初始记录得分保留了规则评估时的值。平稳的两小时基线使3.7%的下降显得突出。对于具有数天波动历史数据的节点相同变化的得分预计会更低。三种信号共同覆盖哪些情况本演示使用low_mean异常检测作业为每个节点的可用内存建模并以25分的记录得分作为告警阈值。余量规则和 [Kubernetes OTel] 压力模板覆盖了模型评分较低的变化。排查流程从异常记录出发追踪到工作集发生变化的容器组、最先被驱逐的容器组再进行包含服务在内的恢复检查。在分阶段测试中异常告警最先到达但仅在变化逐步发生时如此。保持三种信号全部启用并根据团队响应所需的时间设置软驱逐宽限期。接下来如何扩展 Kubernetes 监控配置以下资源可用于扩展实验并提供各个部分的背景信息在同一个作业中为k8s.node.filesystem.available添加第二个low_mean检测器以便为每个节点的磁盘余量建模。用于 Kubernetes 监控的常见 ES|QL 查询包括因内存不足而终止前的容器内存利用率Elastic 集成中的告警规则模板Kubernetes 文档中的节点压力驱逐为异常检测作业生成告警EDOT Kubernetes 指南原文Kubernetes node pressure alerts with Elastic anomaly detection | Elastic Observability Labs

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑