排查 AKS 群集中的 OOMKilled 问题

了解 OOM 终止

在 Azure Kubernetes 服务(AKS)中运行工作负荷时,可能会遇到内存不足错误,导致系统或应用程序 Pod 重启。 本指南可帮助识别和解决 AKS 群集节点中的内存不足(OOMKilled)问题。

重要

使用 Azure 门户中的 AKS 诊断和解决问题 部分来帮助解决群集中的内存问题。

OOMKilled 和逐出解释

在 AKS 等 Kubernetes 环境中,与内存相关的问题可能会导致两种不同的事件类型: OOMKilled逐出。 虽然两者都是由资源压力触发的,但它们在原因、范围和行为上有所不同。

只有内核 OOM 杀手终止的容器才会报告 OOM 已终止的容器。 请务必注意,它是超出其内存限制的容器,默认情况下会终止,并且默认重启,而不是整个 Pod。 另一方面,逐出发生在 Pod 级别,由 Kubernetes 触发,特别是由每个节点上运行的 Kubelet 触发,当节点内存不足时。 被逐出的 Pod 将报告 失败 状态和 逐出原因。

OOMKilled:Container-Level 终止

容器超出其内存限制并且由 Linux 内核的 Out-Of-Memory (OOM) 杀手终止时,将发生 OOMKilled

这是特定于容器的事件。 仅影响违反其内存限制的容器。 如果 Pod 包含其他正常的容器,则 Pod 可能会继续运行。 终止的容器通常会自动重启。

常见指标包括 退出代码 137OOMKilled 的原因 kubectl describe pod

逐出:kubelet 删除 Pod-Level

虽然本指南重点介绍 OOMKilled,但了解 逐出 是 Kubernetes 中的单独机制非常有用。 它们发生在 Pod 级别,例如 节点处于内存压力之下时。

注释

本指南不涵盖 Pod 逐出的所有可能原因,因为它的范围仅限于与内存相关的 OOMKilled 事件。

  • Kubelet 可能会逐出 Pod 来释放内存并维护节点稳定性。

  • 逐出的 Pod 将显示 失败 状态和 被逐出的原因。

  • 与面向单个容器的 OOMKilled 不同,逐出会影响整个 Pod。

OOM 终止的可能原因

OOMKilled 事件可能由于多种原因而发生。 以下是最常见的原因:

  • 资源过度使用:Pod 资源请求和限制未正确设置,导致内存使用率过高。

  • 内存泄漏:应用程序可能有内存泄漏,导致它们随着时间的推移消耗更多的内存。

  • 高工作负荷:应用程序负载突然激增可能导致内存使用量超出分配的限制。

  • 节点资源不足:节点可能没有足够的内存来支持正在运行的 Pod,从而导致 OOM 终止。

  • 低效的资源管理:缺少资源配额和限制可能会导致资源消耗不受控制。

标识 OOM 已终止的 Pod

可以使用以下各种方法之一来标识由于内存压力而终止的 POD:

注释

OOMKilled 可能同时发生在系统 Pod(AKS 创建的 kube-system 命名空间中)和用户 Pod(其他命名空间中的 Pod)。 必须先确定哪些 Pod 在采取进一步作之前受到影响。

重要

使用 Azure 门户中的 AKS 诊断和解决问题 部分来帮助解决群集中的内存问题。

检查 Pod 状态

使用以下命令检查命名空间中所有 Pod 的状态:

  • kubectl get pods -n <namespace>

查找状态为 OOMKilled 的 Pod。

描述 Pod

用于 kubectl describe pod <pod-name> 获取有关 Pod 的详细信息。

  • kubectl describe pod <pod-name> -n <namespace>

在输出中,检查“容器状态”部分是否有 OOM 终止的指示。

Pod 日志

查看 Pod 日志,以 kubectl logs <pod-name> 识别与内存相关的问题。

若要查看 Pod 的日志,请使用:

  • kubectl logs <pod-name> -n <namespace>

如果 Pod 已重启,请检查以前的日志:

  • kubectl logs <pod-name> -n <namespace> --previous

节点日志

可以查看节点上 的 kubelet 日志 ,以查看是否有消息指示在问题发生时触发了 OOM 杀手,并且 Pod 的内存使用量达到了其限制。

或者,可以通过 SSH 连接到 运行 Pod 的节点,并检查内核日志中是否有任何 OOM 消息。 此命令将显示处理 OOM 杀手终止的进程:

chroot /host # access the node session

grep -i "Memory cgroup out of memory" /var/log/syslog

事件

  • 用于 kubectl get events --sort-by=.lastTimestamp -n <namespace> 查找 OOMKilled Pod。

  • 使用 Pod 说明中的事件部分查找与 OOM 相关的消息:

    • kubectl describe pod <pod-name> -n <namespace>

处理系统 Pod 的 OOMKilled

注释

系统 Pod 是指 kube-system 命名空间中由 AKS 创建的 Pod。

metrics-server

问题:

  • 由于资源不足,OOMKilled。

Solution:

CoreDNS

问题:

  • 由于流量高峰,OOMKilled。

Solution:

kube-system 命名空间中的其他 Pod

确保群集包括 系统节点池 和用户节点池,以便将内存密集型用户工作负荷与系统 Pod 隔离开来。 还应确认系统节点池至少有三个节点。

处理用户 Pod 的 OOMKilled

由于内存限制不足或内存消耗过多,用户 Pod 可能会被 OOMKilled。 解决方案包括设置适当的资源请求和限制,以及吸引应用程序供应商调查内存使用情况。

原因 1:用户工作负荷可能在系统节点池中运行

建议为用户工作负荷创建用户节点池。 有关详细信息,请参阅: 在 Azure Kubernetes 服务(AKS)中管理系统节点池

原因 2:应用程序 Pod 由于 OOMkilled 而继续重启

此行为可能是由于 Pod 没有足够的内存分配给它,并且需要更多内存,这将导致 Pod 不断重启。

若要解决此问题,请查看请求和限制文档,了解如何相应地修改部署。 有关详细信息,请参阅 Pod 和容器的资源管理

kubectl set resources deployment <deployment-name> --limits=memory=<LIMITS>Mi ---requests=memory=<MEMORY>Mi

将资源请求和限制设置为应用程序 Pod 的建议数量。

若要诊断,请参阅 Azure Kubernetes 服务(AKS)诊断和解决问题概述

原因 3:Pod 中运行的应用程序消耗过多的内存

确认 Pod 级别的内存压力:

使用 kubectl top 检查内存使用情况:

kubectl top pod <pod-name> -n <namespace>

如果指标不可用,可以直接检查 cgroup 统计信息:

kubectl exec -it <pod-name> -n <namespace> -- cat /sys/fs/cgroup/memory.current

或者,可以使用此值以 MB 为单位查看值:

kubectl exec -it <pod-name> -n <namespace> -- cat /sys/fs/cgroup/memory.current | awk '{print $1/1024/1024 " MB"}'

这有助于确认 Pod 是接近还是超出其内存限制。

  • 检查 OOMKilled 事件

kubectl get events --sort-by='.lastTimestamp' -n <namespace>

若要解决此问题,请与应用程序供应商联系。 如果应用来自第三方,请检查它们是否有已知问题或内存优化指南。 此外,根据应用程序框架,要求供应商在 群集升级到 Kubernetes 1.25 后,在 Pod 中建议使用最新版本的 Java 还是 .Net。

应用程序供应商或团队可以调查应用程序以确定它为何使用这么多内存,例如检查内存泄漏或评估应用是否需要 Pod 配置中的更高内存资源限制。 同时,为了暂时缓解问题,增加遇到 OOMKill 事件的容器的内存限制。

避免将来出现 OOMKill

验证和设置资源限制

查看所有应用程序 Pod 并设置适当的 资源请求和限制 。 使用 kubectl topcgroup 统计信息来验证内存使用情况。

设置系统和用户节点池

确保群集包括 系统节点池 和用户节点池,以便将内存密集型用户工作负荷与系统 Pod 隔离开来。 还应确认系统节点池至少有三个节点。

应用程序评估

为了确保最佳性能并避免 AKS 群集中的内存不足,建议查看应用程序的资源使用模式。 具体而言,评估应用程序是否请求适当的内存限制,以及其负载下的行为是否与分配的资源保持一致。 此评估将有助于确定是否需要调整来防止 Pod 逐出或 OOMKilled 事件。