土法炼钢 · 系统与基础设施

【Envoy Gateway】Gateway API Translator 与 IR:对象如何变成 XdsIR / InfraIR

文章导航

分类入口
kubernetesnetwork
标签入口
#envoy-gateway#translator#xdsir#infroir#gateway-api#ir#v1.9.0

目录

Provider 已经给出一份 resource.Resources。下一步误区是:有 Gateway 对象就等于 Envoy 会有一条 Listener。 Envoy Gateway 不把 Gateway API 字段直接填进 LDS。Gateway API Translator 先把外部对象译成两份中间表示:XdsIR(代理配置)与 InfraIR(舰队与端口)。翻译按固定顺序走,后面的步骤依赖前面的 listener 上下文;status 在同一次 Translate 里计算。顺序错了,不是「结果差不多」,而是 hostname 交集、Policy 附着和 overlap 检查会看到不完整的世界。

本文钉 IR 轴:IR 为何存在,两份 IR 各给谁用,翻译顺序依赖什么,status 如何在翻译中落地。xDS 资源树与 Infra Manager 拉起 Deployment 是第 5 篇。

本篇在系列中的位置

篇目 核心内容
第 3 篇 · Provider 与 watch 输入集与 status 写回
第 4 篇 · Translator 与 IR XdsIR / InfraIR、顺序、翻译中的 status
第 5 篇 · xDS 与 Infra IR → LDS/RDS/… 与 Envoy 舰队
系列目录 关键问题、阅读路径、全部价值点

版本锚定:Envoy Gateway v1.9.0(tag v1.9.0)。Translator 在 internal/gatewayapi/translator.goTranslate;IR 类型在 internal/ir/xds.gointernal/ir/infra.go。官方设计见 System Design 的 IR 节与 Gateway API Translator Design。设计文档的输入清单仍带 v0.2 痕迹(未列 GRPCRoute / 各类 Policy);以 tag 函数体为准无 IR dump / config_dump 实测;不伪造输出。


一、IR 为何存在

官方 System Design 把 Intermediate Representation 定义成:外部资源被译入的内部数据模型,从而使 Envoy Gateway 与动态配置所用的外部资源解耦。IR 分成:

Resource Translator 在文档里对应 gatewayapi 包的 Translator API 类型。tag 上结构体注释重复这一点:Translator translates Gateway API resources to IRs and computes status for Gateway API resources.

解耦要解决的不是审美,而是三条工程约束:

  1. Gateway API 与 Envoy 资源树的基数不同。 一个 Gateway listener 在兼容条件下会折叠进同一 Envoy Listener;一条 HTTPRoute 规则变成多条 hostname 上的 route;每个 backendRef 对应 cluster。直接「一个 CR 字段 = 一个 proto 字段」写不出 listener 折叠与冲突规则。
  2. Provider 可替换。 Kubernetes 与 Custom/File 都产出同一份 Resources → IR;xDS 与 Infra 后端不需要知道对象来自 informer 还是文件。
  3. status 与配置必须同源。 Translator Design 要求每一步都可以同时产生 IR 与 status。若先生成 xDS 再倒推条件,Accepted 与实际下发会分叉。

对照 Istio:istiod 把 CRD / HTTPRoute 译进自己的 model,再出 xDS(站内 istio-xds)。那是另一条编译器,不是「少了一层 IR」。Envoy Gateway 把中间层显式命名并分成给代理的与给舰队的两份,排障时可以单独问「XdsIR 有没有这条 HTTP listener」而不必先读 LDS。代价是多一跳;第 13 篇把「IR 空」列为独立轴,正是为这笔代价买单。

编译器传统里,中间表示用来隔离前端语言与后端指令(Aho, Sethi & Ullman, Compilers: Principles, Techniques, and Tools)。这里的「前端语言」是 Gateway API + EG CR,「后端指令」是 Envoy xDS 与 Kubernetes Deployment。类比只用于说明为何不能跳过这一层;EG 的 IR 不是 SSA,也不追求形式化验证。开放问题是:IR 与 xDS 的对账会不会成为一等公民工具——目前 tag 上 runner 可以在 debug 日志里打印 IR JSON,但本系列没有集群实测,不把任何 dump 写成样例。


二、XdsIR vs InfraIR

tag 上类型别名(internal/gatewayapi/resource/resource.go):

type (
    XdsIRMap   map[string]*ir.Xds
    InfraIRMap map[string]*ir.Infra
)

map 的键默认是 {GatewayNamespace}/{GatewayName}mergeGateways 打开时合并到 {GatewayClassName}Translator.IRKey)。排障时用错键,会以为 IR 空,其实写在 class 级键下。

2.1 XdsIR:ir.Xds

internal/ir/xds.goXds 保存即将变成 xDS 的代理意图,而不是 envoy proto。v1.9.0 顶层字段包括:就绪检查 listener、access log / tracing / metrics、HTTP / TCP / UDP listeners、EnvoyPatchPolicies、HTTP filter 顺序、全局资源(如 OIDC HMAC)、extension server policies、以及 BackendClusters(合并后的 cluster 真源)。Validate 遍历 HTTP/TCP/UDP listener。

XdsIR 回答的问题是:这个 Gateway(或合并后的 class)应该暴露哪些协议监听、哪些路由、哪些集群与策略。它不回答 Deployment 副本数、Service 类型、Pod 标签。

2.2 InfraIR:ir.Infra

internal/ir/infra.goInfra 目前核心是 Proxy *ProxyInfraProxyInfra 含 metadata(annotations/labels/ownerReference)、Name/Namespace、Config *EnvoyProxyListeners []*ProxyListener(端口、协议、可选 HTTP/3)、对外地址、以及翻译期预解析的 OTel metric sink。ObjectName() 生成 envoy-{name} 形式的基础设施对象名。Validate 要求 name 非空、listener 端口合法。

InfraIR 回答的问题是:该跑几类端口的 Envoy 进程、叫什么、归谁所有。 Infra Manager(第 5、10 篇)据此 CRUD Deployment/Service。GatewayNamespaceMode 下 buildIR 把 proxy Name/Namespace 改成 Gateway 自己的名字与 namespace,OwnerReference 从 GatewayClass 改为 Gateway。

2.3 为何必须两份

数据面配置与数据面进程的生命周期不同步:可以先有 Deployment 再有 RDS,也可以在 listener 证书未就绪时仍保留舰队以免删除抖动。InitIRsfailed gateways 也建 IR 槽位,注释写明是为了避免 Infra IR 上的删除事件拆掉这些 Gateway 已托管的基础设施。XdsIR 可以几乎为空(没有合法 listener),InfraIR 仍占键——这是「Accepted=False 但 Pod 还在」的 IR 层解释。

flowchart LR
  snap["Resources snapshot"] --> tr["Translator.Translate"]
  tr --> xdsir["XdsIR ir.Xds"]
  tr --> infroir["InfraIR ir.Infra"]
  tr --> st["resource Status"]
  xdsir --> xdstr["xDS Translator"]
  infroir --> infra["Infra Manager"]

图:同一份快照进入 Translate,产出两份 IR 与写回用的 status。xDS 与舰队从这里分叉。


三、翻译顺序与依赖

Translator Design 的 Outline 把过程粗分为:先处理 Gateway Listeners(唯一性、supported kinds、allowed namespaces、TLS Secret),再处理 HTTPRoutes(matches / filters / backends),对每个 parentRef 找匹配 listener 与 hostname 交集后写入 host。tag v1.9.0 的 Translate 把这条大纲扩成完整流水线。顺序本身就是依赖图,不能当「实现细节」跳过。

gatewayapi runner 构造 Translator 时注入的不是 Gateway API 字段,而是静态门与运行时开关internal/gatewayapi/runner/runner.go):GatewayControllerNameGatewayClassNameGlobalRateLimitEnabledEnvoyPatchPolicyEnabledBackendEnabledSDSSecretRefEnabledControllerNamespaceGatewayNamespaceModeMergeGatewaysMergeBackendsPerResourceSystemCASecretLuaEnvoyExtensionPolicyDisabledInfraRemotelyManaged 等。同一份 Resources 在 Lua 关闭与打开时译出的 XdsIR 不同。排障「策略在输入集里却没进 IR」要先对这张开关表,再怀疑 Translate 顺序。

Translate 开头先 resources.StatusDeepCopy():输入树与 watchable coalesce 共享,翻译会原地改 Status;v1.9.0 用只深拷贝 Status 的副本隔离竞态。随后建立 TranslatorContext 索引(Namespace、Service、ServiceImport、Backend、Secret、ConfigMap、ClusterTrustBundle、EndpointSlice)。

然后是固定顺序(internal/gatewayapi/translator.goTranslate):

  1. GetRelevantGateways — 按 GatewayClassName 过滤;校验 EnvoyProxy;mergeGatewaysmergeBackends 不能同时开,否则 Gateway Accepted=False
  2. InitIRs — 为 accepted 与 failed Gateway 建 XdsIR/InfraIR 槽。
  3. 预计算 BTP / CTP 索引,供后续 backend 与 cluster 设置 O(1) 查找;涉及 ReferenceGrant。
  4. ProcessListenerSets — 把 ListenerSet 挂到 Gateway(v1.9 的扩展附着面)。
  5. ProcessGatewayTLS — 解析 listener 证书;失败会在 listener 条件上体现。
  6. ProcessListeners — 写入两份 IR 的 listener/端口;做兼容性折叠。
  7. ProcessListenerSetStatus必须在 ProcessListeners 之后,因为 ListenerSet status 依赖 listener 处理结果。
  8. ProcessEnvoyPatchPolicies — 补丁策略进入 XdsIR(匹配的是后续 xDS 名,破坏面见第 9 篇)。
  9. ProcessAddresses
  10. ProcessBackends
  11. ProcessHTTPRoutes / GRPCRoutes / TLSRoutes / TCPRoutes / UDPRoutes — 依赖已处理且校验过的 listener。
  12. ProcessClientTrafficPolicies — 同时碰 XdsIR 与 InfraIR(例如 HTTP/3 端口)。
  13. ProcessBackendTrafficPolicies — 需要 RouteContext 列表。
  14. checkRouteOverlaps必须在 BTP 之后:CONNECT upgrade 会替换 path matcher,从而改变 overlap 集合;结果写入 v1.9 的 RouteRulesOverlap
  15. ProcessSecurityPoliciesProcessEnvoyExtensionPoliciesProcessExtensionServerPolicies
  16. ProcessGlobalResources(例如全局客户端证书、OIDC HMAC)。
  17. ProcessBackendTLSPolicyStatus
  18. sortXdsIRMap(按 Gateway API 排序约定)、把 EnvoyProxy FilterOrder 抄进对应 XdsIR。

Translator Design 的 Listener Compatibility 示例(同端口同协议不同 hostname 可折叠;双方都省略 hostname 则不兼容)在这一步落地。官方还钉死:不跨 Gateway 折叠 listener。

HTTPRoute 步内部仍按设计大纲的三层展开(tag 上 ProcessHTTPRoutes 实现):对每条 rule 计算 matches(Core:path exact/prefix、header exact;Extended:query、method)、filters(Core:request header modifier、redirect;Extended:mirror)、backends(Core:Service)。然后对每个 parentRef 取匹配 listener——检查 Gateway、section name、listener 是否已通过校验、allowedRoutes、hostname 交集——再把算好的 rule 挂到每个相交 hostname 上。hostname 不相交时,这条 parent 不会往 XdsIR 写 route,但 parent status 仍可能记录 Accepted=False 或未附着。这就是「YAML 有 hostnames、IR 无对应 HTTP route」最常见的翻译期内原因,不必先怀疑 xDS。

GRPC/TLS/TCP/UDP 走同一 RouteContext 接口,但协议必须与 listener 兼容。TCP/UDP 只调和 v1:输入集若因 CRD 版本跳过,本函数根本收不到对象,IR 里不会出现「被拒绝的 L4 route」,只会缺席。

翻译顺序的排障含义:

若提前结束于 IR 上能看到 看不到
Gateway 进 failed 集合 InfraIR 槽(防删) 有效 HTTP listener
TLS Secret 未解析 端口可能仍在 InfraIR 该 listener 的 HTTPS 配置
Route 未匹配 listener Gateway listener 对应 hostname 的 route
未跑到 overlap 检查 路由可能已写入 RouteRulesOverlap 警告

Lua 扩展是否翻译由 Translator 字段 LuaEnvoyExtensionPolicyDisabled 决定,runner 从静态 extensionApis 注入。对象可以在输入集里,这一步仍跳过——静态门与 IR 轴的交界。

Translator Design 用 Context 结构体保存翻译期可变视图,tag 上同名概念仍在用:GatewayContext 包一份 Gateway 与其 listener 列表;ListenerContext 记住 listenerStatusIdx、namespace selector、已解析的 TLS Secret;RouteContext 接口把 HTTP/gRPC/TLS/TCP/UDP 收成「能查 parentRef / hostname」的同一形状。status 能按 listener 下标写回,是因为下标活在 Context 里,而不是事后用名字反查。GetRelevantGateways 在接受一个 Gateway 之前调用 ResetListeners(),避免沿用上一轮残留的 listener status。

buildIRInitIRs 里就把 InfraIR 的 labels/annotations 从 Gateway spec.infrastructure 拷过去,并按 mergeGateways / GatewayNamespaceMode 选择 owner 标签与 namespace。XdsIR 此时还是空的 ir.Xds{};真正的 HTTP/TCP/UDP 切片要等到 ProcessListeners 与各类 Process*Routes。看到「InfraIR 有端口、XdsIR.HTTP 为空」不要当成 map 丢键,要问 listener 处理有没有把协议写进 xDS 侧。


四、翻译过程中的 status 计算

Translator Design 规定 status 计算覆盖:托管的 GatewayClass 条件;每个 Gateway(基于 listener status;Kubernetes provider 还纳入 Envoy Deployment/Service);gateway.status.listeners;每条 Route 的 status.parents

tag 上 GetRelevantGateways 在翻译 listener 之前就把部分 Gateway 打成未 Accepted(无效 EnvoyProxy、不支持的 address type、merge 冲突),并分开 accepted/failed 切片。newTranslateResultaccepted 与 failed 都放进结果,因为 failed Gateway 同样要写回 status。runner 再按资源聚合多父 status,截断 Route parents 至 32,交给第 3 篇的 Status Manager。

Gateway Programmed 的最终值仍要回到 Kubernetes provider:updateStatusForGateway 用活的 Service/Deployment 覆盖地址与副本条件。Translator 负责 listener/Route 条件与「能否进入 accepted 集合」;Programmed 的「soon」在 EG 里被具体化为副本或 remote infra,这一步看不到 xDS ACK。IR 轴排障因此必须允许「XdsIR 已有 listener、Gateway Programmed 仍 False」:那是舰队未就绪,不是 Translate 丢掉了 listener。

因此出现一种 IR 轴特有的分裂:

ir.Xds.Validateir.Infra.Validate 检查的是 IR 不变量(listener 名非空、端口范围、证书来源互斥等),不是 Gateway API 的 Accepted。翻译可以产出「对规范而言可接受、对 IR 而言非法」的中间态,例如证书与私钥不匹配——v1.9.0 改为在翻译期拒绝坏链,并隔离到引用该 Secret 的 listener,以免 mergeGateways 时一张坏证拖垮共享代理上的全部 TLS。IR 轴的失败因此既包括「没写进去」,也包括「写了但 Validate 挡住不下发」。

Context 结构体(Translator Design)给翻译期提供可变视图:GatewayContext 包 Gateway 与 listener 列表;ListenerContext 包 selector 与 TLS Secret;RouteContext 接口统一 HTTP/TLS/TCP/UDP 等,便于 parentRef 查找。它们不是用户 API,但解释了为何 status 能按 listener 下标写回:listenerStatusIdx 存在 Context 里。

学术争论在本层收束为:多一层 IR 换正确性,还是让排障多一跳。 A 侧(EG 设计):Gateway API 与 Envoy 树解耦,Provider 与 xDS 后端可测、可替换。B 侧:值班必须会问 IR,而大多数「Gateway 教程」只教 YAML;Istio 用户习惯直接对 xDS。本系列不站队性能,只要求平台若选 EG,就把「IR 是否有 Listener/Route」写进第 13 篇的否证顺序。开放问题仍是 PLAN 所记:IR 与 xDS 对账会否成为一等公民——没有官方把 IR dump 当成稳定 API 之前,本篇不把任何内部 JSON 字段当成用户契约。


五、小结

  1. IR 存在是为了解耦 Gateway API 与 Envoy 资源树,并让 status 与配置同源。
  2. XdsIR 给 xDS Translator,InfraIR 给 Infra Manager;键默认按 Gateway,mergeGateways 时按 GatewayClass。
  3. failed Gateway 仍占 IR 槽,避免舰队被误删。
  4. 翻译顺序是依赖图:ListenerSet status 在 ProcessListeners 之后;overlap 在 BTP 之后。
  5. status 在 Translate 内计算,不是 xDS ACK 之后的倒推。条件绿仍可能 IR 空或 IR 未下发。

下一篇从 XdsIR 走进 LDS/RDS/CDS/EDS/SDS,并从 InfraIR 走进可调度的 Envoy 舰队——那一层才出现 NACK 与 warming。


六、参考资料

规范 / 官方文档(A)

源码(A)

核心论文 / 奠基 work

实验 / 工具

站内对照


上一篇:Provider 与 watch · 系列目录 · 下一篇:xDS 与 Infra

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。

2026-08-19 · kubernetes / network

Envoy Gateway / Gateway API:从 CR 附着到 xDS

补齐站内 Gateway API 产品教程与 Envoy 数据面消费侧之上的南北向控制面内核:附着与 status、Provider watch、IR 翻译、xDS 与 Infra、路由/TLS/L4/Policy 边界,并以排障与相对 Cilium Gateway / Istio / Ingress 的选型收束。


By .