GPU 集群越来越像航空公司的机队:采购只是开始,真正决定经济性的,是设备有多少时间在执行有效任务。已经分配却长期空闲的 GPU,就像停在机坪上的飞机,不但没有产出,还持续占用机位、电力、运维预算和折旧成本。
由于来源只给出了“空闲 GPU 如同停飞飞机”这一核心命题,下面不推断特定产品或原文数据,而是围绕这个命题给出一套可以落地的诊断与治理方法。
“已分配”不等于“正在工作”
GPU 管理最容易出现的误判,是把资源分配率当成实际利用率。例如,一个 Kubernetes Pod 申请了 4 张 GPU,调度器会认为这些设备已经被占用;但容器可能仍在下载数据、执行 CPU 预处理、等待人工连接 Notebook,甚至已经异常卡住。
因此,至少要区分三组指标:
- 分配指标:多少 GPU 已经绑定到作业或租户。
- 活动指标:GPU 核心利用率、显存占用、功耗和活跃进程。
- 产出指标:训练步数、推理请求量、Token 吞吐量、任务完成时间或实验成功率。
显存占满也不代表计算繁忙。框架可能预留显存,但 GPU 核心利用率依然很低。反过来,短小而密集的推理请求会让瞬时利用率剧烈波动,单次采样也无法说明问题。治理时应观察时间窗口,例如 15 分钟或 1 小时的平均值与分位数,而不是只看某一秒的快照。
先做一份可解释的空闲清单
在安装完整监控系统之前,可以先用 nvidia-smi 建立基线。下面的脚本每 10 秒采样一次,并输出时间、GPU 编号、核心利用率、显存使用量和功耗:
#!/usr/bin/env bash
set -euo pipefail
INTERVAL="${INTERVAL:-10}"
while true; do
nvidia-smi \
--query-gpu=timestamp,index,name,utilization.gpu,memory.used,memory.total,power.draw \
--format=csv,noheader,nounits
sleep "$INTERVAL"
done
保存为 sample-gpus.sh 后运行:
chmod +x sample-gpus.sh
INTERVAL=10 ./sample-gpus.sh | tee gpu-samples.csv
这份数据适合快速排查,但生产环境还需要关联作业身份。否则团队只能知道“第 3 张卡很闲”,却不知道是谁申请的、属于哪个项目、是否正处于合法的数据准备阶段。
在 Kubernetes 中,可以同步查看 GPU 申请及节点分布:
kubectl get pods --all-namespaces -o custom-columns='NAMESPACE:.metadata.namespace,POD:.metadata.name,NODE:.spec.nodeName,GPU:.spec.containers[*].resources.requests.nvidia\.com/gpu,PHASE:.status.phase'
可以进一步把 GPU 指标送入 Prometheus,并按 namespace、workload、team 和 cost_center 聚合。关键不是堆更多仪表盘,而是让“物理设备活动”能够映射到“业务负责人”。
用回收规则替代人工追问
发现空闲只是第一步。若每次都靠平台团队在聊天工具里询问资源是否还能使用,管理成本很快会超过节省的算力成本。更可靠的方式是明确资源生命周期:
- 交互式开发环境必须设置最长运行时间和空闲超时。
- 批处理任务应具备失败退出、重试上限和完成后自动清理。
- 长时间低利用率的独占 GPU 应触发告警,并允许负责人申请豁免。
- 没有所有者、项目标签或成本中心的任务不进入昂贵队列。
- 回收动作先通知、后终止,并保存日志与必要的检查点。
下面是一个可改造的 Kubernetes Job 示例。它通过 activeDeadlineSeconds 限制作业最长运行时间,通过 ttlSecondsAfterFinished 在完成后自动清理对象:
apiVersion: batch/v1
kind: Job
metadata:
name: gpu-training-demo
labels:
team: vision
cost-center: research
spec:
activeDeadlineSeconds: 14400
ttlSecondsAfterFinished: 1800
backoffLimit: 1
template:
metadata:
labels:
team: vision
workload: training
spec:
restartPolicy: Never
containers:
- name: trainer
image: nvcr.io/nvidia/pytorch:24.01-py3
command: ["python", "-c"]
args:
- |
import torch
assert torch.cuda.is_available()
x = torch.randn(4096, 4096, device="cuda")
for _ in range(100):
x = x @ x
print(torch.cuda.get_device_name(0))
resources:
limits:
nvidia.com/gpu: 1
应用前需要集群已安装 NVIDIA 设备插件,并将镜像版本调整为组织批准的版本:
kubectl apply -f gpu-job.yaml
kubectl logs -f job/gpu-training-demo
超时不应设置成一个随意的统一数字。训练、批量推理、Notebook 和模型评测的运行特征不同,应该分别定义策略,并为确实需要长时间占用的任务保留审批机制。
提高利用率不能只靠“塞满”
治理空闲 GPU 并不意味着追求每分钟 100% 核心利用率。数据加载、检查点保存、分布式同步和推理流量波动都会制造合理的空档。过度压缩余量可能增加排队时间,让在线服务失去突发容量,甚至使多个共享任务互相干扰。
可采用的手段包括:
- 将短任务聚合为批次,减少启动和镜像拉取开销。
- 优化数据管道,避免 GPU 等待 CPU、网络或对象存储。
- 对可共享的推理或开发任务采用时间切片、MPS 或适当的硬件分区能力。
- 使用优先级与抢占机制,让低优先级实验填补空闲容量。
- 将交互式环境与稳定的生产推理隔离,避免共享策略破坏延迟目标。
这些手段都有边界。共享会降低隔离性,抢占要求任务能够恢复,批处理会增加单请求等待时间,硬件分区也会限制可用的显存和计算规模。真正需要优化的是单位业务产出的基础设施成本,而不是单一的 GPU 利用率数字。
从小范围策略开始
一套务实的采用顺序是:先连续采集两周数据,再识别“已分配但低活动”的主要来源;随后为任务补齐团队、项目和成本标签;然后只对最明确的浪费场景启用通知与自动回收;最后再评估共享、抢占和容量规划。
上线前可以检查以下事项:
- 指标是否同时覆盖分配、活动和业务产出。
- 每个 GPU 作业是否都有明确负责人。
- 空闲判定是否使用了合理的时间窗口。
- 自动终止前是否通知,并允许有限期豁免。
- 长任务是否支持检查点和恢复。
- 在线服务是否保留了经过容量测试的突发余量。
- 节省的成本是否超过监控、调度和运维复杂度。
GPU 不产生价值的原因,通常不是设备本身不够快,而是任务生命周期、可观测性和责任归属没有连起来。先让每一张卡的状态可解释,再谈提高密度,才能把“停在机坪上的资产”变成稳定、可调度的计算能力。