上一篇,我们终于把 vLLM 正式部署到了 Kubernetes。
通过 Deployment、Service、GPU Resource 和 PVC,我们已经可以让一个大语言模型:
在 Kubernetes 中运行;
使用 NVIDIA GPU 完成推理;
通过 Service 提供 API;
在 Pod 重建后恢复服务;
通过持久化存储复用模型文件。
到这里,我们实际上已经拥有了一个可以使用的 AI 推理服务。
那么问题来了:
既然 Deployment + Service 已经能够运行 vLLM,为什么还需要 KServe?
如果只有一个模型、一张 GPU、一台服务器,确实没有太大必要。
但假设以后需要运行:
Qwen 7B,用来处理聊天请求;
一个 Embedding 模型,用来生成向量;
一个 Reranker 模型,用来重新排序搜索结果;
几个不同版本的模型,用来进行灰度验证。
每个模型都需要自己的部署配置、运行环境、模型文件、健康检查和服务入口。
这时,我们很快就会发现:
Kubernetes 知道如何管理容器,却不知道这些容器代表什么模型。
Deployment 只关心期望的 Pod 数量。
Service 只关心怎样把请求转发给后端。
它们并不知道:
模型文件应该从哪里获取?
应该用 vLLM、Triton,还是其他推理引擎?
模型什么时候真正可以对外提供推理?
不同模型应该怎样使用统一的部署规范?
模型升级、版本切换和流量管理应该由谁协调?
换句话说:
Deployment + Service 解决了应用如何运行,而 KServe 希望解决模型如何作为一种服务被管理。
这就是本篇要研究的核心问题。
需要说明,本篇不重新介绍 vLLM 的 PagedAttention、KV Cache、GPU 调度和 PVC 基础知识。
我们重点研究 Kubernetes 原生工作负载之上的另一层:Model Serving Control Plane。
一、背景问题:运行模型容易,管理很多模型却不容易
1. 一个模型时,Deployment + Service 完全够用
回顾上一篇的架构:
Client | v Kubernetes Service | v Deployment | v vLLM Pod | v GPU对于单个模型,我们可以手工维护:
vllm-deployment.yaml vllm-service.yaml vllm-storage.yaml这套方案简单、直观,而且非常容易控制。
事实上,即使进入生产环境,也并不是所有模型服务都必须使用 KServe。
如果模型数量有限,团队又已经熟悉 Kubernetes,直接使用 Deployment + Service 仍然是合理方案。
但随着模型服务数量增加,问题就开始变化。
2. 当模型从一个变成十几个
假设现在要部署三个模型:
| 模型 | 推理引擎 | 主要任务 |
|---|---|---|
| Qwen2.5-7B | vLLM | Chat Completion |
| BGE Embedding | Hugging Face / 其他引擎 | 向量生成 |
| ONNX 模型 | ONNX Runtime / Triton | 分类预测 |
使用原生 Kubernetes 时,每个服务都需要分别配置。
例如 Qwen:
Deployment Service Model Storage GPU Resource Health CheckEmbedding 模型也类似。
ONNX 模型还是类似。
其中有很多配置属于通用基础设施,但具体模型的启动方式、资源需求、接口协议又可能各不相同。
如果全部依靠手工 YAML 管理,我们实际上是在不断重复一件事:
把模型的部署需求翻译成 Kubernetes 资源。
当模型数量继续增加,就可能希望建立统一的平台。
用户只需要告诉平台:
模型名称 模型存储位置 推理 Runtime 计算资源 副本数量其余工作由平台自动完成。
这就是 Model Serving Platform 的思路。
3. 为什么不能直接用 Helm?
这是一个很值得讨论的问题。
实际上,可以。
我们完全可以给 vLLM 编写一套 Helm Chart,把 Deployment、Service、PVC 等资源模板化。
然后通过:
helm install qwen ...部署模型。
所以 KServe 的价值绝不是简单地:
把很多 YAML 合并成一个 YAMLHelm 主要负责根据配置生成和安装 Kubernetes 资源。
KServe 则在此基础上引入了模型服务专用的 API、控制器和状态管理机制。
它可以持续观察:
Model Serving Desired State | v Actual State | v Reconcile这与我们前面研究 GPU Operator 时接触过的 Controller + Reconciliation 思想非常相似。
区别在于管理对象。
GPU Operator 管理的是 GPU Software Stack。
KServe 管理的是 Model Serving Workload。
这里也能看出 Kubernetes Operator 模式的一种通用价值:
不是让安装更方便,而是让某一类系统具备统一的、持续运行的管理逻辑。
二、原理:KServe 到底增加了什么?
KServe 是运行在 Kubernetes 之上的模型推理平台。
它不会取代 Kubernetes,也不会取代 vLLM。
更准确地说:
Kubernetes | +-- 通用应用管理 | +-- KServe | +-- Model Serving 管理要真正理解它,需要分清四个概念:
Model Serving Lifecycle
InferenceService
ServingRuntime / ClusterServingRuntime
LLMInferenceService
1. Model Serving Lifecycle:模型服务有自己的生命周期
上一篇已经介绍过 Pod 的生命周期。
但 Model Serving Lifecycle 并不等于 Pod Lifecycle。
一个 Pod 进入 Running 状态,只说明容器已经运行。
一个模型服务真正能够提供推理,还涉及更多阶段。
例如:
Model Definition | v Runtime Selection | v Resource Provisioning | v Model Preparation | v Engine Initialization | v Service Ready | v Traffic Serving | v Update / Scale / Terminate第一阶段是定义模型服务。
用户需要说明模型在哪里、使用什么 Runtime,以及需要多少计算资源。
第二阶段是选择 Runtime。
一个模型可能由 vLLM 运行,也可能由 Triton 或其他框架运行。
第三阶段是准备 Kubernetes 资源。
包括 Deployment、Service、存储、GPU 资源等。
第四阶段是准备模型。
模型可能来自 Hugging Face,也可能来自 S3、PVC 或其他存储系统。
第五阶段是初始化推理引擎。
这一部分仍然由具体 Runtime 负责。
最后才是对外提供推理 API。
KServe 的作用是把这些阶段组织成一个能够被声明、观察和维护的模型服务。
这里还要注意一个边界:
KServe 可以管理模型部署流程,但不代表它会自动理解每一种模型的内部初始化细节。
例如 vLLM 需要多长时间加载权重、怎样分配 KV Cache、如何调度 Token,仍然是推理引擎的责任。
KServe 主要通过配置、资源状态、探针及控制器来管理模型服务。
2. InferenceService:把模型变成 Kubernetes 的一种资源
先看 Deployment。
它是 Kubernetes 内置的一种资源:
apiVersion: apps/v1 kind: DeploymentKServe 则引入了自己的 Custom Resource:
apiVersion: serving.kserve.io/v1beta1 kind: InferenceService这背后使用的是 Kubernetes 的 CRD,也就是 CustomResourceDefinition。
我们以前已经接触过 CRD。
例如 GPU Operator 使用 ClusterPolicy 描述 GPU 软件栈的期望状态。
KServe 使用 InferenceService 描述模型服务的期望状态。
一个简化示意:
apiVersion: serving.kserve.io/v1beta1 kind: InferenceService metadata: name: my-model spec: predictor: model: modelFormat: name: huggingface storageUri: hf://example/model这段配置表达的不是:
启动一个普通容器而是:
部署一个 Hugging Face 模型推理服务KServe Controller 会根据它的配置,生成和维护所需的底层资源。
这里最重要的变化是:
用户管理的是 InferenceService,而不是直接管理每一个底层 Deployment。
当然,Deployment 并没有消失。
KServe 仍然使用 Kubernetes 原生能力完成实际部署。
它增加的是上层抽象和管理逻辑。
3. ServingRuntime:模型应该交给谁来运行?
有了 InferenceService,我们还需要解决另外一个问题:
同样是模型,运行方式可能完全不同。
例如:
LLM | +-- vLLM ONNX | +-- Triton / ONNX Runtime Traditional ML | +-- MLServerKServe 通过 ServingRuntime 定义模型的运行环境。
Runtime 中可以包含:
容器镜像;
启动命令和参数;
环境变量;
支持的模型格式;
健康检查;
运行相关配置。
KServe 提供两种作用域:
| 资源 | 作用域 |
|---|---|
| ServingRuntime | Namespace |
| ClusterServingRuntime | 整个 Cluster |
例如,一个管理员可以创建全局的 ClusterServingRuntime。
之后,不同 Namespace 中的模型服务可以复用同一种运行环境。
而某个团队如果希望使用自定义 Runtime,也可以定义 Namespace 范围内的 ServingRuntime。
这样,模型部署和 Runtime 管理就分开了。
可以理解为:
InferenceService | | 需要运行某个模型 v ServingRuntime | | 指定容器和执行环境 v Model Server | v Inference这里的 Runtime 还需要和前面 Kubernetes 实验中的 containerd 区分。
它们不是同一个层次。
KServe ServingRuntime 负责模型运行环境 containerd 负责容器生命周期更不能把 ServingRuntime 直接理解成 vLLM。
vLLM 是一种实际的推理引擎,而 ServingRuntime 是 KServe 对模型运行环境的定义。
KServe 的 Hugging Face Serving Runtime 可以使用 vLLM 作为后端执行引擎。
这正是本篇实验选择的方案。
4. InferenceService 和 Runtime 怎样协作?
我们可以把它们的职责进一步拆开。
InferenceService 主要描述:
我要部署什么模型? 模型存在哪里? 需要什么资源? 使用哪个 Runtime? 期望的服务状态是什么?ServingRuntime 则主要描述:
使用什么容器镜像? 怎样启动 Model Server? 支持什么模型格式? 使用什么运行参数?而 Kubernetes 继续负责:
Pod Scheduling Container Lifecycle Networking Storage Resource Allocation因此,整个体系形成了三个层次:
Model Serving API | v KServe Controller | v Kubernetes Resources | v Runtime Container | v Model Inference这里还有一个很重要的结论:
KServe 本身不提供 GPU 算力。
它最终还是需要依赖我们前面建立的 NVIDIA Device Plugin、GPU Operator 和 Kubernetes GPU Resource。
5. LLMInferenceService:为什么又要增加一个新的 CRD?
看到这里可能会有疑问。
InferenceService 已经可以部署 vLLM,为什么 KServe 后来还要设计:
kind: LLMInferenceService原因是传统预测模型和大语言模型之间的差异越来越大。
传统预测服务通常是:
Request | v Prediction | v Response但大语言模型的请求可能持续很长时间。
一次请求涉及:
Prompt | v Prefill | v Decode | v Streaming Tokens不同请求的 Context Length、KV Cache 占用、计算时间也可能差异很大。
假设我们有多个 vLLM Replica。
普通 Service 可以将请求分发给后端。
但是它通常不知道:
哪个 Replica 已经缓存了相关 Prefix?
哪个 Replica 的 KV Cache 压力更大?
哪个 Replica 的等待队列更长?
哪个请求更适合交给哪台 GPU?
这些信息都属于 LLM 特有的调度问题。
于是 KServe 进一步发展了 LLMInferenceService。
它与 llm-d 等组件结合,可以引入更适合 LLM 的基础设施能力。
例如:
Prefix-aware Routing
尽量把具有相同前缀的请求送到能够复用对应缓存的 Replica。
Load-aware Scheduling
根据后端负载等信息选择服务实例。
Prefill / Decode Disaggregation
把 Prefill 和 Decode 拆分成不同类型的服务实例,以便分别优化。
Distributed Inference
支持跨 GPU、跨 Node 的复杂模型部署。
这些能力不是创建一个 LLMInferenceService 对象就全部自动生效,而是需要匹配的 Scheduler、Gateway、Runtime 和硬件部署方案。
特别要强调:
LLMInferenceService 并不是 InferenceService 的简单改名。
二者目前是不同的 API 路线。
| 对比 | InferenceService | LLMInferenceService |
|---|---|---|
| 主要场景 | 通用 ML 和普通 LLM Serving | 面向 LLM 的复杂 Serving |
| 核心抽象 | Predictor、模型、Runtime | LLM Workload、Router、Scheduler |
| Runtime 组织 | ServingRuntime | LLMInferenceServiceConfig 等配置机制 |
| 普通单模型推理 | 适合 | 可以,但通常没必要 |
| Prefix-aware Routing | 非默认内建能力 | 可通过相关组件实现 |
| Prefill / Decode 分离 | 需要另外构建 | 专门支持相关部署模式 |
| 部署复杂度 | 较低 | 较高 |
对于目前这台 RTX 3060 实验机:
Single Node Single GPU Single Replica我们暂时没有必要引入整套 LLMInferenceService 高级架构。
因此,本篇先实验:
InferenceService + Standard Mode + vLLM。
三、架构图:KServe 怎样管理 vLLM?
把这次实验中的组件组合起来:
User / kubectl | v InferenceService | v Kubernetes API | v KServe Controller | Reconciliation | +---------+---------+ | | v v ClusterServingRuntime Deployment | | | v | vLLM Pod | | +-------------------+ | +-----------+-----------+ | | | v v v Model Data GPU Resource Model API | | | v v v HF / PVC Device Plugin Service | v RTX 3060其中需要区分两条逻辑链路。
第一条是控制链路:
InferenceService | v KServe Controller | v Deployment / Service用户通过 InferenceService 定义期望状态。
KServe Controller 通过 Reconciliation 管理底层资源。
第二条是请求链路:
Client | v Kubernetes Service / Gateway | v Model Server | v vLLM Engine | v GPU真正的推理请求并不需要先经过 KServe Controller。
Controller 负责管理服务,而不是逐个执行 Token 推理。
这就是典型的:
Control Plane + Data Plane分离架构。
和上一篇相比,新增的关键不是一条新的 GPU 执行路径。
而是围绕模型服务增加了专用的管理层。
四、实验:使用 KServe 部署 vLLM
1. 实验环境
继续使用前面已经搭建的 AI Infra 实验机。
| 项目 | 配置 |
|---|---|
| OS | Ubuntu 24.04 |
| CPU | Intel Core i5-10400F |
| GPU | NVIDIA RTX 3060 |
| 显存 | 12GB |
| Kubernetes | 1.37.x |
| Container Runtime | containerd |
| GPU 管理 | NVIDIA GPU Operator |
| Namespace | ai-infra |
| KServe | v0.20.0(本文示例固定版本) |
| 推理后端 | vLLM |
上一篇使用的是:
Qwen2.5-7B-Instruct-AWQ这次我们先用:
Qwen/Qwen2.5-0.5B-Instruct验证 KServe 的完整控制链路。
这里换成小模型,不是因为 KServe 无法运行 7B。
而是我们已经在上一篇验证过 7B 的 vLLM 推理能力,这次没有必要再次把精力投入到大型镜像、模型下载和显存调优上。
使用较小模型,可以把实验重点放在:
InferenceService | v Runtime Selection | v KServe Reconciliation | v Model Serving实验完成后,再考虑将已经验证过的 7B 模型迁移到 KServe。
需要注意,本篇提供的是按照官方接口设计的复现实验步骤;最终版本与数据应以实际实验机执行结果为准。
2. 先停止上一篇的 vLLM
我们只有一张 RTX 3060。
为了避免两个推理服务竞争 GPU,先停止旧的 Deployment:
kubectl scale deployment/vllm \ -n ai-infra \ --replicas=0检查:
kubectl get pods -n ai-infra再查看:
nvidia-smi确认旧的 vLLM 进程已经退出。
这里不删除上一篇的 Deployment,也不删除 PVC。
这样后面需要恢复原来的实验时,仍然可以继续使用。
3. 安装 KServe
首先确认:
kubectl get nodes helm version kubectl get clusterpolicyGPU Software Stack 应保持正常。
KServe 提供 Standard 和 Knative 两种主要的 InferenceService 部署模式。
Knative 模式适合需要相关 Serverless 能力的场景。
Standard 模式直接使用 Kubernetes Deployment 等资源。
对于长期运行、需要稳定占用 GPU 的 LLM 服务,Standard 模式通常更加直接。
本次使用:
KServe Standard Mode而不安装完整 Knative Serving。
KServe Standard 安装仍然需要 cert-manager 等依赖,以及与所选外部访问方案匹配的网络组件。
为了减少手工管理 CRD、Webhook 和 Controller 的步骤,可以使用官方的完整安装脚本。
如果有网络代理,需要检查 kube-apiserver:排除 Kubernetes 内部网络
如果执行kubeadm init时继承了系统代理,代理变量可能被写入/etc/kubernetes/manifests/kube-apiserver.yaml。
检查其中的no_proxy/NO_PROXY,确认包含实际的 Service CIDR、Pod CIDR 和内部域名。例如:
- name: no_proxy value: "127.0.0.1,localhost,LAN_CIDR,10.96.0.0/12,10.244.0.0/16,.svc,.cluster.local"其中LAN_CIDR替换为实际局域网网段。如果同时配置了大小写两种变量,应保持内容一致。
修改静态 Pod Manifest 后,kubelet 会自动重建 kube-apiserver,期间 Kubernetes API 可能短暂中断。恢复后检查:
kubectl get nodes kubectl get --raw='/readyz'本文使用 v0.20.0 对应脚本:
curl -fL \ -o kserve-install.sh \ https://github.com/kserve/kserve/releases/download/v0.20.0/kserve-standard-mode-full-install-with-manifests.sh建议先检查脚本内容及其准备安装的依赖:
less kserve-install.sh确认后执行:
bash kserve-install.sh这不是一个只创建 KServe Deployment 的轻量命令。
完整安装还可能部署证书和网络相关组件,实际资源需求要结合自己的单节点集群评估。
另外,KServe 安装不会替我们重新配置 NVIDIA Driver 或 GPU Operator。
此前建立的 GPU 软件栈继续保留。
安装后检查:
kubectl get pods -n kserve确认 Controller 正常运行。
然后检查 KServe CRD:
kubectl get crd | grep kserve重点查找:
inferenceservices.serving.kserve.io servingruntimes.serving.kserve.io clusterservingruntimes.serving.kserve.io这一步很重要。
因为在安装 KServe 之前,Kubernetes API Server 并不认识这些资源。
只有 CRD 注册以后,下面的命令才有意义:
kubectl get inferenceservices -A4. 检查 ClusterServingRuntime
执行:
kubectl get clusterservingruntimes正常安装内置 Runtime 配置后,通常可以看到:
kserve-huggingfaceserver kserve-tritonserver kserve-mlserver ...如果没有找到内置 Runtime,需要检查 Runtime 资源是否安装。
对于本文固定的 v0.20.0 版本,可以按照官方安装文档补充相应的 Cluster Resources:
kubectl apply --server-side \ -f https://github.com/kserve/kserve/releases/download/v0.20.0/kserve-cluster-resources.yaml再执行:
kubectl get clusterservingruntime \ kserve-huggingfaceserver -o yaml观察:
supportedModelFormats:以及其中定义的容器镜像、运行参数等。
这次先使用 KServe 提供的 Hugging Face Runtime。
它可以使用 vLLM 作为推理后端,因此我们不需要重新构建自己的 vLLM 镜像。
这里有一个特别值得注意的地方:
KServe 不只是“部署一份 vLLM YAML”,而是可以通过 Runtime 定义复用模型服务器的运行方式。
5. 创建 InferenceService
如果使用网络代理,这里需要检查下 containerd:确认镜像拉取代理
sudo systemctl show containerd -p Environment如果尚未设置代理,可通过 systemd drop-in 配置:
[Service] Environment="HTTP_PROXY=http://PROXY_IP:PROXY_PORT" Environment="HTTPS_PROXY=http://PROXY_IP:PROXY_PORT" Environment="NO_PROXY=127.0.0.1,localhost,LAN_CIDR,10.96.0.0/12,10.244.0.0/16,.svc,.cluster.local"保存到/etc/systemd/system/containerd.service.d/proxy.conf,然后执行:
sudo systemctl daemon-reload sudo systemctl restart containerd以上网段仅为示例,需要按照实际 Kubernetes 网络配置调整。
另外需要检查下 Storage Initializer:让模型下载使用代理
KServe 安装完成后、创建第一个 InferenceService 之前,如果模型需要从 Hugging Face 下载,还应为默认的ClusterStorageContainer配置代理。
kubectl edit clusterstoragecontainer default在spec.container下加入:
env: - name: HTTP_PROXY value: "http://PROXY_IP:PROXY_PORT" - name: HTTPS_PROXY value: "http://PROXY_IP:PROXY_PORT" - name: NO_PROXY value: "127.0.0.1,localhost,LAN_CIDR,10.96.0.0/12,10.244.0.0/16,.svc,.cluster.local"保留原有的镜像、资源限制、SecurityContext 等字段。该配置会应用到后续创建的模型下载 Init Container;如果已经创建了失败的 Predictor Pod,则需要重建 Pod 才能应用新环境变量。注意,使用emptyDir存储模型时,重建 Pod 会丢失已下载的模型。
这样,在运行 KServe 安装脚本、创建 InferenceService 和下载模型的过程中,就能避免一些常见的代理问题。
然后正式创建:
nano qwen-kserve.yaml填写:
apiVersion: serving.kserve.io/v1beta1 kind: InferenceService metadata: name: qwen-kserve namespace: ai-infra spec: predictor: model: modelFormat: name: huggingface runtime: kserve-huggingfaceserver storageUri: hf://Qwen/Qwen2.5-0.5B-Instruct args: - --model_name=qwen - --backend=vllm - --max_model_len=2048 - --gpu_memory_utilization=0.80 - --enforce_eager resources: requests: cpu: "1" memory: 4Gi nvidia.com/gpu: "1" limits: cpu: "2" memory: 6Gi nvidia.com/gpu: "1"其中:
kind: InferenceService表示我们创建的是模型服务资源。
modelFormat: name: huggingface让 KServe 按对应格式识别模型。
runtime: kserve-huggingfaceserver显式选择 Hugging Face Serving Runtime。
而:
--backend=vllm则要求这个 Runtime 使用 vLLM 推理后端。
因此,我们实际使用的是:
InferenceService | v Hugging Face ServingRuntime | v vLLM Backend | v RTX 3060storageUri指定 Hugging Face Hub 模型仓库。
KServe 会按照相应存储集成流程准备模型文件。
这里需要说明:本次小模型由 KServe 的存储流程准备,并未复用上一篇专门为 7B AWQ 建立的 PVC。
这两个实验的缓存位置不能直接视为相同。
最后的 GPU Resource 仍然是:
nvidia.com/gpu: "1"这一部分没有任何新机制。
它还是通过我们此前安装的 NVIDIA Device Plugin 来实现。
这些参数只是本次单 GPU 实验的初始配置,实际是否适合当前 Runtime 镜像还需要通过日志验证。
6. 部署模型
执行:
kubectl apply -f qwen-kserve.yaml现在出现了与上一篇明显不同的操作体验。
上一篇我们分别创建:
Deployment Service这一次首先只提交:
InferenceService观察:
kubectl get isvc -n ai-infra -w同时另开 Terminal:
kubectl get deployment,svc,pods \ -n ai-infra我们要观察的核心现象是:
在没有手工创建新 Deployment 的情况下,KServe 是否自动建立了需要的底层资源?
再执行:
kubectl describe isvc qwen-kserve \ -n ai-infra关注 Status 中的 Conditions。
当模型准备完成、后端健康检查通过,并且相应服务状态满足要求后,InferenceService 才能够进入 Ready 状态。
这与单纯看到 Pod Running 并不相同。
7. 验证真正使用了 vLLM
找到生成的 Pod:
kubectl get pods -n ai-infra然后查看实际日志:
kubectl logs \ -n ai-infra \ <KServe创建的Pod名称> \ -c kserve-container实际容器名称应以 Pod Spec 为准。
我们需要确认:
模型加载成功 vLLM Backend 正常初始化 推理 API 已经启动注意,不能仅凭:
modelFormat: name: huggingface就认为一定使用了 vLLM。
必须确认对应 Runtime 和实际后端。
这是因为模型格式、Serving Runtime 和推理引擎属于不同概念。
这也是此次实验最值得观察的地方之一。
8. 调用 OpenAI-compatible API
KServe Hugging Face Runtime 提供 OpenAI-compatible API。
但其默认 API 路径与上一篇直接运行 vLLM 时存在一个值得注意的区别。
上一篇直接运行 vLLM:
/v1/chat/completions而 KServe Hugging Face Runtime 的默认 OpenAI 路由通常是:
/openai/v1/chat/completions这个前缀来自 Runtime 的 API 配置,并不是所有 KServe 部署都必须使用它。
首先查看 Service:
kubectl get svc -n ai-infra找到 KServe 创建的 Predictor Service。
在本地测试时,可以先尝试:
kubectl port-forward \ -n ai-infra \ svc/qwen-kserve-predictor \ 8080:80Service 的实际名称与端口,以集群中生成的资源为准。
另一个 Terminal:
curl http://localhost:8080/openai/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen", "messages": [ { "role": "user", "content": "请用一句话解释 KServe 的作用" } ], "max_tokens": 100, "temperature": 0.2 }'如果能够得到正常的 Chat Completion 响应,就说明:
InferenceService | v KServe Managed Service | v ServingRuntime | v vLLM | v GPU Inference这条链路已经跑通。
这里先通过 Port Forward 验证内部服务。
它并不等于已经配置完整的外部 Gateway、域名、认证或生产访问链路。
五、观察数据:KServe 究竟改变了什么?
上一篇我们已经观察过:
Pod Startup Time Engine Initialization Time GPU Memory Usage这次不再重点研究这些数字。
因为换成 KServe 后,最有价值的问题不是:
KServe 会不会让模型推理更快?而是:
KServe 到底帮我们管理了什么?1. 观察控制器生成的资源
建议首先记录下面这张表。
| 观察项目 | 预期现象 | 实验结果 |
|---|---|---|
| InferenceService CRD | 已注册 | 已注册 |
| ClusterServingRuntime | 可查询 | 可查询 |
| InferenceService | 对象创建成功 | 对象创建成功 |
| Deployment | KServe 自动创建 | KServe 自动创建 |
| Service | KServe 自动创建 | KServe 自动创建 |
| Pod | 被调度并启动 | 被调度并启动 |
| InferenceService Ready | 最终为 True | 最终为 True |
| vLLM Backend | 正常启动 | 正常启动 |
| OpenAI API | 请求成功 | 请求成功 |
可以使用:
kubectl get isvc,deploy,svc,pods \ -n ai-infra同时观察这些资源。
随后:
kubectl get isvc qwen-kserve \ -n ai-infra -o yaml查看 KServe 记录的状态。
2. 观察 Model Serving 的管理时间
我们还可以记录从创建 InferenceService 到 Ready 的总耗时。
例如:
T0=$(date +%s) kubectl apply -f qwen-kserve.yaml kubectl wait \ -n ai-infra \ --for=condition=Ready \ isvc/qwen-kserve \ --timeout=1800s T1=$(date +%s) echo "InferenceService Ready: $((T1-T0)) s"这里记录的是:
提交 InferenceService | v KServe Reconciliation | v 底层资源创建 | v 模型准备 | v Runtime 初始化 | v InferenceService Ready它包括多个阶段,不能理解成 KServe Controller 自身的执行时间。
也不能直接与上一篇的 7B 模型启动耗时比较。
因为本次模型大小、镜像、存储流程及 Runtime 都可能不同。
真正想比较 Deployment 和 KServe 的额外开销,需要控制这些变量。
否则得到的数字没有足够的解释力。
3. 观察 KServe 的 Reconciliation
这是本篇比上一篇更值得做的实验。
当 InferenceService 已经 Ready 后,找到 KServe 管理的 Predictor Deployment。
执行:
kubectl get deployment -n ai-infra记录它的名称、Label 和 OwnerReferences。
然后只删除由 KServe 创建的 Deployment:
kubectl delete deployment \ <KServe创建的Deployment名称> \ -n ai-infra再观察:
kubectl get deployment -n ai-infra -w在正常的控制器工作流程下,KServe 应重新协调资源,使底层 Deployment 恢复到 InferenceService 所声明的期望状态。
这就是:
Desired State | v KServe Controller | v Actual State不断进行 Reconciliation 的体现。
当然,需要注意两点。
第一,普通 Deployment Controller 同样会通过 ReplicaSet 维护 Pod。
所以仅仅观察 Pod 被重建,并不能证明 KServe 比 Deployment 多了什么。
第二,我们真正要验证的是:
当 KServe 管理的 Deployment 资源本身被删除后,上层 InferenceService 是否仍然能够恢复完整服务。
这才是此次实验想观察的控制器边界。
这个测试应在没有其他业务访问时进行,因为删除 Deployment 会造成服务中断。
4. 再观察模型服务与底层资源的关系
通过:
kubectl get isvc qwen-kserve \ -n ai-infra -o yaml观察期望状态。
再通过:
kubectl get deployment -n ai-infra -o yaml观察底层实际资源。
我们会发现:
InferenceService 并没有神奇地消除 Deployment。
它只是让 Deployment 从用户需要直接维护的主要对象,变成 KServe 管理的底层资源。
这就是最关键的区别。
六、KServe 能不能取代我们此前的部署方案?
完成实验以后,还有一个现实问题:
既然前面已经把 Qwen2.5-7B-Instruct-AWQ 跑通了,是否应该把那个服务也迁移到 KServe?
从技术角度看,可以。
但没有必要立即删除现有配置。
原因很简单。
当前两套方案:
Deployment + Service和:
KServe InferenceService都能够运行模型。
区别在于平台管理能力,而不是底层推理引擎天然发生了变化。
如果迁移 7B 模型,我们真正需要处理的是:
使用兼容 AWQ 的 Runtime 镜像;
保持原来验证过的 vLLM 参数;
复用已有模型文件,而不是无意义地重复下载;
确保单张 RTX 3060 不会同时启动两个大型推理实例;
检查新的模型 API 和原来是否兼容。
对于第三点,可以考虑 KServe 支持的 PVC Storage URI,或者定制 ServingRuntime 使用已有模型缓存。
但这里有一个实际细节:
上一篇 Hugging Face Cache 使用的是带有blobs、refs和snapshots的缓存结构。
它不一定等同于可以直接挂载到/mnt/models的完整模型目录。
因此,不能简单把 HF Cache 中任意一个 Snapshot 目录当成独立模型目录挂载。
需要保证模型权重、配置文件以及符号链接引用的文件均可正常访问。
本次先不重新下载 7B,也不对已经稳定工作的部署做不必要的改动。
这更符合实验室逐步增加新能力的原则。
七、总结:KServe 的价值不在于帮我们少写 YAML
回顾这一篇,最开始的问题是:
Deployment + Service 已经能够部署 vLLM,为什么还需要 KServe?
现在答案已经比较清楚。
对于一个模型:
Deployment + Service完全可以满足需求。
但对于一个需要管理大量模型的 AI Platform,仅仅使用通用 Kubernetes 资源,还需要我们自己构建大量模型服务相关的逻辑。
KServe 试图提供这些通用能力。
它引入:
InferenceService
把模型服务变成 Kubernetes 可以声明和管理的资源。
ServingRuntime
把模型的定义与具体推理运行环境分离。
KServe Controller
将模型服务的期望状态转换成 Kubernetes 资源,并持续进行 Reconciliation。
LLMInferenceService
进一步面向 LLM 的复杂路由、调度和分布式推理需求。
不过,这里也有几个边界值得记住。
KServe 不会取代 Kubernetes Scheduler。
KServe 不会取代 NVIDIA Device Plugin。
KServe 不会取代 vLLM 的推理调度。
也不会因为安装 KServe,就让 RTX 3060 自动拥有更多 GPU 算力或者显存。
它提供的是另一种能力:
让模型部署从每个团队自行维护的 Kubernetes Workload,逐渐变成平台可以统一管理的 Model Serving Resource。
从基础设施角度看,这实际上是又一次抽象层次的提升。
我们最早研究的是:
GPU 能不能运行 CUDA 程序?随后变成:
Kubernetes 能不能管理 GPU?再后来是:
vLLM 能不能作为 Kubernetes Service 运行?现在则是:
多个模型服务, 能不能交给一个统一的平台管理?KServe 正是在回答最后这个问题。
对于目前的单节点个人实验室,它带来的直接收益可能还不明显。
甚至增加了额外 Controller、CRD 和网络依赖。
但如果将来扩展为多模型、多用户、多节点的 AI Infra,这些模型级别的抽象就会越来越重要。
八、下一篇预告
这一篇,我们通过 KServe 将 vLLM 部署到了 Kubernetes,并完成了从 InferenceService 创建、模型下载、GPU 调度到推理 API 调用的完整实验。
但目前的模型服务只有一个 Pod。如果请求量突然增加,Kubernetes 能否自动扩容?新的 Pod 需要重新下载模型吗?如果模型服务长时间没有请求,是否可以缩容以节省 GPU 资源?而再次启动时,又需要付出多少时间成本?
这些问题,已经不只是如何把模型运行起来,而是如何让模型服务更高效地使用计算资源。
下一篇,我们将继续研究模型服务的自动扩缩容、冷启动成本,以及模型缓存如何影响 Pod 的启动速度。
参考资料
KServe 官方文档:KServe 架构、核心能力。
KServe Installation:Standard 与 Knative 安装方式。
KServe Serving Runtime:ServingRuntime、ClusterServingRuntime 与 Runtime 选择。
KServe Generative Inference:Hugging Face Runtime 与 vLLM 后端。
KServe LLMInferenceService:LLM 专用服务抽象、Scheduler 和分布式推理。
KServe PVC Storage:通过 PVC 提供模型文件。
KServe GitHub Releases:版本发布与变更记录。