一、文章背景

前面已经单独实践过 vLLM 模型服务部署,也单独学习过 Gateway API 的 Gateway、HTTPRoute、Header 匹配和流量转发能力。

本文章的目标是把 KServe 引入进来,通过 KServe 的 InferenceService 来管理模型服务,再通过 Gateway API 创建新的 GatewayHTTPRoute,将外部请求转发到 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 deploykubectl 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 继续尝试访问远端仓库,从而出现 ImagePullBackOffErrImagePull

可以先查看 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 的最小实验就完成了。


本人水平有限,欢迎各位大佬批评指正。

更多推荐