基于K8s的云原生AI基础设施:架构、部署与实践【011】-AI算力的流量入口(下篇)

1. 指标可视化:从“能访问”走向“可运营”

推理入口真正进入平台化阶段后,不能只停留在“服务能访问”。更重要的是要能回答以下问题:

  • 当前有多少请求进入模型服务?
  • 每个模型消耗了多少输入 Token 和输出 Token?
  • 每个推理实例的队列长度是多少?
  • KV Cache 使用率是否过高?
  • EPP 是否正常执行调度?
  • 不同模型、不同租户、不同调用方的资源消耗如何统计?

因此,指标采集与可视化是 AI 推理平台必不可少的一部分。在实践中,可以分别对 EPP、vLLM 和 SGLang 配置 Prometheus 采集。

EPP 侧主要关注推理池状态、调度行为和请求级统计指标,例如:

inference_pool_ready_pods                        表示推理池中 Ready 的模型实例数量;
inference_pool_average_queue_size                表示推理池平均等待队列长度;
inference_pool_per_pod_queue_size                可以观察每个模型 Pod 的队列情况;
inference_pool_average_kv_cache_utilization      用于观察推理池整体 KV Cache 使用率;
inference_extension_scheduler_attempts_total     可以用于确认EPP是否真正执行了调度决策。
inference_objective_request_total
inference_objective_request_error_total
inference_objective_request_duration_seconds
inference_objective_input_tokens
inference_objective_output_tokens

 

需要特别注意的是,inference_objective_input_tokensinference_objective_output_tokens 在 EPP 中属于分布类指标,Prometheus 中会派生出 _bucket_sum_count 等序列。其中,_count 表示样本数量,并不等于 Token 总量。如果要统计单位时间内的输出 Token,更适合使用:

sum(increase(inference_objective_output_tokens_sum[1h]))

 

如果要统计单位时间内的输入 Token,可以使用:

sum(increase(inference_objective_input_tokens_sum[1h]))

 

但这里也要注意,EPP 侧的请求级 Token 统计依赖响应中的 usage 信息。在流式响应场景下,需要确认模型服务是否返回了 usage 信息。例如 vLLM 的 streaming 请求通常需要携带:

{
  "stream_options": {
    "include_usage":true
  }
}

 

否则响应侧 Token 统计可能不完整。

vLLM 侧主要关注请求状态、KV Cache、Token 计数和请求延迟,例如:

vllm:num_requests_running
vllm:num_requests_waiting
vllm:kv_cache_usage_perc
vllm:prompt_tokens_total
vllm:generation_tokens_total
vllm:time_to_first_token_seconds
vllm:inter_token_latency_seconds
vllm:e2e_request_latency_seconds

 

其中,vllm:prompt_tokens_totalvllm:generation_tokens_total 是累计 Counter,可以用于输入 Token 和输出 Token 的统计;vllm:kv_cache_usage_perc 用于观察 KV Cache 使用比例;vllm:num_requests_runningvllm:num_requests_waiting 用于观察运行中请求和等待队列。不同 vLLM 版本中部分指标命名可能存在差异,实际应以 /metrics 输出为准。

SGLang 侧主要关注运行中请求、等待队列、KV Cache / token capacity 使用率,以及输入输出 Token 计数,例如:

sglang:num_running_reqs
sglang:num_queue_reqs
sglang:token_usage
sglang:prompt_tokens_total
sglang:generation_tokens_total
sglang:time_to_first_token_seconds
sglang:time_per_output_token_seconds
sglang:e2e_request_latency_seconds

 

其中,sglang:prompt_tokens_totalsglang:generation_tokens_total 可以用于输入/输出 Token 统计;sglang:num_queue_reqs 表示等待队列中的请求数;sglang:num_running_reqs 表示当前运行中的请求数;sglang:token_usage 更适合理解为 KV Cache / token capacity 使用率,而不是累计 Token 用量。

这些指标的意义不仅在于展示系统状态,更重要的是为平台运营提供数据基础。例如,vLLM 的 Token 统计可以写成:

sum(increase(vllm:prompt_tokens_total[1h]))
+
sum(increase(vllm:generation_tokens_total[1h]))

 

SGLang 也可以通过类似方式统计单位时间内的输入和输出 Token:

sum(increase(sglang:prompt_tokens_total[1h]))
+
sum(increase(sglang:generation_tokens_total[1h]))

 

如果要观察推理池排队情况,可以使用:

sum by (name) (inference_pool_average_queue_size)

 

如果要观察每个模型 Pod 的队列分布,可以使用:

sum by (name, model_server_pod) (inference_pool_per_pod_queue_size)

 

如果要确认 EPP 是否持续参与调度,可以使用:

sum by (status) (rate(inference_extension_scheduler_attempts_total[5m]))

 

当这些指标进入 Prometheus 和 Grafana 后,推理平台就具备了进一步做容量规划、压测分析、SLA 观测、异常定位和自动扩缩容的基础。这意味着平台建设已经从“能部署模型”,开始走向“能运营模型”。下面来看具体的工程实践。


1.1 配置指标采集

本文使用 Prometheus Operator 的 ServiceMonitor 采集 EPP、vLLM 和 SGLang 的 /metrics 指标。

# 需要监控epp的metric,将epp服务打上标签,
kubectl -n roadshow label svc qwen3-8b-demo-epp scrape=epp --overwrite

# 配置通用的服务监控来监控所有的epp服务
kubectl apply -f - <<'EOF'
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: epp-metrics-all
  namespace: monitoring
  labels:
    release: prometheus-stack
spec:
  namespaceSelector:
    any: true
  selector:
    matchLabels:
      scrape: epp
  endpoints:
  - port: http-metrics
    path: /metrics
    interval: 10s
EOF


# 需要监控vLLM的metric,将vllm服务打上标签,
kubectl -n roadshow label svc qwen3-8b-demo-service scrape=vllm --overwrite

# 配置通用的服务监控来监控所有的vllm业务
kubectl apply -f - <<'EOF'
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: vllm-services-all
  namespace: monitoring
  labels:
    release: prometheus-stack
spec:
  namespaceSelector:
    any: true
  selector:
    matchLabels:
      scrape: vllm
  endpoints:
  - port: http
    path: /metrics
    interval: 10s
    scheme: http
EOF


# # 需要监控SGLang的metric,将sglang服务打上标签,
kubectl -n demo label svc sglang-qwen32b scrape=sglang --overwrite

# 配置通用的服务监控来监控所有的sglang业务
kubectl apply -f - <<'EOF'
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: sglang-services-all
  namespace: monitoring
  labels:
    release: prometheus-stack
spec:
  namespaceSelector:
    any: true
  selector:
    matchLabels:
      scrape: sglang
  endpoints:
  - port: http
    path: /metrics
    interval: 10s
    scheme: http
EOF

 


1.2 在 Prometheus 上验证指标

ServiceMonitor 创建完成后,可以先获取 Prometheus 的 ClusterIP:

PROM_IP=$(kubectl -n monitoring get svc kps-prometheus -o jsonpath='{.spec.clusterIP}')

 

首先验证 EPP 调度指标是否进入 Prometheus:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum by (status) (rate(inference_extension_scheduler_attempts_total[5m]))'

 

如果能够返回按 status 聚合的结果,说明 EPP 调度相关指标已经被采集。

继续验证推理池平均队列:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum by (name) (inference_pool_average_queue_size)'

 

验证每个模型 Pod 的队列分布:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum by (name, model_server_pod) (inference_pool_per_pod_queue_size)'

 

验证推理池 Ready Pod 数量:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum by (name) (inference_pool_ready_pods)'

 

如果需要验证 EPP 侧输出 Token 的分布样本,可以查询:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum(inference_objective_output_tokens_count)'

 

这里返回的值只能说明 EPP 观察到了多少个 output token 统计样本,不能理解为输出 Token 总量。

如果要查看 EPP 侧统计到的输出 Token 增量,应使用:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum(increase(inference_objective_output_tokens_sum[1h]))'

 

然后验证 vLLM 输出 Token Counter:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum by (model_name) (vllm:generation_tokens_total)'

 

验证 vLLM 输入 Token Counter:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum by (model_name) (vllm:prompt_tokens_total)'

 

验证 vLLM 当前运行中请求数和等待队列:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum by (model_name) (vllm:num_requests_running)'

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum by (model_name) (vllm:num_requests_waiting)'

 

验证 vLLM KV Cache 使用率:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=avg by (model_name) (vllm:kv_cache_usage_perc)'

 

不同版本可能存在指标命名差异。如果当前 vLLM 版本暴露的是 vllm:gpu_cache_usage_perc,则可以使用下面的查询:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=avg by (model_name) (vllm:gpu_cache_usage_perc)'

 

验证 SGLang 输出 Token Counter:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum by (model_name) (sglang:generation_tokens_total)'

 

验证 SGLang 输入 Token Counter:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum by (model_name) (sglang:prompt_tokens_total)'

 

验证 SGLang 当前运行中请求数和等待队列:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum by (model_name) (sglang:num_running_reqs)'
  
curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=sum by (model_name) (sglang:num_queue_reqs)'

 

验证 SGLang token capacity / KV Cache 使用率:

curl -sS -G "http://$PROM_IP:9090/api/v1/query" 
  --data-urlencode 'query=avg by (model_name) (sglang:token_usage)'

 

通过这些查询,可以分别确认 EPP、vLLM 和 SGLang 三类指标是否已经进入 Prometheus。后续 Grafana Dashboard、容量分析和告警规则,都可以基于这些指标继续展开。


1.3 在 Grafana 中查看 Dashboard

Gateway API Inference Extension 官方仓库中提供了 Grafana Dashboard 示例,可参考如下链接

https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/main/tools/dashboards/inference_gateway.json

可以将该 JSON 导入 Grafana,用于查看 Inference Gateway 相关指标,包括推理池状态、EPP 调度情况、请求量、延迟和 Token 分布等。不过在实际环境中,Dashboard 往往还需要根据 Prometheus 的标签体系进行调整。例如,不同集群中 jobnamespaceservicepodmodel_name 等标签可能不完全一致。如果导入 Dashboard 后没有数据,通常需要先在 Prometheus 中确认原始指标是否存在,再调整 Dashboard 变量或 PromQL 查询条件。

这些指标最终服务的不是“监控展示”本身,而是平台运营能力:容量规划、压测分析、SLA 观测、异常定位、成本核算和自动扩缩容。只有当这些指标能够被持续采集、查询和可视化,AI 推理平台才真正从“能访问”进入“可运营”的阶段。

2. API Key:把北向认证收敛到网关层

当模型服务从内部测试走向对外开放时,访问控制就成为必须考虑的问题。如果每个模型服务都独立实现认证逻辑,平台后续会面临很大的治理成本:不同模型的认证方式不一致,不同团队维护不同密钥逻辑,审计和权限控制也很难统一。

更合理的方式是将北向认证收敛到网关层。也就是说,所有外部调用方先经过统一入口,由网关完成 API Key 校验,只有通过认证的请求才会进入后端模型服务。

在实践中,可以通过 Kubernetes Secret 保存客户端 API Key,再通过 AgentgatewayPolicy 将 API Key 认证策略绑定到对应的 HTTPRoute 上。这样,请求必须携带合法的key才能访问后端模型服务。

Authorization: Bearer <api-key>

 

这种做法的价值不只是“挡住非法请求”,更重要的是把访问控制从模型服务中解耦出来。后端 vLLM 或 SGLang 只负责推理,北向认证、后续限流、审计和租户治理都可以逐步沉淀在网关层。对于多模型、多租户、多调用方的 AI 平台来说,这是入口治理的基础能力。

接下来我们尝试使用 agentgateway 的 AgentgatewayPolicy 为模型服务增加 API Key 认证能力。agentgateway 官方文档中也明确提供了 API key auth 能力:通过 Kubernetes Secret 保存 API Key,再由 AgentgatewayPolicy 引用该 Secret,并在请求进入后端之前完成认证校验;如果请求缺少有效 API Key,代理会直接返回 401 Unauthorized

2.1 创建 Gateway 入口

首先创建一个 Gateway 实例,作为模型服务的统一北向入口。这里使用 agentgateway 作为 gatewayClassName,并监听 8090 端口。

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: llm
  namespace: agentgateway-system
spec:
  gatewayClassName: agentgateway
  listeners:
  - name: http
    protocol: HTTP
    port: 8090
    allowedRoutes:
      namespaces:
        from: All

 

这一层解决的是“请求从哪里进入集群”的问题。后续所有路由和认证策略,都会挂接在这个入口之上。

2.2 定义模型后端

接下来创建 AgentgatewayBackend,用于描述真实的模型服务后端。在这个示例中,后端是一个 OpenAI 兼容的模型服务,请求会被转发到:qwen8b.demo.svc.cluster.local:8080/v1/chat/completions

apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayBackend
metadata:
  name: qwen8b-openai
  namespace: demo
spec:
  ai:
    provider:
      host: qwen8b.demo.svc.cluster.local
      port: 8080
      path: /v1/chat/completions
      openai:
        model: Qwen3-8B
  policies:
    ai:
      routes:
        '*': Passthrough
        /v1/chat/completions: Completions
        /v1/embeddings: Passthrough
        /v1/models: Passthrough

 

AgentgatewayBackend 是 agentgateway 在 Kubernetes 模式下提供的核心自定义资源之一。这里的重点是,后端模型服务本身不需要直接对外暴露,外部请求统一先进入 agentgateway,再由网关转发到后端服务。

2.3. 创建 HTTPRoute

有了入口和后端之后,需要通过 HTTPRoute 将外部路径和后端资源关联起来。这里将外部路径:/demo/qwen8b转发到刚才创建的 AgentgatewayBackend

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: qwen8b-openai-route
  namespace: demo
spec:
  parentRefs:
  - name: llm
    namespace: agentgateway-system
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /demo/qwen8b
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          replacePrefixMatch: /
          type: ReplacePrefixMatch
    backendRefs:
    - group: agentgateway.dev
      kind: AgentgatewayBackend
      name: qwen8b-openai
      namespace: demo
      port: 8080

 

这里使用了 URLRewrite,将外部访问路径 /demo/qwen8b 重写为 /。这样外部路径可以保持平台侧统一规划,而后端仍然按照 OpenAI 兼容接口接收请求。

2.4 创建 API Key Secret

接下来创建用于认证的API Key,推荐将 API Key 存储在 Kubernetes Secret 中,再通过 AgentgatewayPolicy 引用这些 Secret。这样做的好处是:认证凭证不需要写在路由或后端配置里,而是由 Secret 统一维护。在这个示例中,创建了一个 Kubernetes Secret,将客户端可用的 key 保存进去。

apiVersion: v1
kind: Secret
metadata:
  name: llm-client-api-keys
  namespace: demo
type: Opaque
stringData:
  clientA: "k-123"

 

需要注意的是,API Key 本身属于长期凭证,实际生产环境中还需要配合密钥轮换、权限分级和审计能力。所以使用 API Key 时,服务安全性取决于 API Key 本身的保存与轮换方式。

2.5. 创建认证策略

最后创建 AgentgatewayPolicy,将 API Key 认证策略挂接到前面的 HTTPRoute 上。

apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayPolicy
metadata:
  name: qwen8b-require-api-key
  namespace: demo
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: qwen8b-openai-route
  traffic:
    apiKeyAuthentication:
      mode: Strict
      secretRef:
        name: llm-client-api-keys

 

这里使用的是 Strict 模式。在该模式下,请求必须携带有效 API Key,否则 agentgateway 会直接拒绝请求并返回 401 Unauthorized。官方文档中也明确说明,Strict 模式要求请求中必须包含有效的 Authorization Header;缺少或无效时,请求会被拒绝。从链路上看,认证逻辑发生在请求进入后端模型服务之前。这意味着后端模型服务本身不需要实现 API Key 校验,北向访问控制可以统一收敛到网关层完成。


2.6 访问验证

策略创建完成后,可以通过几组请求验证认证是否生效。首先获取 Gateway 地址:

GW_IP=$(kubectl -n agentgateway-system get gateway llm 
  -o jsonpath='{.status.addresses[0].value}')

 

2.6.1 使用正确 Bearer Token

curl -i "http://$GW_IP:8090/demo/qwen8b" 
  -H "Content-Type: application/json" 
  -H "Authorization: Bearer k-123" 
  -d '{
    "model":"Qwen3-8B",
    "messages":[
      {
        "role":"user",
        "content":"您好,你是什么模型"
      }
    ]
  }'

 

如果 API Key 正确,请求会被放行,并转发到后端模型服务。

2.6.2 使用错误 Bearer Token

curl -i "http://$GW_IP:8090/demo/qwen8b" 
  -H "Content-Type: application/json" 
  -H "Authorization: Bearer k-wrong" 
  -d '{
    "model":"Qwen3-8B",
    "messages":[
      {
        "role":"user",
        "content":"您好,你是什么模型"
      }
    ]
  }'

 

返回结果如下:

HTTP/1.1 401 Unauthorized

api key authentication failure: invalid credentials

 

这说明请求虽然携带了 Authorization Header,但 key 与 Secret 中保存的值不匹配,因此被拒绝。


2.6.3 使用 x-api-key Header

curl-i"http://$GW_IP:8090/demo/qwen8b" 
-H"Content-Type: application/json" 
-H"x-api-key: k-123" 
-d'{
    "model":"Qwen3-8B",
    "messages":[
      {
        "role":"user",
        "content":"hi"
      }
    ]
  }'

 

返回结果如下:

HTTP/1.1 401 Unauthorized

api key authentication failure: no API Key found

 

这说明在当前配置下,agentgateway 并没有从 x-api-key Header 中读取凭证,而是期望从 Authorization Header 中获取 API Key。


2.6.4 不携带 API Key

curl-i"http://$GW_IP:8090/demo/qwen8b" 
-H"Content-Type: application/json" 
-d'{
    "model":"Qwen3-8B",
    "messages":[
      {
        "role":"user",
        "content":"hi"
      }
    ]
  }'

 

返回结果如下:

HTTP/1.1 401 Unauthorized

api key authentication failure: no API Key found

 

这说明 Strict 模式已经生效:没有 API Key 的请求不会被转发到后端模型服务。


通过这一组配置,可以在 agentgateway 上完成一个最基础的北向 API Key 认证闭环,这也是AI 平台对外提供模型服务时一个必须具体的能力。

最后,总结一下。Kubernetes 上的模型服务入口正在经历一个明显变化:从传统 HTTP 路由,逐步走向具备推理语义感知能力的 AI Gateway。

在最基础的阶段,Gateway 和 HTTPRoute 解决的是统一入口和路径转发问题;引入 InferencePool 后,后端不再只是普通 Service,而是一组可以被推理调度的模型实例;引入 EPP 后,入口层开始能够基于队列、KV Cache、Prefix Cache 等指标做端点选择;再结合 API Key、Body Based Router、模型灰度和指标可视化,推理入口就逐渐具备了平台化治理能力。

这套体系的价值不在于“把模型暴露出去”,而在于把推理服务变成一种可治理、可观测、可调度、可演进的基础设施能力。因此,对于一个面向多模型、多租户、多推理引擎的云原生 AI 算力平台来说,推理入口不应该只是南北向流量入口,而应该成为连接用户请求、模型服务、GPU 资源和平台治理能力的核心控制点。这也是 Gateway API Inference Extension 这类项目真正有价值的地方:它让 Kubernetes 的流量入口,从“能转发请求”,进一步走向“懂推理、能调度、可运营”的 AI 基础设施入口。

Leave a Reply