排查磁盘密集型应用程序中内存消耗过高的问题

磁盘输入和输出作成本高昂,大多数作系统都实施缓存策略,用于将数据读取和写入文件系统。 Linux 内核通常使用页面缓存等策略来提高整体性能。 页面缓存的主要目标是在缓存中存储从文件系统读取的数据,使其在内存中可用于将来的读取作。

本文可帮助识别和避免磁盘密集型应用程序中由于 Kubernetes Pod 上的 Linux 内核行为而占用大量内存。

先决条件

用于连接到 Kubernetes 群集的工具,例如 kubectl。 若要使用 Azure CLI 进行安装kubectl,请运行 az aks install-cli 命令。

症状

当 Pod 上运行的磁盘密集型应用程序执行频繁的文件系统作时,可能会出现高内存消耗。

下表概述了高内存消耗的常见症状:

症状 DESCRIPTION
工作集的度量值太高。 Kubernetes 指标 API 报告的工作集指标与应用程序使用的实际内存之间存在显著差异时,会出现此问题。
内存不足(OOM)强制终止。 此问题表示 Pod 上存在内存问题。
在大量磁盘活动后,内存使用量增加。 执行备份、大型文件读取/写入或数据导入等作后,内存消耗将增加。
内存使用量无限期增长。 即使应用程序本身没有内存泄漏,Pod 的内存消耗仍然会随时间推移不断增加,就像存在内存泄漏一样。

故障排除清单

步骤 1:检查 Pod 工作集

若要检查 Kubernetes 指标 API 报告的工作 Pod 集,请运行以下 kubectl top pods 命令:

$ kubectl top pods -A | grep -i "<DEPLOYMENT_NAME>"
NAME                            CPU(cores)   MEMORY(bytes)
my-deployment-fc94b7f98-m9z2l   1m           344Mi

有关如何确定哪个 Pod 占用过多内存的详细步骤,请参阅 排查 AKS 群集中的内存饱和问题

步骤 2:检查 Pod 内存统计信息

若要检查占用过多内存的 Pod 上的 c 组 的内存统计信息,请执行以下步骤:

注释

Cgroups 可帮助对 Pod 和容器强制实施资源管理,包括 CPU/内存请求和容器化工作负荷的限制。

  1. 连接到 pod:

    $ kubectl exec <POD_NAME> -it -- bash
    
  2. 导航到 cgroup 统计信息目录并列出与内存相关的文件:

    $ ls /sys/fs/cgroup | grep -e memory.stat -e memory.current
    memory.current memory.stat
    
    • memory.current:当前由 cgroup 及其后代使用的内存总量。
    • memory.stat:这会将 cgroup 的内存占用分解为不同类型的内存、特定于类型的详细信息,以及有关内存管理系统的状态和过去事件的其他信息。

    这些文件中列出的所有值都以字节为单位。

  3. 大致了解如何在 Pod 上分配内存消耗:

    $ cat /sys/fs/cgroup/memory.current
    10645012480
    $ cat /sys/fs/cgroup/memory.stat
    anon 5197824
    inactive_anon 5152768
    active_anon 8192
    ...
    file 10256240640
    active_file 32768
    inactive_file 10256207872
    ...
    slab 354682456
    slab_reclaimable 354554400
    slab_unreclaimable 128056
    ...
    

    cAdvisor 使用 memory.currentinactive_file 计算工作集指标。 可以使用以下公式复制计算:

    working_set = (memory.current - inactive_file) / 1048576 = (10645012480 - 10256207872) / 1048576 = 370 MB

步骤 3:确定内核和应用程序内存消耗

下表描述了一些内存段:

细分市场 DESCRIPTION
anon 匿名映射中使用的内存量。 大多数语言使用此段来分配内存。
file 用于缓存文件系统数据的内存量,包括 tmpfs 和共享内存。
slab 用于在 Linux 内核中存储数据结构的内存量。

结合 步骤 2anon 表示 5,197,824 字节,这与工作集指标报告的总量不接近。 slab Linux 内核使用的内存段表示 354,682,456 字节,这几乎是 Pod 上工作集指标报告的所有内存。

步骤 4:在调试器 Pod 上删除内核缓存

注释

此步骤可能会导致可用性和性能问题。 避免在生产环境中运行它。

  1. 获取运行 Pod 的节点:

    $ kubectl get pod -A -o wide | grep "<POD_NAME>"
    NAME                            READY   STATUS    RESTARTS   AGE   IP            NODE                                NOMINATED NODE   READINESS GATES
    my-deployment-fc94b7f98-m9z2l   1/1     Running   0          37m   10.244.1.17   aks-agentpool-26052128-vmss000004   <none>           <none>
    
  2. 使用 kubectl debug 命令创建调试器 Pod,并创建 kubectl 会话:

    $ kubectl debug node/<NODE_NAME> -it --image=mcr.microsoft.com/cbl-mariner/busybox:2.0
    $ chroot /host
    
  3. 删除内核缓存:

    echo 1 > /proc/sys/vm/drop_caches
    
  4. 通过重复 步骤 1步骤 2 验证上一步中的命令是否会导致任何影响:

    $ kubectl top pods -A | grep -i "<DEPLOYMENT_NAME>"
    NAME                            CPU(cores)   MEMORY(bytes)
    my-deployment-fc94b7f98-m9z2l   1m           4Mi
    
    $ kubectl exec <POD_NAME> -it -- cat /sys/fs/cgroup/memory.stat
    anon 4632576
    file 1781760
    ...
    slab_reclaimable 219312
    slab_unreclaimable 173456
    slab 392768
    

如果您观察到工作集和 slab 内存段的显著减少,那么您可能遇到了 Linux 内核在 Pod 上占用大量内存的问题。

解决方法:配置适当的内存限制和请求

Kubernetes Pod 上高内存消耗的唯一有效解决方法是设置实际的资源 限制和请求。 例如:

resources:
    requests:
        memory: 30Mi
    limits:
        memory: 60Mi

通过在 Kubernetes 或规范中配置适当的内存限制和请求,可以确保 Kubernetes 更有效地管理内存分配,从而缓解过度内核级缓存对 Pod 内存使用情况的影响。

谨慎

配置错误的 Pod 内存限制可能会导致 OOMKilled 错误等问题。

参考文献

第三方信息免责声明

本文讨论的第三方产品由独立于微软的公司制造。 Microsoft对这些产品的性能或可靠性不作任何明示或暗示的保证。

第三方联系人免责声明

为了帮助您获取有关此主题的更多信息,Microsoft 提供了第三方的联系信息。 该联系信息可能会在不通知的情况下更改。 微软不保证第三方联系信息的准确性。

联系我们以获得帮助

如果您有任何疑问或需要帮助,可以创建支持请求,或咨询Azure社区支持。 您还可以向Azure反馈社区提交产品反馈。