You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ECS Fargate中.NET容器内存使用率计算偏差问题排查

问题描述

我们在AWS ECS Fargate上运行.NET 6应用的Linux Docker容器,该应用从S3流式读取1-数十GB的大对象,拆分JSON片段后加入ConcurrentQueue,同时启动多工作线程从队列取数处理。

最初因StreamReader填充队列速度远超工作线程处理速度,出现OutOfMemory错误。我们实现了基于内存的节流服务,定期检查内存占用,超过阈值时让StreamReader延迟等待,方案曾运行良好。但近期调整消费者处理逻辑后问题复现:应用内计算的内存使用率远低于ECS控制台显示值,导致内存占用飙升至100%,任务被强制终止。

当前内存使用率计算逻辑如下:

private int GetCurrentPercentMemoryUtilization()
{
    long totalMemoryUsedBytes = default;
    foreach (var process in Process.GetProcesses())
        totalMemoryUsedBytes += process.WorkingSet64;
    int precentSystemMemoryUsuage = (int)((totalMemoryUsedBytes / Math.Pow(1024, 3)) / (_runtimeEnvironment.MemoryGb) * 100);
    _logger.LogDebug("Total % used memory used {precentSystemMemoryUsuage}", precentSystemMemoryUsuage);
    return precentSystemMemoryUsuage;
}

本地Windows环境调试时,该函数返回值与任务管理器数据一致,但在ECS Fargate单容器中存在明显偏差。需要调整计算逻辑,消除与控制台数据的差异。

调整方案

1. 基于容器cgroup统计内存,对齐ECS指标

ECS Fargate的内存统计是基于Docker容器的cgroup内存限制与使用数据,而非进程级统计。直接读取cgroup的内存文件能得到和控制台完全一致的数据:

  • 读取/sys/fs/cgroup/memory/memory.usage_in_bytes获取容器当前总内存使用量(字节)
  • 读取/sys/fs/cgroup/memory/memory.limit_in_bytes获取容器的内存限额(即任务定义中设置的内存值)

2. 抛弃遍历所有进程的逻辑

原代码遍历容器内所有进程累加内存,在Linux容器中会包含init、sh等系统辅助进程,且.NET的Process.WorkingSet64在Linux上的统计范围与ECS的cgroup统计存在差异,导致数据不准。直接读取cgroup数据更高效且准确。

3. 修正单位转换的精度问题

原代码用Math.Pow(1024,3)做GB转换,会引入浮点数精度丢失,改用整数运算1024L * 1024 * 1024避免该问题。

调整后的代码示例
private int GetCurrentPercentMemoryUtilization()
{
    long memoryUsedBytes;
    long memoryLimitBytes;

    try
    {
        // 读取cgroup内存使用量
        memoryUsedBytes = long.Parse(File.ReadAllText("/sys/fs/cgroup/memory/memory.usage_in_bytes").Trim());
        // 读取cgroup内存限额
        memoryLimitBytes = long.Parse(File.ReadAllText("/sys/fs/cgroup/memory/memory.limit_in_bytes").Trim());
    }
    catch (Exception ex)
    {
        // 降级逻辑:无法读取cgroup时,使用当前进程内存和配置的实例内存
        _logger.LogWarning(ex, "Failed to read cgroup memory stats, falling back to process-level stats");
        memoryUsedBytes = Process.GetCurrentProcess().WorkingSet64;
        memoryLimitBytes = _runtimeEnvironment.MemoryGb * 1024L * 1024 * 1024;
    }

    // 处理Fargate中罕见的无硬限制情况(通常不会出现)
    if (memoryLimitBytes == long.MaxValue)
    {
        memoryLimitBytes = _runtimeEnvironment.MemoryGb * 1024L * 1024 * 1024;
    }

    int percentMemoryUsage = (int)(((double)memoryUsedBytes / memoryLimitBytes) * 100);
    _logger.LogDebug("Total memory usage percent: {percentMemoryUsage}", percentMemoryUsage);
    return percentMemoryUsage;
}
注意事项
  • Linux Docker容器默认拥有读取/sys/fs/cgroup/memory/目录的权限,无需额外配置权限策略。
  • Fargate任务的内存限额与任务定义中设置的内存值完全一致,读取cgroup的memory.limit_in_bytes比手动维护的_runtimeEnvironment.MemoryGb更准确。
  • 若出现cgroup文件读取失败的情况,降级到当前进程内存统计可保证节流逻辑继续运行。

内容的提问来源于stack exchange,提问作者smk081

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 19:05:15