作者:互联网 时间: 2026-08-30 10:01:56
AI 云原生部署实战:大模型推理服务的 K8s 弹性调度与 GPU 资源治理的重点在于把前置条件、操作顺序和容易误判的地方分清楚。
大模型推理服务上云,账单先到。一个典型的场景:团队用 K8s 部署了 LLM 推理服务,每个 Pod 独占一张 A100 GPU,结果监控一看,GPU 利用率长期在 10%-20% 徘徊。一张 A100 每小时的成本约 3 美元,利用率 15% 意味着 85% 的钱在烧空气。

更棘手的问题在于弹性伸缩。传统微服务的 HPA 基于 CPU/内存指标自动扩缩容,但 GPU 资源无法像 CPU 那样细粒度切分。一个推理 Pod 需要整张 GPU,扩容一个 Pod 就要多一张卡,缩容一个 Pod 就释放整张卡。这种"全有或全无"的资源模型,让传统 HPA 策略直接失效。
AI 云原生部署的核心挑战不是"把模型跑起来",而是"在保证推理延迟的前提下,把 GPU 利用率拉到 60% 以上,同时实现请求级别的弹性伸缩"。本文直接给出方案。
GPU 资源治理的核心矛盾:GPU 不支持像 CPU 那样的时间片分时复用。一个 CUDA Context 一旦占用了 GPU 显存,其他进程就无法使用这部分显存。但推理场景有一个关键特征——推理是计算密集型但非持续满载的,请求间隙 GPU 大量空闲。
graph LRsubgraph 传统部署["传统部署:GPU 独占"]P1["Pod A<br/>A100 #0<br/>利用率 15%"]P2["Pod B<br/>A100 #1<br/>利用率 20%"]P3["Pod C<br/>A100 #2<br/>利用率 12%"]endsubgraph 优化部署["云原生部署:GPU 共享 + 弹性调度"]PS1["Pod A + Pod D<br/>A100 #0<br/>利用率 55%"]PS2["Pod B + Pod E<br/>A100 #1<br/>利用率 60%"]PS3["Pod C<br/>A100 #2<br/>利用率 12%<br/>缩容候选"]end传统部署 -->|GPU 共享调度| 优化部署style 传统部署 fill:#ffebee,stroke:#f44336,stroke-width:2pxstyle 优化部署 fill:#e8f5e9,stroke:#4caf50,stroke-width:2px底层机制拆解:
MPS(Multi-Process Service):NVIDIA 提供的 GPU 共享方案,允许多个 CUDA 进程在同一 GPU 上并行执行。MPS 通过共享 CUDA Context 减少上下文切换开销,但要求所有进程的显存总量不超过 GPU 总显存。适用于推理场景,不适用于训练场景。
时间片调度(Time-Slicing):NVIDIA 设备插件支持的 GPU 分时复用机制。将一张 GPU 虚拟为多个 GPU 实例,每个实例分配固定的时间片。优点是隔离性好,缺点是存在上下文切换开销,延迟敏感型推理不适用。
显存隔离(MIG - Multi-Instance GPU):A100/H100 支持的硬件级 GPU 切分方案。一张 A100 最多可切分为 7 个实例,每个实例有独立的显存和计算单元,硬件级隔离。缺点是切分后实例规格固定,灵活性差。
# nvidia-device-plugin ConfigMap:启用 GPU 共享调度# 为什么用时间片而非 MPS?时间片方案对应用透明,无需修改推理框架代码apiVersion: v1kind: ConfigMapmetadata:name: nvidia-device-plugin-confignamespace: kube-systemdata:config.yaml: |version: v1flags:migStrategy: none# 不使用 MIG,保持灵活性failOnInitError: truesharing:timeSlicing:# 将每张 GPU 虚拟为 4 个实例# 为什么是 4 而非更多?实测 4 实例时推理延迟增加约 8%,可接受# 超过 4 实例后延迟急剧上升,不可接受resources:- name: nvidia.com/gpureplicas: 4---# Device Plugin DaemonSetapiVersion: apps/v1kind: DaemonSetmetadata:name: nvidia-device-plugin-daemonsetnamespace: kube-systemspec:selector:matchLabels:name: nvidia-device-plugin-dstemplate:metadata:labels:name: nvidia-device-plugin-dsspec:tolerations:- key: nvidia.com/gpuoperator: Existseffect: NoSchedulecontainers:- name: nvidia-device-plugin-ctrimage: nvcr.io/nvidia/k8s-device-plugin:v0.14.0args: ["--config=/etc/nvidia-device-plugin/config.yaml"]volumeMounts:- name: device-plugin-configmountPath: /etc/nvidia-device-pluginvolumes:- name: device-plugin-configconfigMap:name: nvidia-device-plugin-config# 自定义指标 HPA:基于推理队列深度和 KV Cache 利用率扩缩容# 为什么不用 CPU 利用率?GPU 推理服务的瓶颈在显存和队列深度,不在 CPUapiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata:name: llm-inference-hpanamespace: ai-servingspec:scaleTargetRef:apiVersion: apps/v1kind: Deploymentname: llm-inferenceminReplicas: 2# 最少保留 2 副本,避免冷启动延迟maxReplicas: 10metrics:# 指标 1:推理请求队列深度# 为什么用队列深度?队列积压说明当前实例处理能力不足,需要扩容- type: Podspods:metric:name: inference_queue_depthtarget:type: AverageValueaverageValue: "5"# 平均队列深度超过 5 触发扩容# 指标 2:KV Cache 显存利用率# 为什么用显存利用率?显存接近满载时 OOM 风险急剧上升- type: Podspods:metric:name: kv_cache_utilizationtarget:type: AverageValueaverageValue: "80"# KV Cache 利用率超过 80% 触发扩容behavior:scaleDown:stabilizationWindowSeconds: 300# 缩容冷却期 5 分钟# 为什么设置冷却期?推理服务冷启动需要加载模型权重,频繁缩容扩容代价高policies:- type: Podsvalue: 1# 每次最多缩容 1 个 PodperiodSeconds: 120apiVersion: apps/v1kind: Deploymentmetadata:name: llm-inferencenamespace: ai-servingspec:replicas: 2selector:matchLabels:app: llm-inferencetemplate:metadata:labels:app: llm-inferenceannotations:# Prometheus 指标采集注解prometheus.io/scrape: "true"prometheus.io/port: "8080"prometheus.io/path: "/metrics"spec:containers:- name: inference-serverimage: ai-registry.internal/llm-server:v2.1.0ports:- containerPort: 8000- containerPort: 8080# metrics 端口resources:requests:nvidia.com/gpu: "1"# 请求 1 个 GPU 时间片cpu: "2"memory: "8Gi"limits:nvidia.com/gpu: "1"cpu: "4"memory: "16Gi"env:- name: MODEL_NAMEvalue: "qwen-72b-chat"- name: MAX_BATCH_SIZEvalue: "32"# 批处理大小,影响吞吐量和延迟的平衡点- name: KV_CACHE_MAX_UTILvalue: "0.85"# KV Cache 最大利用率,超过则拒绝新请求livenessProbe:httpGet:path: /healthport: 8000initialDelaySeconds: 120# 模型加载需要时间,不能太短periodSeconds: 30readinessProbe:httpGet:path: /readyport: 8000initialDelaySeconds: 90periodSeconds: 10nodeSelector:gpu-type: "nvidia-a100"# 指定 GPU 型号,避免调度到低性能节点时间片调度的延迟代价。4 实例时间片共享意味着每个实例只获得 25% 的计算时间。实测数据:独占 GPU 时 P99 延迟 80ms,4 实例共享时 P99 延迟 120ms。对延迟敏感的在线推理场景(如实时对话),这个增加可能不可接受。
MIG 切分的灵活性陷阱。A100 的 MIG 切分规格是固定的(1g.5gb、2g.10gb 等),无法根据模型大小动态调整。如果模型需要 30GB 显存,只能选择 3g.20gb 或 4g.20gb 规格,前者显存不够,后者浪费计算单元。
弹性伸缩的冷启动问题。LLM 推理 Pod 的启动时间通常在 60-120 秒(加载模型权重到 GPU)。HPA 触发扩容后,新 Pod 需要等待模型加载完成才能接收流量。在流量突增场景下,这个延迟可能导致请求超时。解决方案是预热池(Warm Pool),但预热池意味着常驻的空闲 GPU 资源,又是一笔成本。
监控指标的准确性问题。自定义 HPA 指标依赖 Prometheus 采集推理框架暴露的 metrics。如果推理框架的 metrics 采集有延迟(比如每 10 秒才更新一次队列深度),HPA 的扩缩容决策就会滞后。在高频波动场景下,这种滞后可能导致"扩容还没生效,流量已经降了"。
AI 云原生部署的核心目标是"在推理延迟和 GPU 成本之间找到最优平衡点"。落地路线建议:
GPU 共享调度:根据推理延迟要求选择共享方案。延迟不敏感场景用时间片调度(4 实例/卡),延迟敏感场景用 MIG 硬件切分。弹性伸缩策略:基于推理队列深度和 KV Cache 利用率触发 HPA,设置合理的冷却期防止抖动。冷启动治理:部署预热池保持 1-2 个就绪 Pod,配合 KEDA 的事件驱动扩缩容缩短响应时间。成本监控:建立 GPU 利用率与推理成本的关联看板,设定利用率低于 40% 的告警,驱动持续优化。渐进式落地:先在离线推理场景验证 GPU 共享方案,稳定后再推广到在线推理,避免一步到位引入过多变量。