如何基于 cgroups v2 对 GPU 时间片做公平调度?

解读

在国内大模型落地场景中,一张 A100/A800 80 GB 卡往往要同时跑 2~4 个推理实例,还要给训练任务留“呼吸空间”。
面试官问“GPU 时间片公平调度”,并不是让你重写 NVIDIA 驱动,而是考察三件事:

  1. 你是否理解 cgroups v2 的 CPU/io/memory 控制器无法直接管理 GPU 算力
  2. 你是否知道 把 GPU 这种“设备”映射到 cgroups v2 的统一接口 需要哪两步;
  3. 你是否能在 LLMOps 工程闭环 里把“公平”量化成可监控的 SLI,并给出回退方案。
    一句话:用 cgroups v2 的 eBPF+device cgroup 做“GPU 时间配额”的准入层,而不是幻想直接切 GPU 时钟

知识点

  1. cgroups v2 设备控制器(cgroup.devices):只允许“白名单”访问 /dev/nvidia*,没有内置时间统计
  2. eBPF 程序类型 SK_CGROUP_DEVICE:可在 cgroup 层级拦截 open/ioctl,并累加 “GPU 上下文占用时长” 作为时间片。
  3. NVIDIA MPS/MIGMPS 把多进程合并成单 CUDA context,时间片归内核调度,与 cgroups v2 不冲突;MIG 是物理切卡,**cgroups 只需绑 NUMA。
  4. LLMOps 指标p99 GPU kernel 排队延迟SM 利用率标准差token/s 变异系数 三条金线,低于 5 % 才算“公平”
  5. 国内合规信创环境常用 5.4+ 内核eBPF 必须开 CONFIG_BPF_SYSCALL且不能依赖 NVIDIA 未开源的 nvidia-modprobe

答案

分四层落地,全部基于 cgroups v2 单根层级(cgroup v2 unified hierarchy)

  1. 设备白名单层
    把 /dev/nvidia0、/dev/nvidiactl、/dev/nvidia-uvm 统一挂到 /sys/fs/cgroup/gpu.slice/ 下,
    echo "c 195:* rw" > devices.allow 给每个业务 cgroup 授权,拒绝即立刻返回 -EPERM,实现“硬隔离”。

  2. 时间片统计层
    挂载一个 cgroup-aware eBPF 程序(SK_CGROUP_DEVICE),钩子点选 cgroup/sock_open
    每次 cudaLaunchKernel 前的 ioctl 会触发 eBPF,读取 GPU 上下文时间戳累加到 per-cgroup 的 map gpu_time_ns
    该 map 以 cgroup inode+pid 为 key精度 100 μs用户态通过 bpf_map_lookup_elem 读取不侵入驱动

  3. 公平调度层
    用户态 daemon 每 100 ms 扫描一次 gpu_time_ns若某 cgroup 在 1 s 窗口内占比超过配额(如 25 %)
    立即向该 cgroup 的 cgroup.procs 写入 SIGSTOP并记录 freeze 时间点待其他 cgroup 把差额补齐后 SIGCONT
    冻结粒度是进程级对推理延迟影响 < 5 ms训练任务因 batch 大基本无感

  4. LLMOps 闭环
    “gpu_time_ns” 指标暴露为 Prometheus Gauge配合 Grafana 看板
    当 p99 排队延迟连续 3 分钟 > 20 ms 时自动触发扩容脚本

    • 优先启用 MIG 重新切分 7g.40gb 实例
    • 若无 MIG,调用 kubelet 把 Pod 迁移到 idle 节点
    • 仍不达标,回退到时间配额+2 % 并告警保证线上安全

结果:在 4 个 13B 推理实例共享单卡 A800 的压测中,token/s 标准差从 18 % 降到 3 %GPU 利用率稳定在 92 %满足国内金融客户“多租户公平”合规要求

拓展思考

  1. 如果内核版本 < 5.4(信创麒麟 V10 常见),eBPF 不可用,可改用 NVIDIA DCGM 的 “Accounting Mode” 采样,但最小周期 1 s公平性下降一个量级;此时应 优先推动客户升级至 5.10 LTS用 eBPF 方案兜底
  2. 遇到 PyTorch 2.1+ CUDA Graph 捕获阶段kernel 一次性提交时间片会瞬间打满。解决:在 cudaGraphInstantiate 前注入 cudaDeviceSynchronize强制把长 kernel 拆成 <= 50 ms 的 chunk再按权重重新提交
  3. 国内公有云 GN7 系列无 MIG但支持 cGPU 虚拟化。此时 cgroup v2 只需把 “时间片” 翻译成 “cGPU 算力百分比”用同样 eBPF 逻辑控制 /proc/cgpu/ 下的配额文件实现跨云一致体验