“又要不到卡”和“卡又在闲着”,这两件事为什么会同时在 AI 团队里发生?很多团队的 GPU 利用率,看起来忙得不行,真正跑进计算单元的任务却没几个。可一旦找管理员申请新的 A100/H100 资源,排队周期照样一两个星期起。
这不是资源太少的问题,而是算力没有被“组织”起来。
AI 算力走到今天,瓶颈早就从“芯片造了多少”转移到了“资源能不能高效地分出去、接回来、调度好”。再先进的 GPU,如果只能在某一个框架、某一个角色、某一个时间窗口里跑,那算力就是片面的高,而不是可用的高。这篇文章不解决“怎么多买几张卡”的问题,只围绕一个更工程化的问题展开:在卡稀缺、任务多样、平台各异的现实环境里,谁能重新组织算力,以及我们该用什么方式搭出这套调度能力。
1. 一卡难求与算力闲置为什么会在同一个机房同时发生
先说一个很典型的场景。某个做 AI 应用开发的团队,手上有 4 台 8 卡 GPU 服务器。每天上午,训练组把两个大模型任务放到其中 3 台上跑,显存占满,看起来毫无空闲。可你想把自己的推理服务也塞进去,却发现“进不去”。
卡都被占了,对吧?可你跑上去看,训练任务实际在做的只是反复读数据集、做数据增强、写 checkpoint,GPU 上的计算单元大部分时间在等待数据喂进来。用nvidia-smi看,显存占用到 90%,但 GPU 利用率可能只有 40% 甚至更低。与此同时,旁边另一台机器上有两个老项目长期挂着 PID,GPU 利用率只有个位数,却没人敢释放,因为那是“别人跑的正式任务”。
这里存在三个很现实的原因:
第一,资源申请粒度太粗。 很多团队申请 GPU 时只说“我要 8 卡”,可任务真的一定能用满 8 卡吗?数据并行能做到 8 卡线性扩展,通常只存在于教科书里。卡多的时候,通信、数据读取、代码里某个串行瓶颈都会被放大,真正用满的可能是 6 卡、5 卡,剩下几张只是“挂着同一张任务单”而已。
第二,任务时有潮汐性。 模型训练有高峰低谷,白天大家评审、调参、试跑,晚上资源可能空着;线上推理则相反,白天用户请求多,晚上流量下降。如果一个平台只能按“长期占用”的方式分卡,那么潮汐性的空缺都会变成闲置。
第三,算力被绑定在一个固定技术栈上。 同一个节点里,有人用 PyTorch、有人用 TensorFlow、有人用某个团队的私有镜像。容器化没有普及的机房,任务隔离靠人来协调,中间只要换一次驱动、升一次 CUDA,就能让一台机器“荒废”半天。算力是存在的,但没法被快速分发到需要它的任务上。
所以,“一卡难求”很多时候不是绝对意义上的物理资源不足,而是组织方式不足。算力作为一个名词听起来很宏大,落到工程上,就是“谁能把任务送进去、谁能在空闲时自动补位、谁能在任务结束后立刻释放资源”。补齐这套能力,比到处找卡更紧迫。
2. 重新“组织”算力到底要解决什么
想要重新组织算力,先要看清算力的真实结构。普通开发者接触最多的是“显卡”和“GPU 服务器”,但它们不是同一个东西。显卡是硬件载体,GPU 是硬件里的运算核心,真正可被调度的是显存、计算单元、带宽三者的组合。
可以做一个简单类比:CPU 是全能杂工,什么活都能干,但大量重复的矩阵乘法交给它做就很浪费;GPU 是流水线工人,擅长把大批量同质化计算任务摊开同时做。AI 模型训练里的卷积、矩阵乘、张量运算,正好落在 GPU 的舒适区。
当一个任务说自己“需要一张卡”时,它说的是什么?拆开看有三个约束:
- 显存容量:模型参数、激活值、优化器状态能放得下吗?
- 算力强度:模型训练或者推理的总计算量有多大?
- 通信带宽:多卡并行时,卡和卡之间数据要频繁交换吗?
很多人把“显存够不够”和“算力够不够”混在一起,结果调度策略只盯着显存。真正做任务调度时,要同时看这几层。
与此同时,工程领域动不动把“算力”和“不同资源”混为一谈。在线模型服务最常说的“ token 数量”也不是算力,而是计费口径:一次调用消耗了多少输入输出字符,底层对应多少计算量和显存占用,但不同模型、不同显卡、不同批大小时,同样的 token 消耗的资源完全不同。至于 API,只是算力的访问入口。用一句话总结:算力是物理资源,token 是计量方式,API 是服务接口。
在做集群调度系统时,只有先把这个关系理清楚,你才知道系统需要抽象什么、上报什么、计量什么。以 GPU 显存为例,一张卡 80GB,调度器记录 80GB 总量和已用量,这只是第一步。如果两个任务卡在同一张卡的同一块显存地址区域,调度算法不做实时感知,还是会出现“看着没占满,任务却起不来”的假性满载。显存碎片、计算单元占用、显存带宽争用,都是调度系统需要采集的状态。
这也是算力“组织”起来最难的地方:GPU 任务和 CPU 任务的任务模型很不一样。CPU 任务通常几秒到几分钟,做完就退出;GPU 训练任务可能跑几小时甚至几天,而且中间会一直占住显存。推理任务则是弹性流量,白天多晚上少,要求延迟低,却允许少量请求排队。一个完整的算力调度系统,必须同时接纳长任务和短任务、持续任务和波峰任务,并根据不同任务特征设计不同的分配与抢占策略。
3. 从硬件到平台:算力编排的四层路径
重新组织算力的技术路线不是单一方案,而是分层推进的。实践中,可以从硬件层、驱动层、编排层、服务层四个维度来理解。
| 层级 | 代表技术 | 解决什么问题 | 适用者 |
|---|---|---|---|
| 硬件级切分 | NVIDIA MIG、vGPU 等 | 一张物理卡切成多个独立实例 | 硬件管理员 |
| 驱动与运行时 | NVIDIA GPU Operator、容器运行时 | 统一驱动、自动部署 GPU 环境 | 平台工程师 |
| 编排调度 | Kubernetes、Slurm、Ray | 按队列、按资源量调度任务 | 集群管理员 |
| 服务层 | vLLM、TensorRT-LLM、推理网关 | 提高推理吞吐,把请求装进一个卡池 | 模型服务开发者 |
在这四层里,每一层都在回应“缺卡和闲置并存”的不同侧面。
硬件级切分解决的是“大卡装小任务”的问题。比如一张 80GB 的高端卡,跑一个只需要 10GB 显存的小模型,等于浪费了 70GB。MIG(Multi-Instance GPU)允许把一张物理 GPU 切成多个实例,彼此显存和计算单元隔离,A100、H100 等卡都能支持。不过,MIG 能切几份、每份多大,不同卡的规格差异很大,一定要先查硬件规格。
驱动与运行时层解决的是“明明有卡,任务跑不起来”的问题。GPU 不是接上电就能用,需要对应的驱动、CUDA 运行库、容器运行时支持。NVIDIA GPU Operator 这类工具把驱动安装、运行时配置和 device plugin 统一管理起来,在 Kubernetes 集群里能减少大量人工踩坑。
编排层解决的是“任务应该去哪里跑”的问题。Kubernetes 把 GPU 声明为可调度的资源,让任务像申请 CPU 和内存一样申请显卡;Slurm 则更适合传统的 HPC 和高性能计算团队,队列管理更成熟。如果团队里有人同时在跑数据并行训练、微调、推理服务,Kubernetes 的灵活性会更高。
服务层解决的是“模型部署后怎么把并发吃满”的问题。一个部署在 GPU 上的大模型服务,如果来一个请求算一次,GPU 利用率很低。vLLM、TensorRT-LLM 这类推理引擎支持 continuous batching(连续批处理),把多个请求动态组合到一个批次里,吞吐可以比普通逐条请求方式高出一个量级。
这四层不是互相替代,而是要组合使用。对大多数开发者来说,最有体感的是从单机多卡管理和 Kubernetes 调度开始,先把现有资源“转起来”,再往更细的切分和推理优化方向走。
4. 关键实操基础:先学会查看真实 GPU 状态
在设计任何调度方案之前,先学会看显卡。很多人对“GPU 利用率低”的判断,是凭感觉。开机用灯看了没跑,就说不忙;显存满了,就说卡被占满。这两个判断都可能对,但不能只凭感觉下结论。
第一件事,从命令行查看 GPU 状态。最常用的命令是nvidia-smi:
nvidia-smi这条命令会列出所有 GPU 的温度、利用率、显存占用、运行进程。示例输出简化后大致是这样:
+-----------------------------------------------------------------------------+ | NVIDIA-SMI 545.23.08 Driver Version: 545.23.08 CUDA Version: 12.3 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 NVIDIA A100-PCIE-40GB On | 00000000:3B:00.0 On | 0% | | 40% 61C P0 178W / 250W | 32768MiB / 40960MiB | 88% Default | +-------------------------------+----------------------+----------------------+注意区分 Memory-Usage 和 GPU-Util。Memory 是显存占用,占用高不一定代表计算忙。GPU-Util 才表示计算单元被利用的比例。一个训练任务可能把显存占了 80%,GPU-Util 只有个位数,说明模型没有高效地批量计算,或者在做数据加载。
持续观察利用率,避免只看一秒钟的快照:
watch -n 1 nvidia-smi按字段输出更利于脚本采集:
nvidia-smi --query-gpu=index,uuid,memory.used,memory.total,utilization.gpu,utilization.memory,temperature.gpu --format=csv如果系统里有多张卡,又不能确定 PyTorch 装没装好,可以先做一次最小验证。以下脚本要求把 3 个 GPU 同时拉起测试,是排查“驱动正常但卡用不上”问题的常用方式:
#!/usr/bin/env bash # 文件路径:gpu_check.sh # 作用:分别在 0、1、2 号 GPU 上执行 PyTorch 自检 for i in 0 1 2; do CUDA_VISIBLE_DEVICES=$i python -c "import torch; print(f'GPU {i}:', torch.cuda.get_device_name(0), '可用=', torch.cuda.is_available())" & done wait echo "并行 GPU 检查完成"在这个脚本里,CUDA_VISIBLE_DEVICES=$i是关键。它让当前进程只能看到指定的那块 GPU;后台并行执行 3 个进程后,就能同时验证多张卡是否都能被 CUDA 识别。运行后看到类似输出:
GPU 0: NVIDIA A100-SXM4-40GB 可用= True GPU 1: NVIDIA A100-SXM4-40GB 可用= True GPU 2: NVIDIA A100-SXM4-40GB 可用= True只要有一张卡报available=False,优先级最高的排查方向是驱动和 CUDA 的匹配关系,然后才是 PyTorch 编译时用的 CUDA 版本。记住一条原则:驱动版本要先于 CUDA 运行时确认,显卡驱动和 CUDA 版本不匹配,PyTorch 装得再顺利也一样识别不了卡。
下面再看一个核心环境变量:CUDA_VISIBLE_DEVICES。它的作用是控制当前进程能“看见”哪些物理 GPU。物理 ID 不是一成不变的,服务器重启、搬动 PCIe 插槽都可能让卡的编号变化,所以脚本里最好用nvidia-smi -L配合 UUID 确认卡身份。
5. 单机多卡调度:算力最小可运维单位
如果说围绕 GPU 的一切调度最终要落到一个基本动作上,这个动作就是“怎么把不同的任务安排到不同的 GPU 上”。这是算力组织的最小单元。单机多卡是绝大多数团队最先遇到的形态:一台 8 卡服务器,多人共用,没有调度平台的时候,全靠口头协商。
做得差的团队是这样管理的:谁要跑任务,直接在机器上设CUDA_VISIBLE_DEVICES=2,跑完也不清进程;后面的人看显存满了,又找不到占用方,只能找另一张卡。时间久了,卡的使用情况无人知道,这种状态本质上是“没组织”。
单机多卡调度的第一步是给每一张卡明确身份和用途。建议为机器设置两张表:一张记录物理卡和 UUID 对应关系,一张记录卡归属。这个工作粒度很小,但能减少一大半无谓争论。
代码层面的调度方式主要靠环境变量。例如 PyTorch 训练脚本可以这样指定使用两张卡:
export CUDA_VISIBLE_DEVICES=0,1 python train.py在 Python 脚本内部也可以读取设置:
# 文件路径:config/gpu_env.py import os os.environ["CUDA_VISIBLE_DEVICES"] = "0,1"如果你想让不同容器使用不同 GPU,Docker 或容器运行时是更干净的隔离方式:
docker run --gpus '"device=0"' --name task-a nvidia/cuda:12.0.0-base-ubuntu22.04 nvidia-smi docker run --gpus '"device=1"' --name task-b nvidia/cuda:12.0.0-base-ubuntu22.04 nvidia-smi再配合--shm-size、--ulimit memlock等参数,两个任务就同时跑在物理上互不干扰的两张卡上了。
值得多说一句的是 Ollama 这类本地模型工具。很多人在一台多卡机器上同时跑多个模型,但是默认情况下它会自动挑选卡,导致几个模型挤在同一张卡上,别的卡空转。想让 Ollama 使用指定 GPU,可以设置环境变量:
# 只让 Ollama 看到 0、2 号 GPU export CUDA_VISIBLE_DEVICES=0,2 ollama serve每次运行模型前,先确认一下模型的显存占用规划:
ollama ps如果 Ollama 的调度不理想,也可以用OLLAMA_SCHED_SPREAD让模型尽可能分散到不同 GPU 上:
export OLLAMA_SCHED_SPREAD=true这个变量表示:当多个模型需要加载时,优先分散到不同设备,而不是堆在同一块卡上。对单机多模型共存的场景非常有用。
单机多卡调度的下一步是“显存感知”。当你有多个任务要部署在同一台机器上,不是随便指定一张卡就行。建议把每一张卡的显存余量纳入决策:如果任务需要 24GB 显存,而卡 0 只剩 10GB,卡 1 还有 30GB,那就应该选择卡 1。这个过程看起来简单,但人工操作很容易出错。要更可靠,可以写一个小脚本巡检显存,把结果输出成可读报告。它能做的只是辅助,真正自动分配要交给集群调度器。
6. 集群级算力编排:Kubernetes + GPU Device Plugin 实践
单机多卡再多,也只是机房里的几个“信息孤岛”。想真正把算力变成统一资源池,主流方案是 Kubernetes 加 NVIDIA Device Plugin。这套组合能实现的效果是:训练任务像申请 CPU、内存一样申请 GPU,跑完自动释放,节点故障后自动重新调度。
先解释一个关键组件 Device Plugin 是什么。Kubernetes 原本不认识 NVIDIA GPU。Device Plugin 是一种扩展机制,由 NVIDIA 提供一个 DaemonSet 组件,在集群的每个 GPU 节点上运行,负责把节点的 GPU 数量、类型、健康状况上报给 Kubernetes。上报之后,普通 Pod 才能通过resources.limits声明“我要一块 GPU”。
实际搭建时,通常分三步。
第一步,确定 GPU 节点已经准备好了 NVIDIA 驱动、nvidia-container-toolkit 和 Kubernetes 组件。这一部分如果环境不统一,很容易翻车。生产环境更推荐用 NVIDIA GPU Operator 统一编排驱动、运行时和 Device Plugin,因为它能让节点从加入集群到可用 GPU 的流程自动化很多。
第二步,在 GPU 节点上运行 Device Plugin。常见做法是直接加载官方 DaemonSet 清单:
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/main/nvidia-device-plugin.yml生产环境不建议直接使用 main 分支,最好固定到正式 release 版本。安装后查看 Pod 是否正常运行:
kubectl get pods -n kube-system -l name=nvidia-device-plugin第三步,提交一个显式申请 GPU 的测试 Pod:
# 文件路径:k8s/gpu-pod.yaml apiVersion: v1 kind: Pod metadata: name: gpu-check-pod spec: restartPolicy: OnFailure containers: - name: cuda-check image: nvidia/cuda:12.0.0-base-ubuntu22.04 command: ["nvidia-smi"] resources: limits: nvidia.com/gpu: 1提交并观察:
kubectl apply -f k8s/gpu-pod.yaml kubectl get pods kubectl logs gpu-check-pod如果日志里能看到完整的 GPU 信息列表,说明这个 Pod 真的拿到了一块物理 GPU。此时再查看节点资源,能看到nvidia.com/gpu的已分配数量:
kubectl describe node <gpu-node-name> | grep -A 10 "Capacity"在 Kubernetes 的 GPU 调度中,有一个非常容易误解的点:nvidia.com/gpu默认是整卡调度。也就是说,你申请1,Kubernetes 会给你一整张卡,Pod 里能看到的 GPU 数量是 1;你申请2,它会尽量把同一节点的两块卡给你,但如果你需要两块卡必须在同一台物理机上通信,要配合节点选择器和亲和性做约束。
如果你想把一块大卡拆成多个小实例来进行共享,有几种做法:一是在硬件层面使用 MIG 把 A100/H100 这样的卡切分成几个 GPU 实例;二是使用支持时间片调度的 Device Plugin;三是引入第三方 GPU 池化方案。不同的方案对应不同的隔离等级和性能损失,不是越花哨越好,要根据安全边界和任务特点选型。
在 YAML 里通过环境变量进一步约束容器使用哪些 GPU,也是常见需求。这里的约束有两层:外层是 Kubernetes 调度的资源约束,内层是容器运行时的 NVIDIA_VISIBLE_DEVICES 可见性约束。
# 文件路径:k8s/gpu-env-pod.yaml apiVersion: v1 kind: Pod metadata: name: gpu-env-demo spec: restartPolicy: OnFailure containers: - name: cuda-env-demo image: nvidia/cuda:12.0.0-base-ubuntu22.04 command: ["sh", "-c", "nvidia-smi && sleep 30"] env: - name: NVIDIA_VISIBLE_DEVICES value: "0,1" resources: limits: nvidia.com/gpu: 2这段配置里的NVIDIA_VISIBLE_DEVICES是容器运行时会去识别的环境变量,它控制容器内部能看见的物理 GPU 编号;nvidia.com/gpu则是 Kubernetes 调度器的资源声明。需要特别注意的是,物理编号和容器内看到的编号不一定一致。容器里的 GPU 编号从 0 开始重新排列,所以不要在容器内写死依赖物理卡号。
7. 训练与推理并存:AI 应用与 Agent 场景下的算力服务化
算力组织除了要管训练任务,还得管推理任务。这两年应用类 AI 越来越多,AI Agent 这类任务的特点是:实时请求、流量弹性大、每次请求需要多轮模型调用,但每个单请求的算力消耗比一次训练要小得多。如果按训练任务的思路每次拿整卡长期占住,很快就又会出现“利用率不高”却“卡全部被占”的尴尬。
解决推理场景闲置的核心是把“每个请求一个模型实例”改成“动态批处理模式”。在这种模式下,多个用户的请求会在同一个模型实例内排队,调度引擎一边收集请求,一边把能合并的计算合并成同一个 batch,在 GPU 上一起算。vLLM、TensorRT-LLM、SGLang 这些推理引擎实现的就是这个目标。
部署时并不需要每个模型都独占一张卡。一个标准的推理服务化结构是:
- 模型网关负责接收请求,做模型路由、鉴权和超时控制。
- 推理引擎管理模型实例,并支持按流量伸缩副本。
- 显存调度器记录每个模型实例占用的显存和 GPU,自动把新副本调度到还有空闲显存的卡上。
比如你有一台 8 卡服务器,白天要把 8 张卡都用于线上推理,因为流量高峰在白天;凌晨流量下降,就可以自动释放 6 张卡,把它们加入到离线训练队列。这种基于时间的弹性调度,不需要什么很贵的商业产品,普通 Kubernetes 加上自定义 CronJob 或指标触发器就能做出基础版。
对于微调任务,也不一定每次都要从零训练。用 LoRA 这类参数高效微调方法,单卡甚至低显存卡也能完成部分微调工作。这个事实意味着,一个管理良好的平台完全可以把“训练”和“推理”两类任务混合调度:推理服务优先保障延迟,微调任务则可以被抢占、暂停或者排到空闲窗口。设计任务优先级和抢占策略,比单纯增加卡数更能缓解“一卡难求”。
8. 常见问题与排查思路
算力调度遇到的故障,很多时候不是算法问题,而是环境一致性、资源可见性和版本兼容性这三类问题。这里将排障集中的情况整理成表格,便于按图索骥:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 容器里执行 nvidia-smi 失败 | 容器运行时未配置 NVIDIA Container Toolkit | 查看容器日志和 docker info | 安装并配置 nvidia-container-runtime |
| 用 PyTorch 调用 GPU 返回 False | CUDA 版本与 PyTorch 不匹配,或驱动过旧 | 运行 nvidia-smi 查看驱动版本,再查看 torch.version.cuda | 按实际驱动重新安装匹配的 PyTorch 版本 |
| K8s Pod 一直 Pending,无法调度到 GPU 节点 | 没有安装 Device Plugin 或资源标签异常 | kubectl describe pod 查看调度事件;检查节点 nvidia.com/gpu Capacity | 重新安装 Device Plugin,确认 DaemonSet 运行正常 |
| GPU 显存还有剩余,新任务 OOM | 显存碎片或 CUDA context 预占显存 | 查进程占用,确认单卡剩余连续显存是否充足 | 重启占位进程释放显存;选择显存余量更大的卡 |
| nvidia-smi 显示 GPU-Util 很低但显存占满 | 任务在读数据或存在 CPU 瓶颈 | 用 nvidia-smi dmon 观察,结合 top 查看 CPU | 优化 DataLoader,增加 num_workers,使用 NVMe 存储 |
| 多卡训练速度不升反降 | 数据并行通信开销过大 | 查看 NCCL 日志,测卡间带宽 | 使用 NVLink 拓扑更好的卡;减少小张量通信次数 |
| 多个任务同时跑在同一张卡上,互相影响 | 未设置 CUDA_VISIBLE_DEVICES 或未使用资源隔离 | 使用 nvidia-smi 查看进程所在 GPU | 容器化并为每个任务分配独立 GPU 或 MIG 实例 |
一个需要慢慢建立的意识是:系统报错时,先判断是驱动层、运行时层、调度层还是应用层的问题,不要一上来就重装系统或重刷驱动。大多数 GPU 调度故障,靠日志就能定位,真正棘手的是“现象出现在上层,根因在下层”的情况,比如任务没被调度到预期节点,最终查出来是 YAML 里标签写错,这种问题要加倍小心。
9. 算力组织的最佳实践与平台建设建议
再往下,你会发现“组织算力”已经从技术问题变成工程管理问题。结合真实环境里的经验,可以从下面几条开始建设。
第一,先统一驱动和容器镜像,再做调度。 在 GPU 资源紧张的环境里,最影响调度效率的往往不是调度算法,而是镜像不兼容。一个团队跑 PyTorch 需要 CUDA 11.8,另一个团队需要 CUDA 12.1,如果底层的驱动只支持其中一个版本,冲突马上出现。建议把基础镜像分层管理,固定 CUDA 和 cuDNN 版本,并把新版本上线前在测试节点验证。
第二,从“物理卡视角”切换到“资源池视角”。 当你还在说“这台机器归 A 组,那台机器归 B 组”时,算力不可能真正被组织起来。一个好的资源池设计,要支持多租户、配额、命名空间隔离。Kubernetes 的 ResourceQuota 能限制每个团队最多申请多少 GPU,LimitRange 能限制单 Pod 申请范围。团队之间共享一个池,但配额清晰,可以大幅减少“缺卡”部门之间的摩擦。
第三,监控必须早于调度建设。 调度做到什么程度,取决于你对资源状况的感知有多细。建议在平台层采集以下指标:
- GPU-Util 和显存使用率,按卡、按节点、按任务维度聚合。
- GPU 温度和功耗,提前发现散热或供电瓶颈。
- 任务排队时长和释放时长,找到调度策略的优化点。
- GPU 报错事件,如 Xid 错误,帮助定位硬件故障。
第四,对在线推理和离线任务采用不同策略。 离线训练和批量任务可以被抢占、排队,适合像批处理队列一样管理;在线推理任务要求稳定延迟,建议通过独立的节点池保障资源,不要让离线的显存大户抢占它们的卡。控制好资源水位,给推理服务预留安全缓冲区。
第五,安全与权限边界要明确。 GPU 资源是昂贵的公共资源,当不同团队共享一批服务器时,要确保任务只在自己的命名空间里运行,不能让它通过宿主机路径访问其他团队的模型文件或数据。配置 Pod 安全策略、镜像白名单、网络策略,都是必要的。生产环境的任何变更,都必须先在测试环境验证,并准备回滚方案。
第六,处理多人共管的 GPU 节点时,别创造“全能的 root”。 尽量使用 Kubernetes 的 RBAC 来定义哪个人能创建、删除或修改 GPU Pod。直接给每个人发 root 权限,会把整个集群变成比谁手快的实验区。真实事故里,一次误删除 checkpoint 目录的操作,比缺卡造成的损失大多了。
10. 结语:算力调度能力会成为下一个竞争点
回到最开始的问题,一卡难求和算力闲置之间,缺少的是“调度和组织”的中间层。买更多卡当然是方案,但它无法解决任务与资源之间的失配。一个能调度单卡、多卡、多机、多租户的平台,一套能监控显存、算力、温度的观测系统,再加上一份能约束任务优先级的资源策略,才是把每块 GPU 的价值真正释放出来的关键。
对个人开发者来说,可以先从两件事入手:把自己的代码写成对“CUDA_VISIBLE_DEVICES”友好、不硬编码物理 GPU 号的形态;学会用 nvidia-smi 判断任务是不是真的跑在显卡上。对团队来说,则可以从部署 Kubernetes Device Plugin、搭建资源监控开始,先把可观测性补上,再做自动调度。
AI 算力的下半场,比的是谁能把已有的卡用得更满、让任务的等待时间更短、让每一次请求尽量命中空闲资源。先看到卡在哪里,再决定任务去哪里,这就是算力“组织”的起点。