GPU 调度与弹性伸缩:K8s 上部署大模型推理服务的实战陷阱
GPU 调度与弹性伸缩:K8s 上部署大模型推理服务的实战陷阱
一、GPU 利用率 15% 的尴尬:大模型推理上云的真实困境
将大模型推理服务部署到 Kubernetes 集群,听起来是标准的云原生操作。实际落地后,却发现 GPU 利用率长期徘徊在 15%-20%,单张 A10 卡只能服务 2-3 路并发,而推理延迟 P99 已经飙到 8 秒。更头疼的是,流量高峰时 HPA 触发扩容,新 Pod 因为 GPU 资源不足而 Pending,流量低谷时缩容又把正在处理请求的 Pod 杀掉,用户端直接 502。
这些问题的根源在于:K8s 原生的调度器和 HPA 是为 CPU 密集型无状态服务设计的,对 GPU 这种昂贵的、不可超卖的资源缺乏精细化调度能力。大模型推理服务既有 GPU 资源独占的需求,又有流量波动带来的弹性伸缩需求,两者之间存在根本性冲突。
本文从 GPU 资源模型、调度策略、弹性伸缩三个维度,拆解 K8s 上部署 AI 推理服务的工程实践。
二、K8s GPU 调度模型与推理服务生命周期
K8s 通过 Device Plugin 机制将 GPU 资源暴露为可调度的扩展资源(如 nvidia.com/gpu: 1)。调度器在 Pod 分配阶段检查节点上可用的 GPU 数量,但调度完成后,GPU 资源就被独占,直到 Pod 删除。
flowchart TD
A[用户请求到达 Ingress] --> B[推理服务 Pod 接收请求]
B --> C{GPU 显存是否充足?}
C -->|是| D[加载模型到 GPU / 复用已加载模型]
D --> E[执行推理计算]
E --> F[返回结果]
C -->|否| G[请求排队等待 / 返回 503]
G --> H[HPA 检测到排队延迟上升]
H --> I{集群有空闲 GPU 节点?}
I -->|是| J[调度新 Pod 到 GPU 节点]
I -->|否| K[Pod 处于 Pending 状态]
K --> L[Cluster Autoscaler 扩容节点]
L --> M[新节点就绪后 Pod 启动]
M --> J
J --> N[新 Pod 模型加载 冷启动 30-120s]
N --> D
style K fill:#ff6b6b,color:#fff
style G fill:#ffa94d,color:#fff
style N fill:#ffd43b,color:#333
上图揭示了三个关键瓶颈:
- GPU 独占与碎片化:一个 Pod 声明
nvidia.com/gpu: 1就独占整张卡,即使只用了 30% 显存,剩余 70% 也无法被其他 Pod 使用。 - 冷启动延迟:新 Pod 启动后需要将模型权重从磁盘加载到 GPU 显存,大模型(7B/13B)的加载时间可达 30-120 秒,期间无法处理任何请求。
- 缩容破坏性:HPA 缩容时直接删除 Pod,不会等待推理请求完成,导致正在处理的请求被中断。
三、生产级 GPU 推理服务部署方案
3.1 多实例 GPU 分割与时间片复用
# 使用 NVIDIA MIG 将 A100 切分为多个实例,提升 GPU 利用率
# 适用于推理延迟敏感度较低、并发量较高的场景
apiVersion: v1
kind: ConfigMap
metadata:
name: mig-partition-config
data:
partition.yaml: |
# A100 40GB 切分为 2 个 20GB 实例——每实例可独立运行一个 7B 模型
# 设计意图:单卡双实例,将 GPU 利用率从 15% 提升到 50%+
version: v1
mig-configs:
all-2g.20gb:
- devices: all
mig-enabled: true
mig-devices:
2g.20gb: 2
---
# Pod 声明使用 MIG 实例而非整卡
apiVersion: apps/v1
kind: Deployment
metadata:
name: llm-inference-mig
spec:
replicas: 2
selector:
matchLabels:
app: llm-inference
template:
metadata:
labels:
app: llm-inference
spec:
containers:
- name: vllm
image: vllm/vllm-openai:v0.6.0
resources:
limits:
# 声明 MIG 实例而非整卡——避免独占整张 GPU
nvidia.com/mig-2g.20gb: 1
requests:
nvidia.com/mig-2g.20gb: 1
env:
# vLLM 的 PagedAttention 机制——减少显存碎片,提升并发能力
- name: VLLM_GPU_MEMORY_UTILIZATION
value: "0.85"
3.2 基于推理队列深度的 HPA 策略
# 自定义 HPA 指标——基于推理队列深度而非 CPU 利用率
# 原因:GPU 推理服务 CPU 利用率极低,无法反映真实负载
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: llm-inference-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: llm-inference
minReplicas: 2
maxReplicas: 8
metrics:
# 核心指标:推理请求队列深度
# 队列深度 > 5 说明当前 Pod 已无法及时处理请求,需要扩容
- type: Pods
pods:
metric:
name: vllm_num_requests_waiting
target:
type: AverageValue
averageValue: "5"
behavior:
scaleDown:
# 缩容稳定窗口设为 5 分钟——避免流量波动导致频繁缩容
stabilizationWindowSeconds: 300
# 每次最多缩 1 个 Pod——减少缩容对在线请求的冲击
policies:
- type: Pods
value: 1
periodSeconds: 60
scaleUp:
# 扩容可以更激进——推理请求堆积时需要快速增加处理能力
policies:
- type: Pods
value: 2
periodSeconds: 30
3.3 优雅缩容:保护推理中的请求
"""
推理服务优雅退出处理器——确保缩容时不中断正在处理的请求
设计意图:K8s 发送 SIGTERM 后,给服务一个宽限期完成推理
"""
import signal
import sys
import time
from threading import Event
# 全局标志:收到终止信号后,拒绝新请求但继续处理已有请求
shutting_down = Event()
def graceful_shutdown(signum, frame):
"""收到 SIGTERM 后的优雅退出逻辑"""
shutting_down.set()
# 等待所有进行中的推理请求完成
# 超时后强制退出,避免 Pod 被 SIGKILL
timeout = 120 # 大模型推理可能需要较长时间
start = time.monotonic()
while active_requests_count() > 0:
if time.monotonic() - start > timeout:
print(f"[WARN] 优雅退出超时,仍有 {active_requests_count()} 个请求未完成")
break
time.sleep(1)
sys.exit(0)
def active_requests_count():
"""返回当前正在处理的推理请求数量"""
# 实际实现中从推理引擎获取
return _inference_engine.pending_count
signal.signal(signal.SIGTERM, graceful_shutdown)
四、GPU 推理上云的架构权衡
4.1 MIG 分割 vs. 时间片复用 vs. 整卡独占
| 方案 | GPU 利用率 | 推理延迟 | 适用场景 |
|---|---|---|---|
| 整卡独占 | 低(15%-30%) | 最低 | 延迟敏感的在线推理 |
| MIG 硬分割 | 中(40%-60%) | 低 | 多模型同卡部署 |
| MPS 时间片 | 高(60%-80%) | 不稳定 | 离线批处理、非实时推理 |
| vLLM Continuous Batching | 高(70%-90%) | 中 | 高并发在线推理(推荐) |
4.2 HPA 扩容的冷启动陷阱
扩容新 Pod 后,模型加载需要 30-120 秒。在此期间,新 Pod 无法处理请求,流量全部压在已有 Pod 上。如果已有 Pod 也已满载,用户请求会持续超时。
缓解方案:
- 预热池:维持 1-2 个已加载模型的备用 Pod,HPA 扩容时直接从预热池取 Pod,跳过冷启动。
- 预测性扩容:基于历史流量模式提前扩容,而非等队列堆积后再响应。
- 模型权重预加载:使用
initContainer将模型权重从对象存储下载到本地 SSD,减少 Pod 启动后的加载时间。
4.3 禁用场景
- 超大规模模型(70B+):单卡显存不足,需要张量并行,MIG 和时间片方案均不适用。
- 严格延迟要求(P99 < 200ms):任何形式的资源复用都会引入延迟抖动,必须整卡独占。
- GPU 节点池不足:如果集群只有 2-3 张 GPU 卡,弹性伸缩没有意义,固定副本数更稳定。
五、总结
大模型推理服务上 K8s,核心矛盾是 GPU 资源的独占性与流量的弹性需求之间的冲突。解决思路:
- 提升单卡利用率:优先使用 vLLM 的 Continuous Batching 机制,配合 PagedAttention 减少显存碎片,单卡并发能力可提升 3-5 倍。
- 精细化 HPA 策略:基于推理队列深度而非 CPU 利用率触发扩缩容,缩容时设置稳定窗口和优雅退出,避免中断在线请求。
- 冷启动治理:通过预热池或预测性扩容,将扩容响应时间从分钟级压缩到秒级。
- GPU 资源模型选择:根据延迟要求和并发量,在 MIG 分割、时间片复用、整卡独占之间做取舍,不要一刀切。
落地路线:先用 vLLM + Continuous Batching 验证单卡并发能力;再配置基于队列深度的 HPA;最后引入预热池解决冷启动问题。每一步都用压测数据验证效果,避免过度设计。
更多推荐




所有评论(0)