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.go的Translate;IR 类型在internal/ir/xds.go与internal/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 分成:
- Infra IR:托管数据面基础设施的内部定义,给 Infra Manager。
- xDS IR:托管数据面 xDS 配置的内部定义,给 xDS Translator。
Resource Translator 在文档里对应 gatewayapi
包的 Translator API 类型。tag
上结构体注释重复这一点:Translator translates Gateway API resources to IRs and computes status for Gateway API resources.
解耦要解决的不是审美,而是三条工程约束:
- Gateway API 与 Envoy 资源树的基数不同。
一个 Gateway listener 在兼容条件下会折叠进同一 Envoy
Listener;一条 HTTPRoute 规则变成多条 hostname 上的
route;每个
backendRef对应 cluster。直接「一个 CR 字段 = 一个 proto 字段」写不出 listener 折叠与冲突规则。 - Provider 可替换。 Kubernetes 与
Custom/File 都产出同一份
Resources→ IR;xDS 与 Infra 后端不需要知道对象来自 informer 还是文件。 - 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.go 的 Xds
保存即将变成 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.go 的 Infra
目前核心是
Proxy *ProxyInfra。ProxyInfra 含
metadata(annotations/labels/ownerReference)、Name/Namespace、Config *EnvoyProxy、Listeners []*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
证书未就绪时仍保留舰队以免删除抖动。InitIRs 对
failed 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):GatewayControllerName、GatewayClassName、GlobalRateLimitEnabled、EnvoyPatchPolicyEnabled、BackendEnabled、SDSSecretRefEnabled、ControllerNamespace、GatewayNamespaceMode、MergeGateways、MergeBackends、PerResourceSystemCASecret、LuaEnvoyExtensionPolicyDisabled、InfraRemotelyManaged
等。同一份 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.go,Translate):
GetRelevantGateways— 按 GatewayClassName 过滤;校验 EnvoyProxy;mergeGateways与mergeBackends不能同时开,否则 GatewayAccepted=False。InitIRs— 为 accepted 与 failed Gateway 建 XdsIR/InfraIR 槽。- 预计算 BTP / CTP 索引,供后续 backend 与 cluster 设置 O(1) 查找;涉及 ReferenceGrant。
ProcessListenerSets— 把 ListenerSet 挂到 Gateway(v1.9 的扩展附着面)。ProcessGatewayTLS— 解析 listener 证书;失败会在 listener 条件上体现。ProcessListeners— 写入两份 IR 的 listener/端口;做兼容性折叠。ProcessListenerSetStatus— 必须在 ProcessListeners 之后,因为 ListenerSet status 依赖 listener 处理结果。ProcessEnvoyPatchPolicies— 补丁策略进入 XdsIR(匹配的是后续 xDS 名,破坏面见第 9 篇)。ProcessAddresses。ProcessBackends。ProcessHTTPRoutes/GRPCRoutes/TLSRoutes/TCPRoutes/UDPRoutes— 依赖已处理且校验过的 listener。ProcessClientTrafficPolicies— 同时碰 XdsIR 与 InfraIR(例如 HTTP/3 端口)。ProcessBackendTrafficPolicies— 需要 RouteContext 列表。checkRouteOverlaps— 必须在 BTP 之后:CONNECT upgrade 会替换 path matcher,从而改变 overlap 集合;结果写入 v1.9 的RouteRulesOverlap。ProcessSecurityPolicies、ProcessEnvoyExtensionPolicies、ProcessExtensionServerPolicies。ProcessGlobalResources(例如全局客户端证书、OIDC HMAC)。ProcessBackendTLSPolicyStatus。sortXdsIRMap(按 Gateway API 排序约定)、把 EnvoyProxyFilterOrder抄进对应 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。
buildIR 在 InitIRs 里就把
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 切片。newTranslateResult 把
accepted 与 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 轴特有的分裂:
- status 已写、XdsIR 对应 listener 为空:listener 未通过兼容性/TLS 校验,条件在 Gateway/listener 上为 False,Route 在 v1.9.0 仍可能 Accepted(缺证书不再连坐)。
- status 看起来齐、XdsIR 无该
route:parentRef 未匹配到任何「已校验 listener +
hostname 交集」;或 backend 引用失败只影响
ResolvedRefs,部分规则被丢掉。 - IR 校验失败、不发给下游:runner 对
InfraIR.Validate/ XdsIR validate 失败的键 skip store 并打错误日志。此时 status 可能已经按翻译结果算完,但 xDS/Infra 仍停在上一份——这是「条件绿、数据面旧」的一种机制来源,第 5 篇与第 13 篇继续。
ir.Xds.Validate 与
ir.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 字段当成用户契约。
五、小结
- IR 存在是为了解耦 Gateway API 与 Envoy 资源树,并让 status 与配置同源。
- XdsIR 给 xDS Translator,InfraIR 给 Infra Manager;键默认按 Gateway,mergeGateways 时按 GatewayClass。
- failed Gateway 仍占 IR 槽,避免舰队被误删。
- 翻译顺序是依赖图:ListenerSet status 在 ProcessListeners 之后;overlap 在 BTP 之后。
- status 在
Translate内计算,不是 xDS ACK 之后的倒推。条件绿仍可能 IR 空或 IR 未下发。
下一篇从 XdsIR 走进 LDS/RDS/CDS/EDS/SDS,并从 InfraIR 走进可调度的 Envoy 舰队——那一层才出现 NACK 与 warming。
六、参考资料
规范 / 官方文档(A)
- Envoy Gateway System Design — IR
两份、Translator 对应
gatewayapi包、不跨 Gateway 合并 listener。 - Gateway API Translator Design — 输入输出、listener 兼容性示例、status 职责、翻译大纲、Context 结构体。其中「v0.2 未完整实现 ReferenceGrant」及偏短的输入列表以 tag 源码修正。
- Gateway API v1.6 规范 — listener 兼容性与冲突;Route parent status。
- Envoy Gateway v1.9.0 Release Notes —
RouteRulesOverlap;Translate 与 DeepEqual 竞态;Accepted 与 listener Programmed 分离。
源码(A)
internal/gatewayapi/translator.go@ tag v1.9.0:Translate、GetRelevantGateways、InitIRs、IRKey。internal/gatewayapi/resource/resource.go:XdsIRMap/InfraIRMap/Resources。internal/ir/xds.go:type Xds struct。internal/ir/infra.go:Infra/ProxyInfra/ObjectName。internal/gatewayapi/runner/runner.go:构造Translator、校验后StoreIR、聚合 status。internal/provider/kubernetes/status.go:Gateway Programmed 在写回前用活基础设施对象补算。
核心论文 / 奠基 work
- Aho, Sethi & Ullman, Compilers: Principles, Techniques, and Tools —— 中间表示隔离前端与后端的经典动机;此处仅为类比,EG IR 非 SSA。
- Burns et al., Borg, Omega, and Kubernetes, ACM Queue 2016 —— 期望状态与实际状态的调和;EG 把「实际状态」拆成 IR、xDS、舰队三截。
- 对照:istio-xds 的 model→xDS,说明「多一层 IR」不是网格控制面的唯一解。
实验 / 工具
- 本篇无 IR dump、无
config_dump。实验台账:未跑。有环境时对照 runner 的 IR JSON 日志与 Envoy admin 应视为两个轴,不可互相冒充。
站内对照
← 上一篇:Provider 与 watch · 系列目录 · 下一篇:xDS 与 Infra
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【Envoy Gateway】南北向全景:缺口、五轴坐标系与 16 篇路线
相对 Gateway API 产品教程、Cilium 南北向边界、Envoy 消费侧与 istiod 翻译,钉清 Envoy Gateway v1.9.0 控制面内核缺口;定义附着/status、IR、xDS、Envoy 数据面、入口之后东西向五条轴,给出 16 篇阅读路线。
【Envoy Gateway】排障坐标系:未 Accepted、空 IR、xDS NACK 与旧快照
按五轴映射 Envoy Gateway v1.9.0 南北向故障:status 未绿、Translator 产出空或错误 XdsIR、xDS NACK、Envoy 仍服务旧快照、入口之后的东西向 CNI;口诀是先点名轴,再下钻模块。
Envoy Gateway / Gateway API:从 CR 附着到 xDS
补齐站内 Gateway API 产品教程与 Envoy 数据面消费侧之上的南北向控制面内核:附着与 status、Provider watch、IR 翻译、xDS 与 Infra、路由/TLS/L4/Policy 边界,并以排障与相对 Cilium Gateway / Istio / Ingress 的选型收束。
【Envoy Gateway】角色与附着:GatewayClass / Gateway / Route 各保证什么
钉清 Gateway API v1.6.1 角色分离在 Envoy Gateway v1.9.0 里的失败语义:parentRefs 与跨 namespace、ReferenceGrant 握手、Accepted 相对 Programmed / ResolvedRefs,以及 YAML 已 apply 仍无流量时该先查哪条条件。