上一篇我们终于把 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-7BvLLMChat CompletionBGE EmbeddingHugging Face / 其他引擎向量生成ONNX 模型ONNX Runtime / Triton分类预测使用原生 Kubernetes 时每个服务都需要分别配置。例如 QwenDeployment 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 LifecycleInferenceServiceServingRuntime / ClusterServingRuntimeLLMInferenceService1. 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 ResourceapiVersion: 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 提供两种作用域资源作用域ServingRuntimeNamespaceClusterServingRuntime整个 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 路线。对比InferenceServiceLLMInferenceService主要场景通用 ML 和普通 LLM Serving面向 LLM 的复杂 Serving核心抽象Predictor、模型、RuntimeLLM Workload、Router、SchedulerRuntime 组织ServingRuntimeLLMInferenceServiceConfig 等配置机制普通单模型推理适合可以但通常没必要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 部署 vLLM1. 实验环境继续使用前面已经搭建的 AI Infra 实验机。项目配置OSUbuntu 24.04CPUIntel Core i5-10400FGPUNVIDIA RTX 3060显存12GBKubernetes1.37.xContainer RuntimecontainerdGPU 管理NVIDIA GPU OperatorNamespaceai-infraKServev0.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先停止旧的 Deploymentkubectl scale deployment/vllm \ -n ai-infra \ --replicas0检查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 CRDkubectl 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 Resourceskubectl 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] EnvironmentHTTP_PROXYhttp://PROXY_IP:PROXY_PORT EnvironmentHTTPS_PROXYhttp://PROXY_IP:PROXY_PORT EnvironmentNO_PROXY127.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_nameqwen - --backendvllm - --max_model_len2048 - --gpu_memory_utilization0.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。而--backendvllm则要求这个 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同时另开 Terminalkubectl 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找到生成的 Podkubectl 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 APIKServe Hugging Face Runtime 提供 OpenAI-compatible API。但其默认 API 路径与上一篇直接运行 vLLM 时存在一个值得注意的区别。上一篇直接运行 vLLM/v1/chat/completions而 KServe Hugging Face Runtime 的默认 OpenAI 路由通常是/openai/v1/chat/completions这个前缀来自 Runtime 的 API 配置并不是所有 KServe 部署都必须使用它。首先查看 Servicekubectl get svc -n ai-infra找到 KServe 创建的 Predictor Service。在本地测试时可以先尝试kubectl port-forward \ -n ai-infra \ svc/qwen-kserve-predictor \ 8080:80Service 的实际名称与端口以集群中生成的资源为准。另一个 Terminalcurl 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对象创建成功对象创建成功DeploymentKServe 自动创建KServe 自动创建ServiceKServe 自动创建KServe 自动创建Pod被调度并启动被调度并启动InferenceService Ready最终为 True最终为 TruevLLM 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 \ --forconditionReady \ isvc/qwen-kserve \ --timeout1800s 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 创建的 Deploymentkubectl 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 InstallationStandard 与 Knative 安装方式。KServe Serving RuntimeServingRuntime、ClusterServingRuntime 与 Runtime 选择。KServe Generative InferenceHugging Face Runtime 与 vLLM 后端。KServe LLMInferenceServiceLLM 专用服务抽象、Scheduler 和分布式推理。KServe PVC Storage通过 PVC 提供模型文件。KServe GitHub Releases版本发布与变更记录。