AI 云原生架构:从模型服务到弹性调度的全链路设计

一、AI 工程化的核心矛盾:算力需求与资源效率

去年我们团队上线了一个 LLM 推理服务。模型参数 7B,单卡 A100 跑推理,QPS 上限 15。流量高峰一到,请求排队超 30 秒,用户体验直接崩盘。扩容?一个 GPU 实例启动要 3 分钟,等 Pod Ready 的时候流量早降了。缩容?GPU 空闲一分钟都是钱,成本账根本算不过来。

AI 工程化的核心矛盾很明确:模型推理的算力需求是刚性的,但业务流量是波动的。传统微服务的 HPA 策略对 GPU 资源几乎无效,因为 GPU 调度、显存管理、模型加载都有特殊约束。

云原生架构要解决的是:让 AI 工作负载像普通微服务一样可观测、可调度、可弹性伸缩,同时不丢失 GPU 的性能优势。

二、AI 云原生架构全景:从推理到训练的分层设计

AI 云原生不是简单地把模型塞进容器。它需要一套完整的分层架构,覆盖模型生命周期管理、推理服务编排、训练任务调度和资源弹性分配。

graph TB
    subgraph 接入层
        A[API Gateway] --> B[请求路由]
        B --> C[负载均衡]
    end
    subgraph 推理服务层
        C --> D[vLLM 推理引擎]
        C --> E[Triton Inference Server]
        C --> F[自定义推理服务]
    end
    subgraph 编排调度层
        D --> G[K8s Scheduler]
        E --> G
        F --> G
        G --> H[GPU 共享调度]
        G --> I[弹性伸缩 CRD]
        I --> J[KEDA 事件驱动 HPA]
    end
    subgraph 模型管理层
        K[Model Registry] --> L[模型版本管理]
        L --> M[模型热加载]
        M --> D
        M --> E
    end
    subgraph 基础设施层
        H --> N[NVIDIA GPU Operator]
        N --> O[MIG 分区]
        N --> P[显存池化]
    end

关键设计点:

1. 推理引擎选型

vLLM 适合 LLM 场景,PagedAttention 机制让显存利用率提升 2-4 倍。Triton 适合多模型混合部署,支持动态 batching。选型取决于模型类型和延迟要求。

2. GPU 共享调度

原生 K8s 调度器只能按整卡分配 GPU。通过 GPU Operator + MIG 或时间分片,可以让多个 Pod 共享一张 GPU,提升利用率。

3. 事件驱动弹性

KEDA 可以基于消息队列深度、请求延迟等自定义指标触发 HPA,比原生 CPU/Memory 指标更贴合 AI 场景。

三、生产级 AI 推理服务部署:从 CRD 到弹性伸缩

3.1 推理服务自定义资源定义

# inference-service.yaml - 基于 KServe 的推理服务
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: llm-chat
  namespace: ai-serving
  annotations:
    # 开启 GPU 时间分片
    scheduling.k8s.io/gpu-sharing: "true"
spec:
  predictor:
    minReplicas: 1
    maxReplicas: 8
    scaleTarget: 5        # 目标并发请求数
    scaleMetric: concurrency
    model:
      modelFormat:
        name: vllm
      storageUri: "gs://models/llm-7b/v2"
      resources:
        limits:
          nvidia.com/gpu: 1
          memory: "16Gi"
        requests:
          nvidia.com/gpu: 1
          memory: "8Gi"
      # vLLM 推理参数
      args:
        - --max-model-len=4096
        - --gpu-memory-utilization=0.85
        - --max-num-seqs=64
    # 就绪探针 - 模型加载完成后才接收流量
    readinessProbe:
      httpGet:
        path: /health
        port: 8000
      initialDelaySeconds: 60
      periodSeconds: 10
      failureThreshold: 3
    # 存活探针 - 检测 OOM 和 hang
    livenessProbe:
      httpGet:
        path: /health
        port: 8000
      initialDelaySeconds: 120
      periodSeconds: 30
      failureThreshold: 5

3.2 KEDA 事件驱动弹性伸缩

# keda-scaler.yaml - 基于队列深度弹性伸缩
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: llm-inference-scaler
  namespace: ai-serving
spec:
  scaleTargetRef:
    apiVersion: serving.kserve.io/v1beta1
    kind: InferenceService
    name: llm-chat
  pollingInterval: 5
  cooldownPeriod: 120
  minReplicaCount: 1
  maxReplicaCount: 8
  triggers:
    # 基于请求队列深度
    - type: rabbitmq
      metadata:
        queueName: llm-inference-queue
        queueLength: "10"
      authenticationRef:
        name: rabbitmq-trigger-auth
    # 基于自定义指标(推理延迟 P99)
    - type: prometheus
      metadata:
        serverAddress: http://prometheus:9090
        metricName: inference_latency_p99_seconds
        threshold: "5"
        query: >
          histogram_quantile(0.99,
            sum(rate(inference_request_duration_seconds_bucket{
              namespace="ai-serving"
            }[1m])) by (le)
          )
---
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: rabbitmq-trigger-auth
  namespace: ai-serving
spec:
  secretTargetRef:
    - parameter: host
      name: rabbitmq-secret
      key: host

3.3 GPU 资源共享配置

# gpu-sharing-config.yaml - GPU 时间分片配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: gpu-sharing-config
  namespace: gpu-operator
data:
  # 每张 GPU 最多分给 4 个 Pod 共享
  NVIDIA_GPU_SHARED_MAX_CLIENTS: "4"
  # 时间片长度(微秒)
  NVIDIA_GPU_SHARED_TIMESLICE: "1000"
---
# 使用 GPU 共享的 Pod 声明
apiVersion: v1
kind: Pod
metadata:
  name: llm-lightweight
  annotations:
    # 请求 GPU 的一部分算力
    nvidia.com/gpu.shared: "true"
spec:
  containers:
    - name: inference
      image: vllm/vllm-openai:latest
      resources:
        limits:
          nvidia.com/gpu: 1      # 逻辑 GPU,实际共享物理卡
          memory: "4Gi"
        requests:
          nvidia.com/gpu: 1
          memory: "2Gi"

3.4 模型热加载与版本管理

# model-rollout.yaml - 模型滚动更新策略
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: llm-chat
  namespace: ai-serving
spec:
  predictor:
    model:
      storageUri: "gs://models/llm-7b/v3"  # 新版本模型
    # 滚动更新配置
    deploymentStrategy:
      type: RollingUpdate
      rollingUpdate:
        maxSurge: 1
        maxUnavailable: 0     # 保证至少有 1 个可用副本
    canaryTrafficPercent: 10  # 先导 10% 流量到新版本

四、AI 云原生的架构代价:冷启动、显存碎片与成本失控

GPU 冷启动问题

模型加载到 GPU 显存需要 30-120 秒,取决于模型大小。这意味着 HPA 扩容的 Pod 不能立即服务。解决方案:使用模型预热(Model Pre-warming),提前加载模型到备用 Pod;或使用 Serverless GPU 方案(如 RunPod),保持 warm pool。

显存碎片化

多个模型共享 GPU 时,显存分配和释放会产生碎片。长时间运行后,即使总显存够用,也可能无法为新模型分配连续空间。vLLM 的 PagedAttention 部分解决了这个问题,但只限于单模型内。跨模型的显存管理目前没有成熟方案,需要定期重启推理节点来回收碎片。

成本控制的盲区

GPU 实例成本是 CPU 实例的 5-10 倍。如果弹性策略配置不当,一个流量尖峰可能导致 GPU 扩容到 maxReplicas,然后长时间不缩容。必须设置严格的 cooldownPeriod 和 maxReplicaCount,同时配合集群自动缩放器(Cluster Autoscaler)的节点级缩容。

多租户隔离的挑战

GPU 共享场景下,一个高负载 Pod 可能影响同卡其他 Pod 的推理延迟。MIG 硬件隔离能解决这个问题,但只支持 A100/H100 等高端卡,且分区粒度固定。时间分片方案成本更低,但隔离性差。

可观测性缺口

GPU 指标(显存利用率、SM 占用率、功耗)需要 DCGM Exporter 单独采集。原生 K8s 监控看不到这些数据。必须部署 NVIDIA GPU Operator + DCGM Exporter,并在 Grafana 中建立 GPU 专用看板。

五、总结

AI 云原生架构的本质,是把 AI 工作负载的特殊性(GPU 依赖、模型生命周期、显存管理)封装成 K8s 原生资源,让运维和调度对上层透明。

落地路线建议:

  1. 第一阶段:用 KServe 或 vLLM 部署推理服务,配置 GPU Operator,建立 GPU 监控看板
  2. 第二阶段:引入 KEDA 事件驱动弹性,实现基于队列和延迟的自动伸缩
  3. 第三阶段:落地 GPU 共享调度和模型热加载,优化资源利用率和部署效率
  4. 持续迭代:建立成本看板,跟踪 GPU 利用率和单请求成本,持续调优弹性策略

AI 云原生不是把模型丢进容器就完事。它需要从资源层到应用层的完整设计,才能在算力效率和业务弹性之间找到平衡点。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐