KServe 工作流程:从 InferenceService 到 VLLM 服务

KServe 工作流程:从 InferenceService 到 vLLM 服务

上一篇完成了 KServe 的安装,并通过 InferenceService 跑通了一个 Qwen 模型服务。

当时提交的 InferenceService 只包含模型格式、存储地址、GPU 资源和启动参数,KServe 却创建出了 Deployment、Service 和 HTTPRoute。这份 YAML 中没有容器镜像,那么 KServe 如何选中 HuggingFaceServer,Pod 中又为什么会运行 vLLM?

这一篇先看 KServe 的整体架构和核心 CRD,再结合上一篇的 qwen-llm,梳理从提交 InferenceService 到请求进入 vLLM 的完整流程。

以下内容基于 KServe 0.18,部署模式为 Standard。

1. KServe 整体架构

KServe 是一个构建在 Kubernetes 之上的 AI 推理平台: KServe AI 推理平台架构概览

整体分为控制面和数据面:

KServe Standard 模式控制面与数据面架构

1.1 控制面:从 InferenceService 到推理服务

控制面负责创建和维护推理服务。整个过程从用户提交 InferenceService 开始:

  1. 用户通过 kubectl apply 将 InferenceService 提交到 Kubernetes API Server。
  2. Admission Webhook 对配置进行默认化和校验。例如补充默认部署模式,并拒绝不符合 API 约束的配置。
  3. InferenceService 写入集群后,KServe Controller 通过 Watch 感知对象变化,开始执行调谐。
  4. Controller 根据 modelFormat 匹配 ServingRuntime,从 Runtime 中取得模型服务器镜像和默认启动参数,再与 InferenceService 中的模型地址、运行参数和资源需求合并。
  5. 在 Standard 模式下,Controller 根据合并后的配置创建 Deployment、Service 和 HTTPRoute。Pod 创建时,KServe Pod Mutating Webhook 根据存储注解注入 PVC volume 和 volumeMount;接下来由 Kubernetes 负责调度 Pod、挂载存储和分配 GPU,Gateway Controller 负责让 HTTPRoute 生效。
  6. Controller 持续观察 Deployment 和 HTTPRoute 的状态。当 Predictor 可用、路由被 Gateway 接受后,会更新 PredictorReadyIngressReady 和最终的 Ready 状态。

所以,控制面管理的是推理服务的生命周期:创建、更新、状态同步,以及配置变更后的持续调谐。

1.2 数据面:访问已经创建的推理服务

数据面由实际运行模型和处理请求的资源组成。上一篇的环境使用 Envoy Gateway 作为 Gateway API 的实现。模型服务 Ready 后,客户端就可以通过数据面访问推理服务。

客户端访问模型域名时,请求先到达 Envoy Gateway 管理的 Envoy Proxy。Envoy Proxy 应用 HTTPRoute/qwen-llm 的规则,并根据其中指向 Service/qwen-llm-predictorbackendRef,将请求发送到 Ready 的 Predictor Pod。这里的 Service 用于标识和发现后端,并不是一个独立的代理进程。Pod 中运行的是 HuggingFaceServer,当前配置使用 vLLM 作为推理后端。vLLM 完成推理后,响应沿原链路返回客户端。

1
2
3
4
5
6
Client
  -> Envoy Proxy(匹配 HTTPRoute)
  -> Predictor Pod(后端由 Predictor Service 标识)
  -> HuggingFaceServer
  -> vLLM Backend
  -> NVIDIA GPU

控制面决定服务应该如何部署,并保证它处于期望状态;数据面负责服务创建后的请求转发和模型推理。在线请求不会经过 KServe Controller。

2. 核心 CRD

KServe 通过 CRD 描述模型服务、运行时、推理图和模型存储初始化方式。对于上一篇的 Qwen Demo,最核心的是 InferenceService 和 ClusterServingRuntime。

二者之间的关系如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
InferenceService
  ├── 模型格式
  ├── 模型地址
  ├── 启动参数
  └── 资源需求
          |
          | 根据 modelFormat 匹配
          v
ServingRuntime / ClusterServingRuntime
  ├── 模型服务器镜像
  ├── 默认启动参数
  ├── 支持的模型格式
  └── 推理协议

2.1 InferenceService

InferenceService 用于描述一个模型服务需要什么,包括模型格式、模型地址、运行参数、副本数和资源需求。

一个最小的 InferenceService 如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: qwen-llm
spec:
  predictor:
    model:
      modelFormat:
        name: huggingface
      storageUri: pvc://qwen-model

InferenceService 的 Spec 可以包含以下三个逻辑部分:

组成部分是否必需作用
Predictor加载模型并执行预测或生成
Transformer请求预处理和响应后处理
Explainer生成模型解释

对于大模型推理,通常只需要 Predictor。上一篇的 Qwen 服务也只配置了 Predictor。

这里的 Predictor 是一个逻辑角色。到了 Standard 模式下,它最终会变成名为 qwen-llm-predictor 的 Deployment 和 Service。

2.2 ServingRuntime 和 ClusterServingRuntime

InferenceService 描述要运行什么模型,ServingRuntime 描述使用什么模型服务器运行。

Runtime API 按作用范围分为两种:

  • ServingRuntime 只在当前 Namespace 中生效
  • ClusterServingRuntime 则可以被整个集群中的 InferenceService 使用

一个简化后的 HuggingFace Runtime 如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
apiVersion: serving.kserve.io/v1alpha1
kind: ClusterServingRuntime
metadata:
  name: kserve-huggingfaceserver
spec:
  containers:
    - name: kserve-container
      image: kserve/huggingfaceserver:v0.18.0
  supportedModelFormats:
    - name: huggingface
      version: "1"
      autoSelect: true

containers 定义模型服务器的镜像和启动方式,supportedModelFormats 声明它支持哪些模型格式。

当 InferenceService 没有显式指定 Runtime 时,KServe 会根据 modelFormat、版本、协议以及 Runtime 的 autoSelectpriority 自动匹配。

因此:

1
2
modelFormat:
  name: huggingface

表示模型格式为 huggingface,KServe 会选择支持该格式的 Runtime。它并不表示 Pod 中只能使用 Hugging Face Transformers 进行推理。

2.3 其他 CRD

除了 InferenceService 和 Runtime,KServe 还提供了一些面向特定场景的 CRD:

  • InferenceGraph 编排多个 InferenceService,实现路由、分支和服务组合
  • ClusterStorageContainer 定义不同模型存储协议对应的初始化容器和匹配规则
  • TrainedModel 在 ModelMesh 场景中声明需要加载或卸载的模型

上一篇的 Demo 没有使用这些资源,后续遇到对应场景时再单独分析。

3. 核心组件

CRD 只描述期望状态,真正读取这些对象并创建模型服务的是 KServe 控制面。

3.1 Webhook

Webhook 位于资源写入 Kubernetes API 的入口。InferenceService Admission Webhook 负责补充默认值,并拒绝不符合 API 约束的配置;Pod Mutating Webhook 则会在 Pod 创建时注入模型存储相关配置。

在当前环境中,Webhook 和 Controller 运行在同一个 kserve-controller-manager Pod 中,但承担不同职责。

3.2 KServe Controller

KServe Controller 持续 Watch InferenceService,并通过 Reconcile 循环让实际状态与期望状态保持一致。

它主要负责:

  • 匹配 ServingRuntime 或 ClusterServingRuntime
  • 合并 Runtime 与 InferenceService 中的容器配置
  • 创建工作负载、Service 和网络入口
  • 处理模型存储和健康检查配置
  • 观察底层资源状态并回写 InferenceService Status

Controller 只负责资源编排。Deployment 创建以后,Pod 由 Kubernetes Scheduler 调度,GPU 由 NVIDIA Device Plugin 分配;Gateway Controller 负责让 HTTPRoute 生效,实际流量由 Envoy Proxy 转发。

3.3 Predictor Pod 内部结构

以上一篇的 Qwen 服务为例,Predictor Pod 内部的层次如下:

1
2
3
4
Predictor Pod
  └── HuggingFaceServer(容器主进程)
        └── vLLM Backend
              └── NVIDIA GPU

Predictor Pod 是模型服务的运行载体,容器主进程是 HuggingFaceServer。它负责启动模型服务并提供 OpenAI 兼容接口。

当前模型使用 vLLM 作为 HuggingFaceServer 的推理后端。HuggingFaceServer 会在同一个容器中创建 vLLM 引擎,由 vLLM 使用 GPU 执行推理。

4. Qwen Demo 工作流程

前面介绍的 CRD 和组件,最终会通过 Controller 的调谐过程串起来。

上一篇的 qwen-llm 使用 Standard 模式,下面只分析该模式下的资源创建和请求流程。

上一篇使用的核心配置如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: qwen-llm
  namespace: kserve-test
spec:
  predictor:
    model:
      modelFormat:
        name: huggingface
      storageUri: pvc://qwen-model
      args:
        - --model_name=qwen
        - --max_model_len=4096
        - --max-num-seqs=32
        - --gpu-memory-utilization=0.8
      resources:
        limits:
          nvidia.com/gpu: "1"

qwen-model PVC 是上一篇提前创建的,InferenceService 只负责引用它。

4.1 控制面资源创建流程

从提交 InferenceService 到服务 Ready,完整流程如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
1. 用户提交 InferenceService/qwen-llm
                     |
2. Webhook 默认化并校验配置
                     |
3. Controller 根据 huggingface 匹配 ClusterServingRuntime
                     |
4. 合并启动参数、镜像和 GPU 资源,并写入模型存储注解
                     |
5. 创建 Deployment、Service 和 HTTPRoute
                     |
6. Pod Mutating Webhook 注入 PVC volume 和 volumeMount
                     |
7. Kubernetes 调度 Pod、挂载 PVC 并分配 GPU
                     |
8. Pod 启动并加载模型,Controller 汇总状态并回写 InferenceService

可以从 InferenceService Status 查看最终选择结果:

1
2
kubectl get inferenceservice qwen-llm -n kserve-test \
  -o jsonpath='{.status.deploymentMode}{"\n"}{.status.clusterServingRuntimeName}{"\n"}'
1
2
Standard
kserve-huggingfaceserver

Controller 选择 kserve-huggingfaceserver,再把 InferenceService 中的参数和 GPU 资源合并到 Runtime 提供的容器模板中。最终 Deployment 使用 GPU 版本的 HuggingFaceServer 镜像:

1
docker.m.daocloud.io/kserve/huggingfaceserver:v0.18.0-gpu

Standard 模式下创建的资源关系如下:

1
2
3
4
5
6
7
InferenceService/qwen-llm
├── Deployment/qwen-llm-predictor
│   └── ReplicaSet
│       └── Pod
├── Service/qwen-llm-predictor
├── HTTPRoute/qwen-llm
└── HTTPRoute/qwen-llm-predictor

Deployment 可用、HTTPRoute 就绪后,Controller 会把底层状态汇总到 InferenceService:

1
2
3
4
5
6
7
8
status:
  conditions:
    - type: IngressReady
      status: "True"
    - type: PredictorReady
      status: "True"
    - type: Ready
      status: "True"

4.2 模型 Pod 启动流程

storageUri 描述模型的来源,不是容器内的最终路径,也不是 HuggingFaceServer 的启动参数。Controller 会将 pvc://qwen-model 写入 Pod 模板注解;ReplicaSet 创建 Pod 时,KServe Pod Mutating Webhook 读取该注解,将 qwen-model 解析为 PVC 名称,并向模型容器注入 PVC volume 和挂载到 /mnt/models 的 volumeMount。

如果这里使用 hf:// 地址,Webhook 则会注入 Storage Initializer init container,先从 Hugging Face Hub 下载模型,再通过共享的 EmptyDir volume 将模型提供给模型容器。

HuggingFaceServer 启动后,backend 默认为 auto。GPU 镜像中包含 vLLM;当 vLLM 可用且模型架构在 vLLM 的支持列表中时,HuggingFaceServer 会创建 vLLM 后端并从 /mnt/models 加载模型。

实际日志如下:

1
2
3
Initializing a V1 LLM engine (v0.19.0)
device_config=cuda
Starting to load model /mnt/models...

kserve-huggingfaceserver 是本次部署选择的 Runtime,vLLM 是 HuggingFaceServer 容器内自动选择的推理后端。因此,这次部署不需要单独指定 vLLM Runtime;集群中是否还存在其他自定义 vLLM Runtime,与这条执行链路无关。

4.3 在线请求流程

服务 Ready 后,上一篇的 OpenAI Chat Completions 请求沿着下面的路径进入模型:

1
2
3
4
5
6
Client
  -> Envoy Proxy(匹配 HTTPRoute/qwen-llm)
  -> Predictor Pod:8080(backendRef: Service/qwen-llm-predictor:80)
  -> HuggingFaceServer
  -> vLLM Backend
  -> NVIDIA GPU

这条请求链路属于数据面。KServe Controller 会继续维护 Deployment 和 InferenceService 状态,但不会参与请求转发。

5. 总结

结合上一篇的 Qwen Demo,KServe 的整个工作过程可以概括为:

KServe Standard 模式控制面与数据面架构

  • InferenceService 描述模型服务需要什么
    • modelFormat 声明模型格式,供 KServe 匹配 Runtime
    • storageUri 指定模型存储地址
    • args 设置模型服务器和推理引擎的启动参数
    • resources 声明 CPU、内存和 GPU 等资源需求
  • ClusterServingRuntime/kserve-huggingfaceserver 提供模型服务器镜像和启动方式
  • KServe Controller 匹配 Runtime、合并配置,并创建 Deployment、Service 和 HTTPRoute
  • Kubernetes 负责调度 Pod、挂载 PVC 和分配 GPU
  • Envoy Proxy 应用 HTTPRoute 规则,通过 Predictor Service 标识的后端将请求发送到 Ready Pod
  • HuggingFaceServer 使用 vLLM Backend,由 vLLM 通过 GPU 执行推理
0%