主题
⚙️ GPU 算力调度
一个人用是享受,多个人抢是灾难——算力调度让资源分配有章法
问题场景
只有一个 GPU,三个人同时要跑推理:
- 张三在微调,吃了 80% 显存
- 李四开 vLLM,OOM 了
- 王五跑个小模型,排队等到天荒地老
没有调度 = 抢资源 = 谁都难受。
调度策略对比
| 策略 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 先到先得 | 谁先来谁先用 | 简单 | 一个任务占满,后面全等 |
| 显存分区 | GPU 切成固定份额 | 公平 | 碎片化 |
| 时间片轮转 | 每个任务轮流跑 | 公平 | 切换开销大 |
| 优先级队列 | 重要任务插队 | 灵活 | 需要定义优先级规则 |
| MIG(多实例) | 硬件级分区(A100/H100) | 真正隔离 | 只有高端卡支持 |
方案一:vLLM 内置调度
vLLM 自带请求级调度,可以控制并发:
bash
# 限制同时处理的请求数
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-7B \
--max-num-seqs 8 # 最多同时处理 8 个请求
--max-num-batched-tokens 4096 # 单批最大 token这粒度是按"请求"不是按"用户",适合多人调用同一模型。
方案二:基于 API 层的优先级调度
多个模型实例 + 请求路由:
python
from fastapi import FastAPI, HTTPException
import httpx
import asyncio
from collections import deque
app = FastAPI()
# 高优先级队列
high_queue = deque()
# 普通队列
normal_queue = deque()
# 当前运行的任务
running = None
@app.post("/v1/chat/completions")
async def chat(request: dict):
priority = request.get("priority", "normal")
# 高优先级插队
if priority == "high":
high_queue.append(request)
else:
normal_queue.append(request)
# 等待轮到
result = await asyncio.wait_for(wait_for_turn(), timeout=300)
return result
async def wait_for_turn():
while True:
# 高优先级队列优先
if high_queue:
task = high_queue.popleft()
elif normal_queue:
task = normal_queue.popleft()
else:
await asyncio.sleep(0.1)
continue
# 发送到 vLLM
async with httpx.AsyncClient() as client:
resp = await client.post(
"http://localhost:8000/v1/chat/completions",
json=task,
timeout=120
)
return resp.json()方案三:MIG 硬件分区(A100/H100)
在 GPU 层面把一张卡切成多个独立实例:
bash
# 开启 MIG 模式
sudo nvidia-smi -i 0 -mig 1
# A100 80GB 切分成实例
sudo nvidia-smi mig -cgi 9,9,9,9,9,9,9 # 7个 10GB 实例
# 或
sudo nvidia-smi mig -cgi 19,19,19 # 3个 20GB 实例
# 每个实例分配独立的计算实例
sudo nvidia-smi mig -cci 0 -gi 1 # 给 GPU 实例 1 分配计算实例 0
# 容器绑定特定 MIG 实例
docker run --gpus '"device=MIG-<UUID>"' ollamaMIG 的好处是硬件隔离——一个实例 OOM 不影响其他实例。缺点:只有数据中心卡(A100/H100)支持,消费卡(4090)没有。
方案四:Kubernetes + GPU Operator
如果你有多台 GPU 机器,用 K8s 做集群调度是终极方案:
yaml
# 给推理 Pod 申请 8GB 显存
apiVersion: v1
kind: Pod
spec:
containers:
- name: vllm
resources:
limits:
nvidia.com/gpu: 1 # 1张卡
nvidia.com/gpu-memory: 8192 # 8GB 显存K8s 自动把 Pod 调度到有空闲 GPU 的节点。但学习曲线陡,5-6 台 GPU 以上才值。
显存管理技巧
检测显存使用
bash
# 实时看
watch -n 1 nvidia-smi
# 按进程排序
nvidia-smi --query-compute-apps=pid,used_memory --format=csv
# 谁在用 GPU
sudo fuser -v /dev/nvidia*释放被占用的显存
bash
# 杀掉僵尸进程(推理完没释放)
sudo fuser -k /dev/nvidia0
# 或在 Python 里手动释放
import torch
torch.cuda.empty_cache()设置显存上限
python
# 限制 PyTorch 使用的显存比例
import os
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "max_split_size_mb:128"简单场景推荐
| 场景 | 推荐方案 |
|---|---|
| 1个人1张卡 | 不需要调度,直接用 |
| 1张卡多人推理 | vLLM 限制并发数 |
| 1张卡有人微调有人推理 | 固定时段分配(白天推理、晚上微调) |
| 多张卡多个人 | 按卡分配(张三用卡0,李四用卡1) |
| 多台机器集群 | K8s + GPU Operator |
🎯 本章要点
- 调度核心:别让一个人占满 GPU 让别人干等
- 单卡多人:vLLM 限制并发或用 API 优先级队列
- MIG 硬件分区 = 真正隔离但只有 A100/H100 支持
- 多机集群才上 K8s,单机别折腾——简单方案能用就行
- 僵尸进程占显存用
fuser -k /dev/nvidia*释放
加载练习题中...