AI 模型云原生部署:GPU 调度与弹性伸缩的工程实践

一、算力成本与响应延迟的双重夹击:AI 模型部署的核心痛点

将 AI 大模型从实验室推向生产环境,面临的第一个问题不是"能不能跑起来",而是"能不能跑得经济且稳定"。一个 7B 参数的语言模型,单次推理需要约 14GB 显存,在 NVIDIA A10G 上推理延迟约 200ms;但如果请求量突增,单卡吞吐量迅速饱和,排队延迟飙升到秒级。

更关键的是成本。GPU 实例的价格是 CPU 实例的 5-10 倍,24 小时满载运行一个月就是数万元。而 AI 推理服务的流量特征通常是"潮汐式"的——白天高峰需要 8 卡并行,深夜低谷 1 卡即可。如何在 Kubernetes 上实现 GPU 的精细化调度和弹性伸缩,直接决定了 AI 服务的 ROI。

二、GPU 调度与显存管理:K8s 上的 AI 推理架构

Kubernetes 原生不支持 GPU 共享,一个 GPU 只能分配给一个 Pod。这在 AI 推理场景下造成巨大的资源浪费——一个推理进程可能只占用了 GPU 显存的 30%,剩余 70% 完全空闲。

graph TB
    subgraph Kubernetes 集群
        API[API Server] --> SCHED[Scheduler]
        SCHED -->|GPU 调度| NODE1[Node 1: A100 x2]
        SCHED -->|GPU 调度| NODE2[Node 2: A10G x4]
    end
    subgraph GPU 调度策略
        EXCLUSIVE[独占模式] --> NODE1
        MPS[MPS 共享模式] --> NODE2
        TIME[时间片共享] --> NODE2
    end
    subgraph 弹性伸缩
        HPA[HPA: CPU/自定义指标] --> SCALE[Pod 水平伸缩]
        VPA[VPA: 显存指标] --> RESIZE[Pod 垂直调整]
        KEDA[KEDA: 队列深度] --> SCALE
    end
    NODE1 -->|推理服务 A| MODELA[模型 A: 7B]
    NODE2 -->|推理服务 B| MODELB[模型 B: 13B]
    NODE2 -->|推理服务 C| MODELC[模型 C: 3B]

上图展示了 K8s 上 AI 推理服务的调度架构。三种 GPU 共享策略各有适用场景:

独占模式:一个 Pod 独占整张 GPU。适合大模型(70B+)或对延迟极度敏感的场景。资源利用率低,但隔离性最好。

MPS(Multi-Process Service)共享:NVIDIA 提供的 GPU 共享方案,允许多个 CUDA 进程共享同一 GPU。MPS 通过共享内存和计算资源的协作调度,减少上下文切换开销。适合中小模型(7B-13B)的批量推理。

时间片共享:通过 NVIDIA GPU Operator 的 time-slicing 配置,将一张 GPU 虚拟为多个 GPU 实例。每个实例获得固定的时间片,进程间完全隔离。适合小模型(3B 以下)或低 QPS 场景。

三、生产级 AI 推理服务部署

3.1 GPU 时间片共享配置

# gpu-time-slicing-config.yaml — GPU 时间片共享配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: gpu-time-slicing-config
  namespace: gpu-operator
data:
  # 将每张 GPU 虚拟为 4 个实例,每个实例获得 25% 算力
  config.yaml: |
    version: v1
    sharing:
      timeSlicing:
        renameByDefault: false
        failRequestsGreaterThanOne: true
        resources:
          - name: nvidia.com/gpu
            replicas: 4
---
# 使用 GPU 时间片的推理 Pod
apiVersion: apps/v1
kind: Deployment
metadata:
  name: llm-inference-7b
  namespace: ai-serving
spec:
  replicas: 2
  selector:
    matchLabels:
      app: llm-inference-7b
  template:
    metadata:
      labels:
        app: llm-inference-7b
    spec:
      containers:
        - name: inference
          image: ghcr.io/huggingface/text-generation-inference:2.0
          resources:
            limits:
              # 请求 1 个虚拟 GPU(实际共享物理 GPU)
              nvidia.com/gpu: 1
          env:
            - name: MODEL_NAME
              value: "Qwen/Qwen2-7B-Instruct"
            - name: MAX_INPUT_LENGTH
              value: "2048"
            - name: MAX_TOTAL_TOKENS
              value: "4096"
            # 限制单次推理最大 token 数,防止显存溢出
            - name: MAX_BATCH_PREFILL_TOKENS
              value: "4096"
          ports:
            - containerPort: 80
          # 健康检查:确认推理服务就绪
          livenessProbe:
            httpGet:
              path: /health
              port: 80
            initialDelaySeconds: 120
            periodSeconds: 30
          readinessProbe:
            httpGet:
              path: /health
              port: 80
            initialDelaySeconds: 60
            periodSeconds: 10

3.2 基于 KEDA 的队列驱动弹性伸缩

# keda-scaler-llm.yaml — 基于请求队列深度的弹性伸缩
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: llm-inference-scaler
  namespace: ai-serving
spec:
  scaleTargetRef:
    name: llm-inference-7b
  minReplicaCount: 1
  # 最大副本数受 GPU 节点数量限制
  maxReplicaCount: 8
  cooldownPeriod: 120
  triggers:
    # 基于自定义指标:推理队列中等待的请求数
    - type: prometheus
      metadata:
        serverAddress: "http://prometheus.monitoring:9090"
        metricName: tgi_queue_size
        threshold: "10"
        query: >
          sum(tgi_request_queue_size{model="qwen2-7b"})
    # 辅助指标:GPU 显存利用率
    - type: prometheus
      metadata:
        serverAddress: "http://prometheus.monitoring:9090"
        metricName: gpu_memory_utilization
        threshold: "85"
        query: >
          avg(DCGM_FI_DEV_GPU_UTIL{namespace="ai-serving"})

3.3 GPU 节点自动扩缩容

# gpu-node-autoscaler.yaml — GPU 节点池自动扩缩容
apiVersion: cluster-autoscaler.k8s.io/v1
kind: ClusterAutoscaler
metadata:
  name: default
spec:
  resourceGroups:
    - name: gpu-node-pool
      resources:
        - name: nvidia.com/gpu
          # 最小 1 节点,最大 10 节点
          minSize: 1
          maxSize: 10
      nodeSelector:
        node.kubernetes.io/instance-type: "gpu-a10g"
---
# Cluster Autoscaler 的 Pod 配置需要关注扩容延迟
# GPU 节点启动通常需要 3-5 分钟(驱动初始化 + 镜像拉取)
# 因此 HPA 的冷却时间应设置为 180s 以上,避免频繁抖动

四、GPU 调度方案的代价与边界

时间片共享的性能损耗:4 路时间片共享下,单实例的推理吞吐量约为独占模式的 60-70%,延迟波动增加 2-3 倍。这是因为 GPU 的计算单元在多个进程间切换时存在上下文切换开销。对于 P99 延迟要求低于 500ms 的场景,不建议使用时间片共享。

MPS 共享的隔离缺陷:MPS 共享模式下,一个进程的异常(如显存越界)可能导致同一 GPU 上所有进程崩溃。此外,MPS 不支持计算资源隔离,一个计算密集型进程会挤占其他进程的算力。生产环境中建议为关键推理服务配置 MPS 亲和性,避免与非关键服务共享 GPU。

弹性伸缩的冷启动问题:GPU 节点从启动到推理服务就绪通常需要 5-8 分钟(节点初始化 2 分钟 + 驱动加载 1 分钟 + 镜像拉取 2 分钟 + 模型加载 2 分钟)。这意味着基于队列深度的弹性伸缩无法应对突发流量,必须配合预热策略或预留缓冲容量。

成本边界:当 GPU 利用率长期低于 30% 时,云上按需实例的成本远高于 Spot 实例。但 Spot 实例存在被回收的风险,推理服务必须具备检查点恢复能力。建议将 70% 的推理流量调度到 Spot 实例,30% 保留在按需实例上作为兜底。

五、结语

AI 模型的云原生部署,本质是在算力成本、推理延迟和系统稳定性之间寻找平衡点。GPU 调度策略决定了资源利用率的上限,弹性伸缩策略决定了应对流量波动的响应速度,而成本优化策略决定了服务的长期可持续性。

落地路线建议:第一步,使用 NVIDIA GPU Operator 配置时间片共享,将中小模型的 GPU 利用率从 30% 提升到 70% 以上;第二步,部署 KEDA 基于 Prometheus 自定义指标实现队列驱动的弹性伸缩;第三步,配置 Cluster Autoscaler 管理 GPU 节点池,设置合理的冷却时间避免抖动;第四步,引入 Spot 实例降低成本,同时实现模型检查点的快速恢复机制;第五步,建立 GPU 利用率和推理延迟的监控看板,持续优化调度参数。

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐