单节点 Kubernetes 实战:使用 KServe 部署 Qwen2.5,并通过 Gateway API 暴露 vLLM 推理服务
一、文章背景
前面已经单独实践过 vLLM 模型服务部署,也单独学习过 Gateway API 的 Gateway、HTTPRoute、Header 匹配和流量转发能力。
本文章的目标是把 KServe 引入进来,通过 KServe 的 InferenceService 来管理模型服务,再通过 Gateway API 创建新的 Gateway 和 HTTPRoute,将外部请求转发到 KServe 生成的模型服务上。
从实际请求转发角度看,请求链路可以理解为:
curl 请求
-> Envoy Gateway 数据面 Service / Pod
-> 根据 Gateway Listener 和 HTTPRoute 规则匹配 Host、Path
-> 转发到 qwen-vllm-predictor Service
-> Service 转发到 qwen-vllm-predictor Pod
-> Pod 内部 vLLM 服务处理 OpenAI API 请求
-> 加载本地 Qwen2.5-1.5B-Instruct 模型完成推理
本文最终实现的效果是:
curl -H "Host: llm.example.local" http://127.0.0.1:8888/v1/models
可以正常返回 vLLM 模型列表。并且:
curl -H "Host: llm.example.local" \
http://127.0.0.1:8888/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen2.5-1.5b-instruct",
"messages": [
{
"role": "user",
"content": "1+1等于几?"
}
],
"max_tokens": 64,
"temperature": 0
}'
可以正常返回模型推理结果。
二、实验环境说明
本文环境是单节点 Kubernetes,节点上有一张 NVIDIA RTX 3070 Ti 显卡,模型文件已经提前放在宿主机 /data/models 目录下。
本次实验中涉及的核心组件如下:
| 组件 | 作用 |
|---|---|
| Kubernetes | 模型服务运行的基础平台 |
| KServe | 通过 InferenceService 管理模型服务 |
| vLLM | 负责加载 Qwen 模型并提供 OpenAI-compatible API |
| Envoy Gateway | Gateway API 的实现,负责创建 Envoy 数据面 |
| Gateway API | 通过 Gateway 和 HTTPRoute 定义流量入口和路由规则 |
| Volcano | 本文模型 Pod 使用 schedulerName: volcano 调度 |
| NVIDIA RuntimeClass | 本文模型 Pod 使用 runtimeClassName: nvidia 运行 GPU 容器 |
本次使用的模型和镜像如下:
模型名称:Qwen2.5-1.5B-Instruct
模型路径:/data/models/Qwen2.5-1.5B-Instruct
推理镜像:docker.m.daocloud.io/vllm/vllm-openai:latest
服务模型名:qwen2.5-1.5b-instruct
vLLM 容器监听端口:8080
本文使用的是 KServe Standard 模式。
KServe 的 Standard 模式会基于标准 Kubernetes 资源创建模型服务,例如 Deployment 和 Service。这个模式更适合需要明确控制资源、GPU、调度和监控的场景。本文后续通过 kubectl get deploy 和 kubectl get svc 也可以看到,KServe 最终确实为这个 InferenceService 创建了 qwen-vllm-predictor Deployment 和 Service。
三、安装 cert-manager、KServe,并切换到 Standard 模式
KServe 安装过程中会涉及 webhook,cert-manager 用于为 webhook 相关组件提供证书能力。因此在安装 KServe 之前,先安装 cert-manager。
1. 安装 cert-manager
执行:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.20.2/cert-manager.yaml
查看 cert-manager Pod 状态:
kubectl get pods -n cert-manager
也可以使用:
kubectl -n cert-manager get pod
实际输出如下:
NAME READY STATUS RESTARTS AGE
cert-manager-68756bcf6f-5vxnx 1/1 Running 0 4m38s
cert-manager-cainjector-c664cf9b8-mvc2r 1/1 Running 0 4m38s
cert-manager-webhook-5749c6dc95-s5zxv 1/1 Running 0 4m38s
这三个 Pod 都是 Running 后,再继续安装 KServe。
2. 安装 KServe 0.18
执行:
kubectl apply --server-side -f https://github.com/kserve/kserve/releases/download/v0.18.0/kserve.yaml
继续安装 KServe 默认的集群级资源:
kubectl apply --server-side -f https://github.com/kserve/kserve/releases/download/v0.18.0/kserve-cluster-resources.yaml
这里使用 --server-side 是因为 KServe 的 InferenceService CRD 比较大,官方安装文档中也使用了这种方式。
安装完成后,先查看 KServe 相关 Service:
kubectl -n kserve get svc
实际输出如下:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kserve-controller-manager-metrics-service ClusterIP 10.102.117.218 <none> 8443/TCP 41m
kserve-controller-manager-service ClusterIP 10.105.184.1 <none> 8443/TCP 41m
kserve-webhook-server-service ClusterIP 10.106.18.105 <none> 443/TCP 41m
llmisvc-controller-manager-service ClusterIP 10.104.248.102 <none> 8443/TCP 41m
llmisvc-webhook-server-service ClusterIP 10.98.235.198 <none> 443/TCP 41m
localmodel-webhook-server-service ClusterIP 10.102.151.218 <none> 443/TCP 41m
然后确认 webhook Service 后面有 Endpoints:
kubectl -n kserve get endpoints | grep webhook
输出如下:
Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice
kserve-webhook-server-service 10.244.0.44:9443 41m
llmisvc-webhook-server-service 10.244.0.46:9443 41m
localmodel-webhook-server-service 10.244.0.45:9443 41m
3. KServe 镜像拉取策略说明
安装 KServe 时需要注意一个问题:有些 KServe 组件的镜像拉取策略可能是 Always。
如果集群节点可以正常访问外部镜像仓库,一般不会有问题。但如果你的环境不能直接拉取镜像,而是从其他机器把镜像导入到本机,那么即使本地已经存在镜像,Pod 仍然可能因为 imagePullPolicy: Always 继续尝试访问远端仓库,从而出现 ImagePullBackOff 或 ErrImagePull。
可以先查看 KServe Deployment 中的镜像拉取策略:
kubectl -n kserve get deploy
然后查看具体 Deployment:
kubectl -n kserve get deploy kserve-controller-manager -o yaml | grep -n "imagePullPolicy"
如果发现是:
imagePullPolicy: Always
并且当前节点已经提前导入了对应镜像,可以将其改成:
imagePullPolicy: IfNotPresent
例如:
kubectl -n kserve edit deploy kserve-controller-manager
找到容器部分,将:
imagePullPolicy: Always
修改为:
imagePullPolicy: IfNotPresent
然后保存退出。
如果 llmisvc-controller-manager 等其他 KServe 组件也存在相同问题,也需要按同样方式检查和修改:
kubectl -n kserve get deploy
kubectl -n kserve edit deploy <deployment-name>
4. 将 KServe 切换到 Standard 模式
先查看 KServe 的 inferenceservice-config:
kubectl get configmap inferenceservice-config -n kserve
然后 patch 配置,将默认部署模式切换为 Standard:
kubectl patch configmap/inferenceservice-config \
-n kserve \
--type=strategic \
-p '{"data": {"deploy": "{\"defaultDeploymentMode\": \"Standard\"}"}}'
重启 KServe Controller,让配置生效:
kubectl rollout restart deployment/kserve-controller-manager -n kserve
kubectl rollout status deployment/kserve-controller-manager -n kserve
查看配置是否已经变更:
kubectl get configmap inferenceservice-config \
-n kserve \
-o jsonpath='{.data.deploy}'; echo
如果返回内容中包含:
{"defaultDeploymentMode": "Standard"}
说明全局默认部署模式已经切换为 Standard。
在后面的 InferenceService YAML 中,我仍然显式加了下面这个 annotation:
serving.kserve.io/deploymentMode: Standard
这样可以让这个模型服务明确使用 Standard 模式,避免受到其他默认配置影响。
四、使用 InferenceService 部署 Qwen2.5-1.5B-Instruct
1. 创建实验命名空间
执行:
kubectl create ns kserve-demo
2. 编写 InferenceService YAML
创建文件:
vim qwen-vllm-isvc.yaml
内容如下:
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
name: qwen-vllm
namespace: kserve-demo
annotations:
serving.kserve.io/deploymentMode: Standard
spec:
predictor:
minReplicas: 1
# 如果 kubectl get runtimeclass 里没有 nvidia,就删除这一行
runtimeClassName: nvidia
# hostPath 是宿主机路径,所以必须让 Pod 调度到有 /data/models 的节点
nodeSelector:
kubernetes.io/hostname: master-01
schedulerName: volcano
containers:
- name: kserve-container
image: docker.m.daocloud.io/vllm/vllm-openai:latest
imagePullPolicy: IfNotPresent
args:
- --model
- /data/models/Qwen2.5-1.5B-Instruct
- --served-model-name
- qwen2.5-1.5b-instruct
- --host
- 0.0.0.0
- --port
- "8080"
- --dtype
- auto
- --max-model-len
- "4096"
- --gpu-memory-utilization
- "0.75"
ports:
- containerPort: 8080
protocol: TCP
resources:
requests:
cpu: "2"
memory: 8Gi
limits:
cpu: "4"
memory: 16Gi
volcano.sh/vgpu-number: "1"
volumeMounts:
- name: model-dir
mountPath: /data/models
readOnly: true
- name: shm
mountPath: /dev/shm
startupProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 90
readinessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 10
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 30
failureThreshold: 3
volumes:
- name: model-dir
hostPath:
path: /data/models
type: Directory
- name: shm
emptyDir:
medium: Memory
sizeLimit: 4Gi
3. YAML 关键字段说明
deploymentMode
annotations:
serving.kserve.io/deploymentMode: Standard
表示这个 InferenceService 使用 KServe Standard 模式部署。Standard 模式下,KServe 会创建标准 Kubernetes Deployment 和 Service,而不是 Knative Service。
runtimeClassName
runtimeClassName: nvidia
表示 Pod 使用名为 nvidia 的 RuntimeClass。可以通过下面命令查看当前集群是否存在:
kubectl get runtimeclass
如果你的集群中没有 nvidia RuntimeClass,就需要删除这一行,或者先正确安装 NVIDIA Container Runtime / GPU Operator 相关组件。
nodeSelector
nodeSelector:
kubernetes.io/hostname: master-01
这里使用了 hostPath 挂载模型目录:
hostPath:
path: /data/models
hostPath 是宿主机路径,不是共享存储。因此必须保证 Pod 调度到拥有 /data/models 目录的节点上。本文是单节点环境,节点名是 master-01,所以直接通过 nodeSelector 固定调度到这个节点。
schedulerName
schedulerName: volcano
表示这个 Pod 交给 Volcano Scheduler 调度。本文环境前面已经部署过 Volcano,并且做过 Volcano 和 HAMi/vGPU 相关实验,所以这里继续使用 Volcano 作为调度器。
vLLM 启动参数
args:
- --model
- /data/models/Qwen2.5-1.5B-Instruct
- --served-model-name
- qwen2.5-1.5b-instruct
- --host
- 0.0.0.0
- --port
- "8080"
- --dtype
- auto
- --max-model-len
- "4096"
- --gpu-memory-utilization
- "0.75"
含义如下:
| 参数 | 说明 |
|---|---|
--model |
指定模型路径 |
--served-model-name |
暴露给 OpenAI API 的模型名 |
--host 0.0.0.0 |
监听所有网卡 |
--port 8080 |
vLLM 服务监听端口 |
--dtype auto |
自动选择数据类型 |
--max-model-len 4096 |
最大上下文长度 |
--gpu-memory-utilization 0.75 |
控制 vLLM 使用 GPU 显存的比例 |
vGPU 资源
resources:
requests:
cpu: "2"
memory: 8Gi
limits:
cpu: "4"
memory: 16Gi
volcano.sh/vgpu-number: "1"
这里的:
volcano.sh/vgpu-number: "1"
表示向 Volcano vGPU 相关资源申请 1 个 vGPU 设备。
如果你的环境没有部署 Volcano vGPU 相关能力,需要改成普通 NVIDIA GPU 资源,例如:
nvidia.com/gpu: "1"
本文环境已经做过 Volcano vGPU 实验,因此这里继续使用:
volcano.sh/vgpu-number: "1"
模型目录挂载
volumeMounts:
- name: model-dir
mountPath: /data/models
readOnly: true
宿主机路径:
volumes:
- name: model-dir
hostPath:
path: /data/models
type: Directory
容器中通过 /data/models/Qwen2.5-1.5B-Instruct 访问模型。
/dev/shm
volumeMounts:
- name: shm
mountPath: /dev/shm
对应:
volumes:
- name: shm
emptyDir:
medium: Memory
sizeLimit: 4Gi
很多推理框架在运行时会使用共享内存。这里将 /dev/shm 挂载为内存型 emptyDir,避免默认共享内存太小导致运行异常。
4. 应用 InferenceService
执行:
kubectl apply -f qwen-vllm-isvc.yaml
查看 KServe 是否创建了 Deployment:
kubectl -n kserve-demo get deploy
实际输出如下:
NAME READY UP-TO-DATE AVAILABLE AGE
qwen-vllm-predictor 1/1 1 1 73s
这里可以看到,KServe 在 Standard 模式下为 qwen-vllm 这个 InferenceService 创建了一个名为 qwen-vllm-predictor 的 Deployment。
继续查看 Service:
kubectl -n kserve-demo get svc
输出如下:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
qwen-vllm-predictor ClusterIP 10.100.211.245 <none> 80/TCP 76s
这里可以看到 KServe 同时创建了一个名为 qwen-vllm-predictor 的 ClusterIP Service。
这个 Service 后面会作为 HTTPRoute 的后端,也就是:
backendRefs:
- name: qwen-vllm-predictor
port: 80
五、先通过 KServe 生成的 Service 验证 vLLM 接口
在接入 Gateway API 之前,先通过 KServe 生成的 Service 直接验证模型服务是否正常。
执行端口转发:
kubectl -n kserve-demo port-forward service/qwen-vllm-predictor 8888:80
然后访问 vLLM 的模型列表接口:
curl http://127.0.0.1:8888/v1/models
返回结果如下:
{
"object": "list",
"data": [
{
"id": "qwen2.5-1.5b-instruct",
"object": "model",
"created": 1781752182,
"owned_by": "vllm",
"root": "/data/models/Qwen2.5-1.5B-Instruct",
"parent": null,
"max_model_len": 4096,
"permission": [
{
"id": "modelperm-83089680312778ea",
"object": "model_permission",
"created": 1781752182,
"allow_create_engine": false,
"allow_sampling": true,
"allow_logprobs": true,
"allow_search_indices": false,
"allow_view": true,
"allow_fine_tuning": false,
"organization": "*",
"group": null,
"is_blocking": false
}
]
}
]
}
这个结果说明:
KServe 生成的 Service 可以正常访问
vLLM 服务已经启动
模型 qwen2.5-1.5b-instruct 已经加载成功
/v1/models 接口正常
到这里为止,KServe + vLLM 的基础服务已经验证成功。
后续如果 Gateway API 访问失败,就可以优先排查 Gateway、HTTPRoute、Host Header、backendRefs 等网络转发配置,而不是怀疑模型服务本身。
六、接入 Gateway API
前面已经验证了 qwen-vllm-predictor Service 可以正常访问。接下来通过 Gateway API 创建新的入口,将请求转发到这个 Service。
注意:本文没有使用 KServe 自动生成 Gateway/HTTPRoute 的对外暴露方式,而是手动创建 Gateway 和 HTTPRoute,将流量转发到 KServe Standard 模式生成的 qwen-vllm-predictor Service。因此,本文的 Gateway API 入口属于手动配置入口,不是 KServe Controller 自动维护的入口。如果希望让 KServe 自己管理 Gateway API 对外访问,需要额外配置 inferenceservice-config 中的 ingress.enableGatewayApi 和 kserveIngressGateway。
本文采用的设计如下:
| 对象 | 配置 |
|---|---|
| Gateway 命名空间 | kserve-demo |
| HTTPRoute 命名空间 | kserve-demo |
| Gateway 名称 | llm-gateway |
| HTTPRoute 名称 | llm-route-basic |
| Hostname | llm.example.local |
| 后端 Service | qwen-vllm-predictor |
| 后端端口 | 80 |
| allowedRoutes | Same |
这里将 Gateway 和 HTTPRoute 都放在 kserve-demo 命名空间,并设置:
allowedRoutes:
namespaces:
from: Same
这样做的原因是降低实验复杂度,先不引入跨 namespace 绑定问题。
1. 创建 Gateway
创建文件:
vim gateway.yaml
内容如下:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: llm-gateway
namespace: kserve-demo
spec:
gatewayClassName: eg
listeners:
- name: http
port: 80
protocol: HTTP
hostname: "llm.example.local"
allowedRoutes:
namespaces:
from: Same
应用:
kubectl apply -f gateway.yaml
这个 Gateway 表示:
使用 GatewayClass eg
在 80 端口创建 HTTP Listener
只接收 Hostname 为 llm.example.local 的请求
只允许同命名空间的 Route 绑定到这个 Listener
2. 创建 HTTPRoute
创建文件:
vim httproute.yaml
内容如下:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: llm-route-basic
namespace: kserve-demo
spec:
parentRefs:
- name: llm-gateway
sectionName: http
hostnames:
- "llm.example.local"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: qwen-vllm-predictor
port: 80
应用:
kubectl apply -f httproute.yaml
这个 HTTPRoute 表示:
绑定到同命名空间下的 llm-gateway
绑定到 Gateway 中名为 http 的 Listener
只匹配 Hostname 为 llm.example.local 的请求
匹配所有以 / 开头的路径
将请求转发到 qwen-vllm-predictor Service 的 80 端口
这里最关键的是:
backendRefs:
- name: qwen-vllm-predictor
port: 80
这个后端 Service 正是 KServe Standard 模式为 InferenceService 自动创建出来的 Service。
3. Gateway API 配置关系说明
本文里 Gateway API 的关系如下:
GatewayClass eg
-> Gateway llm-gateway
-> Listener http:80, hostname=llm.example.local
-> HTTPRoute llm-route-basic
-> backendRefs: qwen-vllm-predictor:80
因为 Gateway 和 HTTPRoute 中都配置了:
hostname: "llm.example.local"
以及:
hostnames:
- "llm.example.local"
所以请求时需要带上:
-H "Host: llm.example.local"
否则请求匹配不到对应的 HTTPRoute。
七、验证 Envoy Gateway 创建的数据面资源
创建 Gateway 和 HTTPRoute 后,Envoy Gateway 会根据 Gateway 资源创建对应的数据面 Pod 和 Service。
查看 Envoy Gateway 相关 Pod:
kubectl -n envoy-gateway-system get pod
实际输出如下:
NAME READY STATUS RESTARTS AGE
envoy-gateway-6f954cd9dd-49zm7 1/1 Running 2 (171m ago) 43h
envoy-gateway-demo-app-gateway-c2617110-5df694555c-smmfb 2/2 Running 4 (171m ago) 42h
envoy-kserve-demo-llm-gateway-4f087a47-5dcc98f85f-rm77f 2/2 Running 0 41m
这里重点关注:
envoy-kserve-demo-llm-gateway-4f087a47-5dcc98f85f-rm77f
这个 Pod 是 Envoy Gateway 为 kserve-demo/llm-gateway 这个 Gateway 创建的数据面 Pod。
继续查看 Service:
kubectl -n envoy-gateway-system get svc
实际输出如下:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
envoy-gateway ClusterIP 10.97.106.78 <none> 18000/TCP,18001/TCP,18002/TCP,19001/TCP,9443/TCP 43h
envoy-gateway-demo-app-gateway-c2617110 LoadBalancer 10.99.157.94 <pending> 80:31738/TCP 43h
envoy-kserve-demo-llm-gateway-4f087a47 LoadBalancer 10.103.44.121 <pending> 80:32221/TCP 41m
这里重点关注:
envoy-kserve-demo-llm-gateway-4f087a47
因为本文是单节点本地实验环境,没有云厂商 LoadBalancer,所以 EXTERNAL-IP 是 <pending>。
这种情况下有两种测试方式:
第一种是使用 NodePort:
80:32221/TCP
也就是可以通过节点 IP + 32221 端口访问。
第二种是使用 kubectl port-forward,本文采用这种方式进行验证。
八、通过 Gateway API 访问模型服务
1. 端口转发 Envoy Gateway Service
执行:
kubectl -n envoy-gateway-system port-forward service/envoy-kserve-demo-llm-gateway-4f087a47 8888:80
这条命令表示:
将本机 127.0.0.1:8888 转发到 envoy-kserve-demo-llm-gateway-4f087a47 Service 的 80 端口
也就是后续本地请求:
http://127.0.0.1:8888
会进入 Envoy Gateway 数据面。
2. 通过 Gateway API 访问 /v1/models
执行:
curl -H "Host: llm.example.local" http://127.0.0.1:8888/v1/models
返回如下:
{
"object": "list",
"data": [
{
"id": "qwen2.5-1.5b-instruct",
"object": "model",
"created": 1781755304,
"owned_by": "vllm",
"root": "/data/models/Qwen2.5-1.5B-Instruct",
"parent": null,
"max_model_len": 4096,
"permission": [
{
"id": "modelperm-b5b80cc40050a5cf",
"object": "model_permission",
"created": 1781755304,
"allow_create_engine": false,
"allow_sampling": true,
"allow_logprobs": true,
"allow_search_indices": false,
"allow_view": true,
"allow_fine_tuning": false,
"organization": "*",
"group": null,
"is_blocking": false
}
]
}
]
}
这个结果说明 Gateway API 转发链路已经跑通:
curl
-> 本地 127.0.0.1:8888
-> port-forward 到 Envoy Gateway Service
-> Envoy 数据面根据 Host Header 匹配 llm.example.local
-> HTTPRoute 匹配 PathPrefix /
-> 转发到 qwen-vllm-predictor Service
-> 转发到 qwen-vllm-predictor Pod
-> vLLM 返回 /v1/models 结果
3. 通过 Gateway API 调用 /v1/chat/completions
继续调用 vLLM 的 OpenAI-compatible Chat Completions API:
curl -H "Host: llm.example.local" \
http://127.0.0.1:8888/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen2.5-1.5b-instruct",
"messages": [
{
"role": "user",
"content": "1+1等于几?"
}
],
"max_tokens": 64,
"temperature": 0
}'
返回结果如下:
{
"id": "chatcmpl-5422e602-6d42-4d2a-b2da-a1939ad3deb3",
"object": "chat.completion",
"created": 1781755522,
"model": "qwen2.5-1.5b-instruct",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "1+1等于2。这是基本的算术运算,表示两个一相加的结果是二。",
"refusal": null,
"annotations": null,
"audio": null,
"function_call": null,
"tool_calls": [],
"reasoning": null
},
"logprobs": null,
"finish_reason": "stop",
"stop_reason": null,
"token_ids": null,
"routed_experts": null
}
],
"service_tier": null,
"system_fingerprint": "vllm-0.22.1-30f25298",
"usage": {
"prompt_tokens": 35,
"total_tokens": 58,
"completion_tokens": 23,
"prompt_tokens_details": null
},
"prompt_logprobs": null,
"prompt_token_ids": null,
"prompt_text": null,
"kv_transfer_params": null
}
可以看到模型正常返回:
1+1等于2。这是基本的算术运算,表示两个一相加的结果是二。
这说明:
KServe InferenceService 部署成功
KServe 生成的 Deployment 和 Service 正常
vLLM 模型服务正常
Gateway API 的 Gateway 和 HTTPRoute 配置正常
Envoy Gateway 数据面转发正常
OpenAI-compatible API 调用正常
九、本文实验总结
本文完成了一个 KServe + vLLM + Gateway API 的最小闭环实验。
整体流程如下:
1. 安装 cert-manager
2. 安装 KServe 0.18
3. 将 KServe 切换到 Standard 模式
4. 使用 InferenceService 部署本地 Qwen2.5-1.5B-Instruct 模型
5. KServe 自动生成 qwen-vllm-predictor Deployment
6. KServe 自动生成 qwen-vllm-predictor Service
7. 先通过 Service 直连验证 /v1/models
8. 创建新的 Gateway
9. 创建新的 HTTPRoute
10. 通过 Envoy Gateway 访问 /v1/models
11. 通过 Envoy Gateway 访问 /v1/chat/completions
从组件职责上看:
| 组件 | 职责 |
|---|---|
| KServe | 使用 InferenceService 声明式管理模型服务 |
| InferenceService | 描述模型服务的期望状态 |
| KServe Controller | 根据 InferenceService 创建底层 Kubernetes 资源 |
| qwen-vllm-predictor Deployment | 运行实际的 vLLM 模型服务 Pod |
| qwen-vllm-predictor Service | 为模型 Pod 提供稳定访问入口 |
| vLLM | 加载 Qwen2.5-1.5B-Instruct,并提供 OpenAI-compatible API |
| Gateway API | 使用 Gateway 和 HTTPRoute 描述流量入口和路由规则 |
| Envoy Gateway | 实现 Gateway API,并创建 Envoy 数据面转发请求 |
| Volcano | 负责本文模型 Pod 的调度 |
| NVIDIA RuntimeClass | 让模型 Pod 使用 NVIDIA GPU 容器运行时 |
这篇实验里最关键的点是:
KServe 负责模型服务生命周期管理
Gateway API 负责入口和路由规则
Envoy Gateway 负责真实的数据面转发
vLLM 负责模型推理接口
本文不是使用 KServe 自动生成 Gateway/HTTPRoute,而是让 KServe 负责创建模型服务对应的 Deployment 和 Service,然后手动创建 Gateway 和 HTTPRoute,将请求转发到 KServe 生成的 Service。
到这里,KServe 部署 Qwen2.5 小模型,并通过 Gateway API 暴露 vLLM OpenAI API 的最小实验就完成了。
本人水平有限,欢迎各位大佬批评指正。
更多推荐


所有评论(0)