LabHub

博客

Kubernetes ML 模型服务:KServe 与 NVIDIA Triton 完全解析

한국어English日本語中文

1. 为什么要在 Kubernetes 上做 ML 模型服务

把机器学习模型部署到生产环境时,用单台服务器上的 Flask 或 FastAPI 来提供模型服务,这种方式适合早期原型验证,但要承接真实的生产流量则存在根本性的局限。Kubernetes 是解决这些局限的最成熟平台。

1.1 可扩展性(Scalability)

Kubernetes 的核心优势是随工作负载而变的水平扩展(Horizontal Scaling)。推理请求激增时可以自动增加 Pod,流量下降时又能重新收缩。借助 HPA(Horizontal Pod Autoscaler),可以基于 CPU、内存或自定义指标(例如 GPU 使用率、请求队列长度)实现自动伸缩。更进一步,使用基于 Knative 的 KPA(Knative Pod Autoscaler)时,还能在完全没有流量的情况下把 Pod 缩到 0,也就是 Scale-to-Zero。

1.2 资源管理(Resource Management)

ML 推理工作负载使用的是 GPU 这种昂贵资源。Kubernetes 通过 resources.requestsresources.limits 精确控制每个 Pod 使用的 CPU、内存和 GPU。使用 NVIDIA Device Plugin 可以把 nvidia.com/gpu 资源以 Kubernetes 原生方式调度,并且通过 MIG(Multi-Instance GPU)或 Time-Slicing 让多个 Pod 共享同一块 GPU。

1.3 运维稳定性(Operational Reliability)

Kubernetes 的 Self-Healing 机制提升了模型服务的稳定性。Pod 异常退出后会自动重启,并通过 liveness/readiness probe 持续监控模型服务器的状态。借助 Rolling Update 与 Canary Deployment,模型更新时可以做到零停机的安全发布。


2. KServe 架构分析

KServe(原 KFServing)是为在 Kubernetes 上做 ML 模型服务而设计的标准化平台。KServe 利用 Kubernetes CRD(Custom Resource Definition)来管理模型服务的完整生命周期。

2.1 分层架构(Layered Architecture)

根据 KServe 官方文档,KServe 由四个主要层次构成。

2.2 InferenceService CRD

InferenceService 是 KServe 的核心 CRD,它封装了 ML 模型服务所需的 autoscaling、networking、health checking、server configuration 的复杂性。一份 YAML 定义就能以声明式方式管理模型服务所需的一切。

最基本的 InferenceService 定义如下。

apiVersion: 'serving.kserve.io/v1beta1'
kind: 'InferenceService'
metadata:
  name: 'sklearn-iris'
spec:
  predictor:
    model:
      modelFormat:
        name: sklearn
      storageUri: 'gs://kfserving-examples/models/sklearn/1.0/model'

仅凭这一份简单的 YAML,模型下载、服务器启动、网络端点设置乃至自动伸缩都会被自动处理。

2.3 三个核心组件:Predictor、Transformer、Explainer

KServe InferenceService 由三个组件构成,其中只有 Predictor 是必需的,其余为可选。

Predictor

Predictor 是 InferenceService 的核心工作负载,由模型与模型服务器构成。它负责处理真实的推理请求,并通过网络端点对外暴露。KServe 提供多种 Serving Runtime:TensorFlow Serving、TorchServe、Triton Inference Server、SKLearn、XGBoost、LightGBM 等都已内置。

Transformer

Transformer 负责推理前后的数据转换(pre/post-processing)。例如对图像输入做归一化、对文本做分词,或把模型输出转换成人类可读的形式。KServe 还提供与 Feast 之类 Feature Store 集成的开箱即用 Transformer。自定义 Transformer 以独立容器部署,并通过预测端点等环境变量与 Predictor 相连。

Explainer

Explainer 是为模型预测提供可解释性(interpretability)的可选组件。它利用 SHAP、LIME 等 XAI(eXplainable AI)技术,说明模型为什么会给出某个特定预测。它运行在与 Predictor 推理结果相分离的数据平面上,仅在收到解释请求时才被激活。

请求流程(Request Flow)

推理请求进来后按如下流程处理。

  1. 客户端请求经由 Ingress Gateway 进入
  2. 在 Transformer 中对输入数据做前处理
  3. 前处理后的数据被传给 Predictor 执行推理
  4. 在 Transformer 中对输出数据做后处理
  5. 最终结果返回给客户端

对于 Explanation 请求,则由 Explainer 额外介入生成模型解释。


3. KServe 安装与基本用法

3.1 前置要求

根据 KServe 官方 QuickStart 指南,需要满足以下条件。

3.2 Quick Install

在开发与测试环境中最快的上手方式是使用 Quick Install Script。

# KServe Quick Install (Serverless mode - 包含 Knative)
curl -s "https://raw.githubusercontent.com/kserve/kserve/master/hack/quick_install.sh" | bash

# Raw Deployment mode (不安装 Knative)
curl -s "https://raw.githubusercontent.com/kserve/kserve/master/hack/quick_install.sh" | bash -s -- -r

该脚本会自动处理依赖安装、平台检测与模式设置。

3.3 部署模式

KServe 支持两种主要的部署模式。

3.4 部署第一个模型

安装完成后,可以按如下方式部署一个简单的 SKLearn 模型。

apiVersion: 'serving.kserve.io/v1beta1'
kind: 'InferenceService'
metadata:
  name: 'sklearn-iris'
spec:
  predictor:
    model:
      modelFormat:
        name: sklearn
      storageUri: 'gs://kfserving-examples/models/sklearn/1.0/model'
      resources:
        requests:
          cpu: '100m'
          memory: '256Mi'
        limits:
          cpu: '1'
          memory: '512Mi'
# 部署模型
kubectl apply -f sklearn-iris.yaml

# 查看状态
kubectl get inferenceservice sklearn-iris

# 测试推理请求
curl -v -H "Content-Type: application/json" \
  http://${INGRESS_HOST}:${INGRESS_PORT}/v1/models/sklearn-iris:predict \
  -d '{"instances": [[6.8, 2.8, 4.8, 1.4]]}'

4. NVIDIA Triton Inference Server 功能分析

NVIDIA Triton Inference Server 是能够同时为多种深度学习与机器学习框架的模型提供服务的高性能推理服务器。它既可以作为 KServe 的 Serving Runtime 集成使用,也可以独立部署。

4.1 核心特性

根据 Triton 官方文档,Triton 提供以下核心功能。


5. 模型格式支持

5.1 Triton Backend 架构

Triton 通过 Backend 插件架构支持多种模型格式。每个模型都必须与一个 Backend 关联,并通过 config.pbtxtbackendplatform 字段指定。

5.2 支持的框架与模型格式

ONNX Runtime Backend

运行 ONNX(Open Neural Network Exchange)格式的模型。支持从 PyTorch、TensorFlow、scikit-learn 等多种框架转换而来的模型。它在 CPU 与 GPU 上都提供经过优化的推理,并可利用 ONNX Runtime 的 Graph Optimization 与 Execution Provider。

# ONNX 模型文件示例
model_repository/
  resnet50_onnx/
    config.pbtxt
    1/
      model.onnx

TensorRT Backend

运行经 NVIDIA TensorRT 优化的模型(engine/plan 文件)。TensorRT 是针对 NVIDIA GPU 特化的推理优化引擎,通过 FP16/INT8 量化、Layer Fusion、Kernel Auto-Tuning 等手段提供最高水平的 GPU 推理性能。

# TensorRT 模型文件示例
model_repository/
  resnet50_tensorrt/
    config.pbtxt
    1/
      model.plan

PyTorch Backend

同时支持 TorchScript 格式和 PyTorch 2.0 的 torch.compile 格式。TorchScript 是用 torch.jit.tracetorch.jit.script 转换出的模型,同一个 Backend 可以同时处理 TorchScript 与 PyTorch 2.0 模型。

# PyTorch 模型文件示例
model_repository/
  bert_torchscript/
    config.pbtxt
    1/
      model.pt

TensorFlow Backend

同时支持 TensorFlow 的 SavedModel 与 GraphDef 格式。同一个 Backend 可以运行 TensorFlow 1 与 TensorFlow 2 的模型。推荐使用 SavedModel 格式。

# TensorFlow SavedModel 示例
model_repository/
  resnet50_tf/
    config.pbtxt
    1/
      model.savedmodel/
        saved_model.pb
        variables/

其他 Backend


6. Model Repository 结构与 config.pbtxt 配置

6.1 目录结构

Triton 官方文档所定义的 Model Repository 基本布局如下。

<model-repository-path>/
  <model-name>/
    [config.pbtxt]
    [<output-labels-file> ...]
    <version>/
      <model-definition-file>
    <version>/
      <model-definition-file>
    ...
  <model-name>/
    [config.pbtxt]
    ...

各组成部分的作用如下。

来看一个实际示例。

model_repository/
  text_detection/
    config.pbtxt
    1/
      model.onnx
  text_recognition/
    config.pbtxt
    output_labels.txt
    1/
      model.plan
    2/
      model.plan
  ensemble_ocr/
    config.pbtxt
    1/

6.2 config.pbtxt 详细配置

config.pbtxt 是把 ModelConfig protobuf 消息以文本形式表达的产物。最小配置至少要包含 name、backend(或 platform)、max_batch_size、input、output。不过对部分模型,Triton 可以自动生成最小配置。

name: "resnet50"
backend: "onnxruntime"
max_batch_size: 8

input [
  {
    name: "input"
    data_type: TYPE_FP32
    dims: [ 3, 224, 224 ]
  }
]

output [
  {
    name: "output"
    data_type: TYPE_FP32
    dims: [ 1000 ]
    label_filename: "labels.txt"
  }
]

instance_group [
  {
    count: 2
    kind: KIND_GPU
    gpus: [ 0 ]
  }
]

dynamic_batching {
  preferred_batch_size: [ 4, 8 ]
  max_queue_delay_microseconds: 100
}

上面示例中各项的含义如下。


7. Dynamic Batching 与 Concurrent Model Execution

7.1 Dynamic Batching

Dynamic Batching 是 Triton 最强大的性能优化功能之一。它把分别到达的推理请求打包成一个批次送入 GPU,从而大幅提升吞吐量(throughput)。

根据 Triton 官方文档,Dynamic Batching 可以按模型独立启用与配置。默认情况下 Triton 会把已到达的请求立即打包而不做等待,但用户可以设置有限的等待时间,让调度器收集更多请求。

dynamic_batching {
  # 期望的批大小(尽可能按该大小打包)
  preferred_batch_size: [ 4, 8 ]

  # 为凑齐批次而等待的最长时间(微秒)
  max_queue_delay_microseconds: 100

  # 优先级设置(可选)
  priority_levels: 2
  default_priority_level: 1

  # 队列策略(可选)
  default_queue_policy {
    timeout_action: REJECT
    default_timeout_microseconds: 5000000
    allow_timeout_override: true
    max_queue_size: 100
  }
}

核心参数的作用如下。

7.2 Concurrent Model Execution

Triton 可以在同一块 GPU 上同时运行多个模型实例。借此更高效地利用 GPU 的算力资源。

# 在一块 GPU 上放置 2 个模型实例
instance_group [
  {
    count: 2
    kind: KIND_GPU
    gpus: [ 0 ]
  }
]
# 把实例分散放置到多块 GPU 上
instance_group [
  {
    count: 1
    kind: KIND_GPU
    gpus: [ 0 ]
  },
  {
    count: 2
    kind: KIND_GPU
    gpus: [ 1, 2 ]
  }
]
# 在 CPU 与 GPU 上同时放置实例
instance_group [
  {
    count: 1
    kind: KIND_CPU
  },
  {
    count: 2
    kind: KIND_GPU
    gpus: [ 0 ]
  }
]

Triton 官方文档给出的、用于取得最大吞吐量的并发度公式如下。

最优并发度 = 2 x <max_batch_size> x <instance_count>

例如,max_batch_size 为 8、instance_count 为 2 时,最优并发度就是 2 x 8 x 2 = 32。也就是说,需要发送 32 个并发请求才能把 Triton 的性能发挥到最大。


8. GPU 资源分配

8.1 NVIDIA Device Plugin for Kubernetes

NVIDIA Device Plugin 是一个 DaemonSet,它让 Kubernetes 集群能够把 GPU 作为原生资源来管理。安装后会自动探测各节点的 GPU 并注册为 nvidia.com/gpu 资源。

# 安装 NVIDIA Device Plugin
kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.17.0/deployments/static/nvidia-device-plugin.yml

在 Pod 中请求 GPU 的方式如下。

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  containers:
    - name: inference
      image: nvcr.io/nvidia/tritonserver:24.01-py3
      resources:
        limits:
          nvidia.com/gpu: 1

8.2 MIG(Multi-Instance GPU)

MIG 是 NVIDIA A100、H100 等新一代 GPU 支持的功能,它把一块物理 GPU 切分为多个相互独立的 GPU 实例。每个实例在硬件层面隔离了内存与算力资源,因此故障隔离(fault isolation)得到保障。

MIG 的主要特性如下。

要在 Kubernetes 中使用 MIG,需通过 NVIDIA GPU Operator 配置 MIG 策略。

# MIG 配置文件示例 (GPU Operator ConfigMap)
apiVersion: v1
kind: ConfigMap
metadata:
  name: mig-parted-config
data:
  config.yaml: |
    version: v1
    mig-configs:
      all-1g.10gb:
        - devices: all
          mig-enabled: true
          mig-devices:
            "1g.10gb": 7
      all-3g.40gb:
        - devices: all
          mig-enabled: true
          mig-devices:
            "3g.40gb": 2

8.3 GPU Time-Slicing

Time-Slicing 是让不支持 MIG 的上一代 GPU 也能实现 GPU 共享的方式。根据 NVIDIA GPU Operator 官方文档,Time-Slicing 让系统管理员可以定义 GPU 的副本(replica),并把它们独立分配给各个 Pod。

核心特性如下。

# Time-Slicing 配置 (GPU Operator ConfigMap)
apiVersion: v1
kind: ConfigMap
metadata:
  name: time-slicing-config
  namespace: gpu-operator
data:
  any: |-
    version: v1
    sharing:
      timeSlicing:
        resources:
        - name: nvidia.com/gpu
          replicas: 4

上面的配置把每块 GPU 暴露为 4 个副本,于是 4 个 Pod 各自请求 nvidia.com/gpu: 1 时,就会以分时方式共享同一块物理 GPU。


9. Auto-scaling 策略

9.1 HPA(Horizontal Pod Autoscaler)

在 KServe 的 Standard Mode(Raw Deployment)中使用 Kubernetes 原生 HPA。它基于 CPU、内存或自定义指标自动调节 Pod 数量。

apiVersion: 'serving.kserve.io/v1beta1'
kind: 'InferenceService'
metadata:
  name: 'sklearn-iris'
  annotations:
    serving.kserve.io/autoscalerClass: 'hpa'
    serving.kserve.io/targetUtilizationPercentage: '80'
    serving.kserve.io/minReplicas: '1'
    serving.kserve.io/maxReplicas: '10'
spec:
  predictor:
    model:
      modelFormat:
        name: sklearn
      storageUri: 'gs://kfserving-examples/models/sklearn/1.0/model'

9.2 KPA(Knative Pod Autoscaler)

在 KServe 的 Serverless Mode(Knative)中使用 KPA。KPA 最大的特点是 Scale-to-Zero,在没有流量时可以把 Pod 缩到 0 以节省资源。HPA 与 KPA 不能在同一个服务上同时使用,需要通过 annotation 指定使用哪个 Autoscaler。

apiVersion: 'serving.kserve.io/v1beta1'
kind: 'InferenceService'
metadata:
  name: 'sklearn-iris'
  annotations:
    autoscaling.knative.dev/class: 'kpa.autoscaling.knative.dev'
    autoscaling.knative.dev/metric: 'concurrency'
    autoscaling.knative.dev/target: '10'
    autoscaling.knative.dev/minScale: '0'
    autoscaling.knative.dev/maxScale: '10'
spec:
  predictor:
    model:
      modelFormat:
        name: sklearn
      storageUri: 'gs://kfserving-examples/models/sklearn/1.0/model'

KPA 的主要指标如下。

9.3 基于 GPU 指标的 Auto-scaling

对于 GPU 工作负载,仅靠 CPU/内存的伸缩是不够的。把 NVIDIA DCGM(Data Center GPU Manager)Exporter 与 Prometheus Adapter 组合起来,就能实现基于 GPU 指标的伸缩。

# 使用 KEDA ScaledObject 实现基于 GPU 指标的伸缩
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: triton-gpu-scaler
spec:
  scaleTargetRef:
    name: triton-inference-server
  pollingInterval: 15
  cooldownPeriod: 60
  minReplicaCount: 1
  maxReplicaCount: 5
  triggers:
    - type: prometheus
      metadata:
        serverAddress: http://prometheus.monitoring:9090
        metricName: gpu_utilization
        query: avg(DCGM_FI_DEV_GPU_UTIL{pod=~"triton-.*"})
        threshold: '80'

KServe Controller Manager 除 HPA 之外也支持 KEDA(Kubernetes Event-Driven Autoscaling),可以配置基于自定义指标的伸缩。


10. Canary / Blue-Green Deployment

10.1 Canary Rollout

根据 KServe 官方文档,Canary Rollout 策略在 Serverless Deployment Mode 下受支持。KServe 会自动追踪最后一次接收 100% 流量的稳定版本(last known good revision),设置 canaryTrafficPercent 字段后,它会在新版本与稳定版本之间自动分配流量。

apiVersion: 'serving.kserve.io/v1beta1'
kind: 'InferenceService'
metadata:
  name: 'my-model'
  annotations:
    serving.kserve.io/enable-tag-routing: 'true'
spec:
  predictor:
    canaryTrafficPercent: 10
    model:
      modelFormat:
        name: tensorflow
      storageUri: 'gs://kfserving-examples/models/tensorflow/flowers-2'

在上面的配置中,10% 的流量被送往新模型版本,其余 90% 路由到此前的稳定版本。新版本的表现得到验证后,逐步提高 canaryTrafficPercent,最终切换到 100%。

Canary Rollout 的一般工作流如下。

  1. 部署新模型版本时设置 canaryTrafficPercent: 10
  2. 通过监控确认新版本的 latency、error rate 等
  3. 没有问题就把 canaryTrafficPercent 逐步提高到 20 -> 50 -> 100
  4. 出现问题时设置 canaryTrafficPercent: 0 立即回滚

10.2 Blue-Green Deployment

在 Blue-Green Deployment 中,会同时运行两套完整环境(Blue:当前生产环境,Green:新版本),验证通过后一次性切换流量。

在 KServe 中,可以设置 canaryTrafficPercent: 0 来部署新版本却不给它流量,验证完成后再切到 100,从而实现 Blue-Green Deployment。另外使用 serving.kserve.io/enable-tag-routing: "true" annotation 后,可以通过基于标签的路由直接调用特定版本进行测试。

apiVersion: 'serving.kserve.io/v1beta1'
kind: 'InferenceService'
metadata:
  name: 'my-model'
  annotations:
    serving.kserve.io/enable-tag-routing: 'true'
spec:
  predictor:
    # 以 0% 流量部署新版本 (Green)
    canaryTrafficPercent: 0
    model:
      modelFormat:
        name: tensorflow
      storageUri: 'gs://kfserving-examples/models/tensorflow/flowers-v2'

启用标签路由后,可以用 latest 标签直接调用新版本、用 prev 标签直接调用旧版本进行测试。验证完成后把 canaryTrafficPercent 设为 100,即可把全部流量切换到新版本。


11. Prometheus + Grafana 监控配置

11.1 指标采集架构

在 ML 模型服务的生产运维中,监控是必不可少的。KServe 原生支持通过 Prometheus 采集指标、通过 Grafana 做可视化。

指标采集路径如下。

  1. KServe 服务容器:各模型服务器(Triton、TorchServe 等)暴露自身的指标
  2. Knative Queue Proxy:在 Serverless Mode 下,queue-proxy 容器会自动生成请求指标
  3. Prometheus:通过 ServiceMonitor 动态发现并抓取 Pod 的指标端点
  4. Grafana:以 Prometheus 为数据源做仪表盘可视化

11.2 Prometheus 配置

使用 Prometheus Operator 与 ServiceMonitor,可以自动采集 KServe Pod 的指标。

# Prometheus ServiceMonitor for KServe
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: kserve-monitor
  namespace: monitoring
spec:
  selector:
    matchLabels:
      serving.kserve.io/inferenceservice: 'my-model'
  endpoints:
    - port: metrics
      interval: 15s
      path: /metrics
  namespaceSelector:
    matchNames:
      - default

Triton 默认在 8002 端口暴露 Prometheus 指标。主要指标如下。

11.3 Grafana 仪表盘

KServe 官方文档提供了按 Serving Runtime 划分的模板仪表盘。

KServe ModelServer Latency Dashboard

这是面向默认 KServe ModelServer(sklearn、xgboost、lgb、paddle、pmml、custom)运行时的仪表盘,把 pre/post-process、predict、explain 各阶段的 latency 以毫秒为单位可视化。

KServe Triton Latency Dashboard

Triton 专用仪表盘,提供五种 latency 图表。

此外还包含 GPU 内存使用量的百分比仪表盘。

KServe TorchServe Latency Dashboard

TorchServe 专用仪表盘,把 ts_inference_latency_microsecondsts_queue_latency_microseconds 指标以毫秒为单位可视化。

11.4 GPU 监控(DCGM Exporter)

为了做 GPU 专项监控,额外部署 NVIDIA DCGM Exporter 就能采集详细的 GPU 指标。

# 安装 DCGM Exporter (Helm)
helm repo add gpu-helm-charts https://nvidia.github.io/dcgm-exporter/helm-charts
helm install dcgm-exporter gpu-helm-charts/dcgm-exporter \
  --namespace monitoring \
  --set serviceMonitor.enabled=true

DCGM Exporter 提供的主要 GPU 指标如下。

把这些指标在 Grafana 仪表盘中综合可视化后,就能实时掌握各模型的推理性能、GPU 资源利用率、瓶颈环节等信息,并用于优化。


12. 总结

Kubernetes 环境下的 ML 模型服务不只是简单的部署,还需要一套系统性的方法来同时达成可扩展性、资源效率与运维稳定性。KServe 通过 InferenceService CRD 抽象掉模型服务的复杂性,并以 Predictor/Transformer/Explainer 结构让人可以灵活组合推理流水线。NVIDIA Triton Inference Server 则通过多框架支持、Dynamic Batching、Concurrent Model Execution 等手段最大化 GPU 利用率。

在 GPU 资源管理上,以 NVIDIA Device Plugin 为基础,再借助 MIG 与 Time-Slicing,可以高效共享昂贵的 GPU 资源。Auto-scaling 方面则按工作负载特性在 HPA、KPA、KEDA 中做选择,并用 Canary/Blue-Green Deployment 保障模型更新的安全性。最后还应以 Prometheus + Grafana + DCGM Exporter 的组合,对模型性能与 GPU 资源做综合监控。

把这些组成部分有机地结合起来,就能构建出可以稳定、高效运行大规模 ML 工作负载的生产级模型服务平台。


References

评论

还没有评论。

登录后即可发表评论