首页
/ Istio 网关 API Inference Extension(GIE)深度解析:为 AI 推理流量实现智能端点选择

Istio 网关 API Inference Extension(GIE)深度解析:为 AI 推理流量实现智能端点选择

2026-09-05 14:33:36作者:韦蓉瑛

本文基于 Istio 仓库中 architecture/networking/gateway-api-inference-extension.md 的架构说明,结合 pilot 网关控制器、路由转换与 xds 过滤器的源码实现,完整讲解 Istio 对 Gateway API Inference Extension(下文简称 GIE)的支持原理:InferencePool CRD 的字段语义、Shadow Service 的自动生成机制、ext_proc 过滤器如何把 EPP(Endpoint Picker Protocol)接入请求路径,以及如何在 istiod 上启用该功能并运行集成测试进行验证。

GIE 解决什么问题

Gateway API Inference Extension 为机器学习推理工作负载提供智能请求路由能力。传统的 HTTPRoute 后端只能按 Kubernetes Service 的静态端点列表做负载均衡,而 GIE 引入了 EPP 服务:在请求实际转发之前,由外部 gRPC 服务根据模型可用性、负载、请求特征(如 KV-cache 占用、排队长度)等因素,动态决定请求应该落到哪个后端 Pod 的哪个端口。

Istio 通过内置 Gateway 控制器实现了对该扩展的支持,核心工作有三块:

  1. 为每个 InferencePool 自动创建一个内部 "shadow" Service,让 Istio 的服务发现机制能够感知池内端点;
  2. 在路由转换阶段把 Envoy 的 ext_proc 过滤器挂到目标路由上,并带上 EPP 服务地址与失败模式;
  3. 运行时由 Envoy 通过 ext_proc 调用 EPP,拿到 <pod-ip>:<port> 形式的选定端点后再转发。

核心组件一:InferencePool CRD

InferencePool 来自 inference.networking.k8s.io/v1 API 组的 Kubernetes CRD,表示一个推理模型服务端点池。关键字段如下:

字段 含义
selector 标签选择器,用于标识池内的模型服务器 Pod
targetPorts 模型服务器暴露的端口列表(自 GIE v1.1.0 起支持多端口)
endpointPickerRef 指向提供端点选择逻辑的外部服务(EPP)
appProtocol(可选) 会透传到 shadow Service 的每个端口上

完整示例(来自架构文档):

apiVersion: inference.networking.k8s.io/v1
kind: InferencePool
metadata:
  name: my-inference-pool
spec:
  targetPorts:
  - number: 8000
  - number: 8001
  selector:
    matchLabels:
      app: inference-workload
  endpointPickerRef:
    name: endpoint-picker-service
    port:
      number: 9002

在 Istio 侧,InferencePool 对象由 krt 响应式集合消费。入口函数 InferencePoolCollectionpilot/pkg/config/kube/gateway/inferencepool_collection.go)为每个 InferencePool 同时产出两样东西:写回 API 对象的 InferencePoolStatus,以及内存中的 InferencePool 结构(包含 shadow Service 描述与 EPP 引用信息)。一个值得注意的实现细节是:只有当该池被至少一个 Gateway(经由 HTTPRoute)引用时,Istio 才会真正构建并管理对应的 InferencePool 对象(见 inferencepool_collection.goif len(gatewayParents) > 0 的分支),孤儿池不会产生 shadow Service。

核心组件二:EPP(Endpoint Picker Protocol)

EPP 是一个外部 gRPC 服务,负责为请求选择具体端点。Istio 使用 Envoy 的 ext_proc(external processing)过滤器实现请求拦截与动态端点选择:EPP 服务收到请求头信息后,通过 x-endpoint 头以 <pod-ip>:<port> 格式返回选中的端点。

在源码中,ext_proc 相关的处理链条为:

  • 基础过滤器定义:InferencePoolExtProcpilot/pkg/xds/filters/filters.go 中声明为一个全局 HttpFilter,其 gRPC 目标集群初始为占位符 dummy,真实目标在路由级覆盖中填入;
  • 路由级覆盖:pilot/pkg/networking/core/route/route.go 中,当某条路由携带 InferencePool 配置时,会构造 ExtProcPerRoute.OverridesGrpcService 指向由 model.BuildSubsetKey 生成的 EPP 服务集群名,FailureModeAllow 取自 InferencePool 声明的失败模式,ProcessingMode.RequestHeaderMode 设为 SEND,即请求头阶段就要向 EPP 咨询。

inferencePoolConfig 结构(pilot/pkg/config/kube/gateway/conversion.go)承载了这条链路所需的四个信息:enableExtProcendpointPickerDst(EPP 服务 FQDN)、endpointPickerPortendpointPickerFailureMode

核心组件三:Shadow Service(影子服务)

InferencePool 本身不是一个 Kubernetes Service,Istio 无法直接从它发现端点。因此控制器为每个 InferencePool 自动创建一个内部 headless Service:

  • 命名规则:<pool-name>-ip-<hash>(例如 test-pool-ip-a1b2c3d4),其中 <hash> 是池名的 8 字符 SHA256 前缀,实现见 inferencepool_collection.go 中的 generateHashInferencePoolServiceName,总长度超过 63 字符时会截断池名以符合 Kubernetes 命名约束;
  • 类型:headless(ClusterIP: None),selector 直接复用 InferencePool 的 spec.selector.matchLabels
  • 关键标签(定义于 inferencepool_collection.go):
    • istio.io/inferencepool-name:池名
    • istio.io/inferencepool-extension-service:EPP 服务名
    • istio.io/inferencepool-extension-port:EPP 服务端口
    • istio.io/inferencepool-extension-failure-mode:失败模式(FailOpen/FailClose)
    • 另外还会打上 constants.InternalServiceSemantics 内部语义标签,标识这是 Istio 管理的内部服务。

从源码结构看(translateShadowServiceToService),多 targetPort 的实现方式是:shadow Service 为每个 targetPort 生成一个"虚拟" Service 端口,起始假端口号 54321 依次递增(54321、54322……),每个端口的 targetPort 指向池内 Pod 的真实端口。这样 Istio 的端点发现能把所有 targetPort 的端点聚合到同一个 Service 之下。

创建与更新采用 Server-Side Apply(applyShadowService),field manager 为 istio.io/inference-pool-controller,并对已有同名 Service 先做"是否由本控制器管理"的校验(canManageShadowServiceForInference,依据 istio.io/inferencepool-name 标签判断),避免覆盖其他控制器管理的同名资源。Service 同时通过 ownerReference 挂到 InferencePool 上,池删除时随属主级联清理。

端到端工作流程

架构文档描述的 5 步流程及其在源码中的对应实现:

  1. HTTPRoute 以 InferencePool 作为后端。例如:

    backendRefs:
      - group: inference.networking.k8s.io
        kind: InferencePool
        name: my-inference-pool
        port: 80
    
  2. Gateway 控制器检测引用并创建 shadow ServicefindGatewayParentsinferencepool_collection.go)遍历引用该池的 HTTPRoute,只认 Istio 内置 GatewayClass 对应的控制器名称(supportedControllers),再结合路由的 status.parents 判定 Gateway 已接受该路由;随后 reconcileShadowService 完成 SSA 落库。

  3. 路由转换时附加 ext_proc 过滤器buildDestinationcase gvk.InferencePool 分支(conversion.go)查找 shadow Service,并从其标签中还原出 EPP 的 FQDN(<svc>.<ns>.svc.<domainSuffix>)、端口与失败模式;若任一标签缺失则返回 InvalidDestination 错误。后端目的端口固定取 shadow Service 的第一个端口(54321),源码注释解释原因:该集群下会包含所有 targetPort 的端点,便于 EPP 跨端口负载均衡。

  4. 运行时 Envoy 通过 ext_proc 咨询 EPP,完成端点选择。

  5. 请求被路由到 EPP 选定的 pod:port 组合

状态回写与失败模式

控制器会把计算结果写回 InferencePool.status.parentscalculateInferencePoolStatus),主要维护两个条件:

  • Accepted:对应 HTTPRoute 是否被父 Gateway 接受(复用路由的 Accepted 条件消息);
  • ResolvedRefs:校验 endpointPickerRef 的 Kind 是否为 Service(未设置 Kind 时默认按 Service 处理)、名字是否非空、以及该 Service 是否存在于与 InferencePool 相同的命名空间,任一不满足则标记 False 并给出 InvalidExtensionRef 原因。

失败模式方面,createInferencePoolObjectinferencepool_collection.go)中,当 endpointPickerRef.failureMode 未显式指定或不是 FailClose 之外的值时,默认取 EndpointPickerFailClose;它最终会被写进 shadow Service 的 istio.io/inferencepool-extension-failure-mode 标签,并在路由级 ext_proc 覆盖中映射为 FailureModeAllow(即 FailOpen 才允许失败时放行)。

启用该功能

Gateway API Inference Extension 默认关闭。启用前置条件与开关(以当前仓库实际代码为准):

  1. PILOT_ENABLE_GATEWAY_API=true(必须,Gateway API 总开关);
  2. 在 istiod 上设置 ENABLE_GATEWAY_API_INFERENCE_EXTENSION=true。该环境变量在 pilot/pkg/features/experimental.go 注册,默认值 false。若未开启,任何引用 InferencePool 的后端都会得到明确报错:InferencePool is not enabled. To enable, set ENABLE_GATEWAY_API_INFERENCE_EXTENSION to true in istiod(见 conversion.go)。

通过 IstioOperator 配置的写法(架构文档示例):

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    pilot:
      env:
        PILOT_ENABLE_GATEWAY_API: "true"
        ENABLE_GATEWAY_API_INFERENCE_EXTENSION: "true"

此外还有一个隐式前提:后端 backendRef.name 必须使用 InferencePool 的名字而非主机名,含 . 的名称会直接报 InvalidDestinationconversion.go)。

运行集成测试与典型场景

GIE 的集成测试位于 tests/integration/pilot/gie/ 目录。运行方式:

go test -tags=integ ./tests/integration/pilot/gie/... -v

测试环境要求:

  • 已安装 Istio 的 Kubernetes 集群;
  • 已安装 Gateway API CRDs;
  • 已安装 Gateway API Inference Extension CRDs。

其中代表性用例 TestInferencePoolMultipleTargetPortsinferencepool_test.go)验证多 targetPort 场景下的完整行为:

  • 多个 targetPorts 的 InferencePool 只创建一个 shadow Service;
  • 该 Service 带有正确的标签且为 headless;
  • EPP 端点选择对所有配置的端口均正常工作;
  • 流量按 EPP 响应被路由到正确的 pod:port 组合。

当前限制与适用前提

结合源码可以确认以下边界:

  • 仅 HTTPRoute 支持 InferencePool 后端route_collections.go 中 GRPCRoute 的转换路径带有明确占位注释,说明 GRPCRoute 目前不返回 InferencePool 的 ext_proc 配置;
  • 池必须经由 Gateway 才被管理:未被 Istio 支持的 GatewayClass 引用的 InferencePool 不会生成 shadow Service,状态条件也会据此计算;
  • EPP 引用限制endpointPickerRef 目前仅支持 Service Kind,且 Service 必须与 InferencePool 同命名空间(calculateResolvedRefsStatus)。

理解以上内容后,读者既可以按架构文档的 YAML 与 IstioOperator 配置在生产环境启用 GIE 路由,也可以顺着 inferencepool_collection.go → conversion.go → route.go 这条源码链路,定位端点发现、ext_proc 挂载与 EPP 咨询各自的实现位置,完成从配置到 xds 下发的全链路排查。

登录后查看全文
热门项目推荐
相关项目推荐