Prhub

#45086 [Bugfix][CPU] Honor cgroup memory limit when computing KV cache size

原始 PR 作者 maobaolong 合并时间 2026-06-15 10:26 文件变更 1 提交数 4 评论 6 代码增减 +52 / -0

执行摘要

修复 CPU 容器内 KV 缓存大小计算错误

用户在容器(Docker/Kubernetes Pod)中运行vLLM时遇到 ValueError: Available memory on node 0 (149.11/692.65 GiB) ... is less than requested memory for kv (206.54/207.8 GiB) 错误。根本原因是 gpu_memory_utilization 基于宿主机NUMA节点总内存(692.65 GiB)计算,但容器仅被分配约150 GiB内存,导致KV缓存分配超出限制。

建议精读。本PR展示了处理Linux容器内资源感知的经典模式,代码简洁且边界处理完整(v1/v2回退、哨兵值、文件异常)。可作为在vLLM中处理cgroup限制的参考实现。

讨论亮点

审核者 bigPYJ1151 最初建议使用 cgroup/memory.numa_stat 获取每NUMA节点的内存状态,但 maobaolong 尝试后发现cgroup不提供每NUMA节点的限制和空闲大小,只能进行近似分配。最终 bigPYJ1151 建议忽略NUMA细节,仅当cgroup有内存限制时直接钳制总内存大小,maobaolong 采纳并移除了 numa_stat 解析。

实现拆解

  1. 新增 _read_int_file 工具函数:安全读取cgroup文件内容,处理空值、'max'及文件异常,返回 int | None

  2. 新增 get_cgroup_memory_limit 函数(带 @cache 装饰器):先尝试cgroup v2路径(memory.max + memory.current),若不存在则回退到cgroup v1(memory.limit_in_bytes + memory.usage_in_bytes),并排除v1中大于等于2^62的无穷大哨兵值。非Linux系统直接返回 (None, None)

  3. 修改 get_memory_node_info 函数:在计算出NUMA节点的 total_memoryavailable_memory 后,调用 get_cgroup_memory_limit() 获取cgroup限制。若cgroup限制存在且小于NUMA总内存,则将 total_memory 设为cgroup限制值,并基于 cgroup_limit - cgroup_usage 计算cgroup可用内存,取 min(available_memory, cgroup_available) 且最低为0。

  4. 未新增测试文件:PR仅修改了一个源码文件(vllm/utils/cpu_resource_utils.py),无对应单元测试。变更涉及文件系统读取,测试依赖容器环境,作者在PR描述中提供了手动测试结果。

文件 模块 状态 重要度
vllm/utils/cpu_resource_utils.py 资源管理 modified 7.6

关键符号

_read_int_file get_cgroup_memory_limit

关键源码片段

vllm/utils/cpu_resource_utils.py core-logic

唯一的变更文件,新增 cgroup 内存限制读取函数并在 KV 缓存计算中应用钳制逻辑。

def _read_int_file(path: str) -> int | None:
    # 安全读取 cgroup 文件,处理 'max'、空值及异常
    try:
        with open(path) as f:
            value = f.read().strip()
        if not value or value == "max":
            return None # cgroup v2 中用 'max' 表示无限制
        return int(value)
    except (OSError, ValueError):
        return None # 文件不存在或格式错误时不阻塞
​
​
@cache
def get_cgroup_memory_limit() -> tuple[int | None, int | None]:
    """Return (limit, usage) in bytes from cgroup, or (None, None).    Supports both cgroup v2 (unified) and v1. Returns (None, None) when
    not running under a constrained cgroup (e.g. bare metal, or limit
    reported as `max`/an unrealistically large value).
    """
    if sys.platform != "linux":
        return None, None
​
    # cgroup v2 unified hierarchy
    v2_limit = _read_int_file("/sys/fs/cgroup/memory.max")
    if v2_limit is not None:
        v2_usage = _read_int_file("/sys/fs/cgroup/memory.current")
        return v2_limit, v2_usage
​
    # cgroup v1
    v1_limit = _read_int_file("/sys/fs/cgroup/memory/memory.limit_in_bytes")
    if v1_limit is not None:
        # cgroup v1 在无限制时返回接近 PAGE_COUNTER_MAX 的哨兵值
        if v1_limit >= (1 << 62):
            return None, None
        v1_usage = _read_int_file("/sys/fs/cgroup/memory/memory.usage_in_bytes")
        return v1_limit, v1_usage
​
    return None, None
​
​
def get_memory_node_info(node_id: int = 0) -> MemoryNodeInfo:
    # ... 原有逻辑计算 total_memory 和 available_memory ...
​
    # 在返回前根据 cgroup 限制钳制内存值
    cgroup_limit, cgroup_usage = get_cgroup_memory_limit()
    if cgroup_limit is not None and cgroup_limit < total_memory:
        total_memory = cgroup_limit # 总内存不能超过 cgroup 限制
        cgroup_available = cgroup_limit - (cgroup_usage or 0)
        available_memory = max(0, min(available_memory, cgroup_available))
​
    return MemoryNodeInfo(
        total_memory=total_memory,
        available_memory=available_memory,
    )

评论区精华

是否使用 cgroup/memory.numa_stat 设计

bigPYJ1151 建议使用 `cgroup/memory.numa_stat` 获取每 NUMA 节点的内存限制,但 maobaolong 尝试后发现 cgroup 不提供每 NUMA 节点限制 / 空闲,只能近似分配。

结论:弃用 numa_stat 方案,仅对总内存进行全局钳制。 · 已解决

风险与影响

  1. 线性回归风险低:变更仅在 get_memory_node_info 末尾增加钳制逻辑,不影响裸机(cgroup无限制时 get_cgroup_memory_limit 返回 (None, None))。
  2. cgroup路径不存在的异常处理_read_int_file 捕获 OSErrorValueError 返回 None,路径不存在时不会崩溃。
  3. cgroup v1哨兵值判断:使用 >= (1 << 62) 排除无限大值,与内核 PAGE_COUNTER_MAX 一致。
  4. available_memory 计算保守:取 min(available_memory, cgroup_available) 且最低为0,确保不超额分配。
  5. 无单元测试:虽手动验证充分,但缺少自动化测试,未来重构可能导致回归。

影响范围:仅限Linux平台CPU后端在容器内运行且设置了cgroup内存限制的场景。裸机、macOS、GPU后端不受影响。
影响程度:对受影响用户而言是功能性修复,解决容器内启动崩溃。对未受影响用户无行为变化。
团队影响:无,仅修改一个文件。

核心路径变更 手动测试为主 cgroup 路径依赖

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论