算力调度系统设计:计费、配额与抢占策略——Kubernetes多租户GPU集群的配额管控与计费实践
多租户GPU集群的算力调度,难点不在把Pod放上去,而在于如何计费、如何划配额、如何安全抢占。本文以Kubernetes 1.29与Kueue 0.8为技术底座,拆解计费口径、分层配额与抢占策略三部分,给出可落地的Prometheus采集、ResourceQuota配置与PriorityClass抢占示例,并对比不同模式下资源利用率与业务风险。
## 1. 背景与痛点:申请量计费的失真与配额僵化
在共享GPU集群中,以申请量(Request)作为计费和配额基准是常见做法。平台按Pod申请的nvidia.com/gpu数量收费,并在命名空间层用ResourceQuota封顶。实际运行中会出现两个问题:一是用户为规避扩容延迟,习惯超额申请,8卡A100只跑2卡训练;二是配额静态生效,闲时资源无法借给其他团队。
据Kubernetes官方文档,ResourceQuota只核算容器的Requests和Limits,不追踪实际用量。这意味着计费系统如果直接读取ResourceQuota,就会把闲置算力计入账单,用户付费偏高,平台资源利用率却被拉低。
从调度器角度看,kube-scheduler的Filter/Score机制主要依据Requests做节点筛选和打分,缺少对实际负载的反馈。当节点GPU已满载但Requests未满时,新Pod仍可能被调度到该节点,引发OOM或GPU显存争抢。
因此,多租户GPU集群需要一套以实际用量为计费基准、支持弹性配额、具备安全抢占能力的调度系统。下文分别讨论计费、配额与抢占三个模块。
## 2. 计费模型:基于GPU卡时与显存占用的细粒度计量
计费的核心是准确、防篡改、可对账。GPU任务通常按卡时(GPU小时)出账,也可叠加显存占用、CPU、内存和网络指标。实际用量数据来自节点级监控,而非容器内自报。
数据采集链路
- 节点侧部署 NVIDIA DCGM exporter,以100ms-1s间隔抓取 GPU 利用率、显存、功耗、温度等指标,由 Prometheus 拉取。
- 为避免高频采样导致存储膨胀,可保留1分钟窗口的原始数据,3个月后的聚合数据做降采样。
PromQL 与 recording rules 示例
# 文件:gpu-billing-rules.yamlgroups:-name:gpu_billinginterval:1mrules:# 统计每个Pod每分钟的GPU卡时(按时间片累加)-record:gpu:pod:active_secondsexpr:sum by (namespace,pod) (nvidia_gpu_active_seconds>0)# 统计显存用量(GiB)-record:gpu:pod:mem_used_gibexpr:sum by (namespace,pod) (nvidia_gpu_memory_used_bytes) / 1024 / 1024 / 1024计费模块消费 recording rules 的输出,以 namespace 和 pod 维度聚合。对于无GPU的测试Pod,可以按CPU核时计费,但需要区分资源类型。计费周期建议设置为分钟级汇总、小时级出账、日级对账,避免实时计费带来的写入压力与延迟抖动。
计费防作弊
- 容器内执行nvidia-smi可能被修改环境变量或挂载伪设备,不能作为计费数据源。
- 应使用节点内核cgroup的GPU设备统计或硬件遥测,保证数据不可篡改。
- 计费数据需保留原始时间序列,支持审计与回溯对账。
| 计费模式 | 计费基准 | 资源利用率 | 用户公平性 | 实现复杂度 |
|---|---|---|---|---|
| 按申请量计费 | Pod 的 Requests | 偏低,用户易超配 | 弱,闲置也收费 | 低 |
| 按实际用量计费 | 监控指标(卡时/显存) | 偏高,鼓励释放闲置 | 较强,多用多付 | 中 |
| 按预留额计费 | 预留配额与保底量 | 可预测,适合SLA | 强,但弹性不足 | 高 |
## 3. 配额管理:从静态ResourceQuota到弹性ClusterQueue
配额管理的目标是在多租户间划清资源边界,同时允许闲时资源被借出。Kubernetes 原生 ResourceQuota 以 Namespace 为单位做硬限制,适合团队间计费隔离;Kueue 的 ClusterQueue/LocalQueue 提供跨命名空间资源共享与排队能力。
原生 ResourceQuota 配置
apiVersion:v1kind:ResourceQuotametadata:name:team-a-gpunamespace:team-aspec:hard:requests.nvidia.com/gpu:"4"limits.nvidia.com/gpu:"4"requests.cpu:"32"requests.memory:"128Gi"persistentvolumeclaims:"10"上述配置将 team-a 的 GPU 申请量上限设为4卡,但该配额在非高峰时段不会自动释放。为了弹性,可以引入 Kueue。
Kueue 弹性配额
apiVersion:kueue.x-k8s.io/v1beta1kind:ClusterQueuemetadata:name:shared-gpuspec:namespaceSelector:{}resourceGroups:-coveredResources:["cpu","memory","nvidia.com/gpu"]flavors:-name:defaultresources:-name:"cpu"nominalQuota:100-name:"memory"nominalQuota:400Gi-name:"nvidia.com/gpu"nominalQuota:16Kueue 的 ClusterQueue 可以设置 nominalQuota 为团队保底额度,并允许团队在资源空闲时借用其余部分。任务进入队列后按优先级和公平共享规则出队,避免直接 OOM。
| 维度 | Kubernetes ResourceQuota | Kueue ClusterQueue |
|---|---|---|
| 作用范围 | 单个 Namespace | 跨 Namespace 集群级 |
| 资源借入 | 不支持 | 支持超发与借用 |
| 排队能力 | 无,直接拒绝 | 有,任务排队 |
| GPU 感知 | 通过扩展资源支持 | 支持多资源组与 flavor |
| 复杂度 | 低 | 中,需部署 Kueue 与 admission webhook |
## 4. 抢占策略:优先级、PDB与可重入任务保护
抢占是在资源紧张时,通过驱逐低优先级工作负载为高优先级任务腾出空间。Kubernetes 中通过 PriorityClass 和抢占(Preemption)实现,但默认抢占不感知业务状态,可能杀掉正在写 Checkpoint 的训练任务。
PriorityClass 示例
apiVersion:scheduling.k8s.io/v1kind:PriorityClassmetadata:name:inference-highvalue:10000globalDefault:falsedescription:"在线推理服务,尽量避免被抢占"---apiVersion:scheduling.k8s.io/v1kind:PriorityClassmetadata:name:training-lowvalue:100globalDefault:falsedescription:"离线训练任务,可被抢占"需要注意,value 数值跨集群要保持一致,且不能设置过大,以免影响系统关键 Pod。系统级组件建议使用系统 PriorityClass(system-cluster-critical)并配置 PDB。
PodDisruptionBudget 限制驱逐
apiVersion:policy/v1kind:PodDisruptionBudgetmetadata:name:training-pdbnamespace:team-aspec:minAvailable:1selector:matchLabels:app:distributed-trainingPDB 只限制自愿中断(Eviction API),不能完全阻止高优先级抢占引发的驱逐,但可降低批量中断风险。训练任务应保存周期 checkpoint,并在收到 SIGTERM 后优雅退出,kubelet 默认宽限期为30秒,可在 Pod spec 中设置 terminationGracePeriodSeconds。
| 策略 | 触发条件 | 优势 | 风险 | 适用场景 |
|---|---|---|---|---|
| 优先级抢占 | 高优先级 Pod 无法调度 | 实现简单,延迟低 | 可能误伤有状态任务 | 在线 vs 离线混合 |
| 基于SLA的抢占 | 按任务 Deadline 与剩余时间评分 | 兼顾业务时效 | 需预测任务时长 | 批处理作业 |
| 利用率触发抢占 | 节点实际利用率超阈值 | 提升节点效率 | 阈值难调,易抖动 | 超卖场景 |
| 分时复用 | 按时间段切换资源池 | 规划清晰 | 切换时需迁移任务 | 训练/推理分时 |
所有可被抢占的任务必须支持 Checkpoint 和优雅退出,否则不要纳入抢占范围。实践建议:给在线推理服务设置较高 PriorityClass,同时配置 PDB;监控抢占事件,使用 kube-state-metrics 的 kube_pod_status_unschedulable;先在非核心命名空间灰度测试。
算力调度系统设计需要同时回答三个问题:如何计费才让用户少抵触、如何配额才让资源不浪费、如何抢占才让业务不中断。建议团队先打通 GPU 监控与计费口径,再引入 Kueue 做弹性配额,最后谨慎开启抢占并配合 PDB 与 checkpoint。
数据来源:本文技术描述基于 Kubernetes 1.29 官方文档、Kueue 0.8 用户指南及 NVIDIA DCGM 指标规范。