第 4 篇 把 Gateway API 对象编译成 XdsIR 与 InfraIR。值班里下一句常被说成「控制面已经推了」。这句话至少覆盖两件独立的事:xDS 资源表有没有进 go-control-plane 快照,以及 Kubernetes 里有没有可调度的 Envoy Pod。两件都完成,仍可能没有在服务——Envoy 可以 ACK 后继续 warming,也可以 NACK 后守着上一份 known-good。
本文只钉生产侧:XdsIR 如何变成 LDS/RDS/CDS/EDS/SDS,InfraIR 如何变成舰队,以及为什么「已推送」不能当成流量切新。Filter 链、HCM 与 warming 语义外链 envoy/09–11,不在本篇重写。
本文是「Envoy Gateway / Gateway API」系列第 5 篇(共 16 篇)。→ 系列目录
篇目 核心内容 第 4 篇 · Gateway API Translator 与 IR CR → XdsIR / InfraIR 第 5 篇 · xDS Translator 与 Infra IR → xDS;InfraIR → 舰队;推送 ≠ 在服务 第 6 篇 · HTTPRoute / GRPCRoute 匹配、权重、阴影路由
版本锚定:Envoy Gateway v1.9.0(源码 tag v1.9.0,2026-08-14)。机制对齐官方 System Design、tag 下
internal/xds与internal/infrastructure。数据面 Envoy v1.39.0 的资源树与 ACK 语义外链 envoy 系列。无集群则不粘贴伪造egctl/config_dump。
一、xDS Translator
官方 System Design 把 xDS Translator 写成:把
xDS IR 译成 xDS Server 消费的资源。tag
v1.9.0 的实现入口是
internal/xds/translator.Translator.Translate。它吃一份
*ir.Xds,吐
*types.ResourceVersionTable。注释写明翻译是
best-effort:CRD 校验与 Gateway API Translator
应已挡住大部分错误;xDS 这一层只在少数路径失败(扩展
hook、EnvoyPatchPolicy JSON
patch),不想因单点失败让整棵快照消失。
Translate 的顺序(删减注释后)是:
// envoyproxy/gateway tag v1.9.0
// internal/xds/translator/translator.go Translator.Translate
if err := t.processHTTPReadyListenerXdsTranslation(tCtx, xdsIR.ReadyListener); err != nil { ... }
if err := t.processHTTPListenerXdsTranslation(tCtx, xdsIR.HTTP, ...); err != nil { ... }
if err := t.processTCPListenerXdsTranslation(tCtx, xdsIR.TCP, ...); err != nil { ... }
if err := t.processUDPListenerXdsTranslation(tCtx, xdsIR.UDP, ...); err != nil { ... }
if err := t.processMergedBackendClusters(tCtx, xdsIR); err != nil { ... }
if err := processSDSClusters(tCtx, xdsIR); err != nil { ... }
if err := t.patchGlobalResources(tCtx, xdsIR); err != nil { ... }
if err := processJSONPatches(tCtx, xdsIR.EnvoyPatchPolicies); err != nil {
// JSONPatch 视为用户错误:打日志,不让整次翻译失败
}和 envoy/09 的资源树对照:
| xDS 类型 | 本层从哪来 | 不在本篇展开 |
|---|---|---|
| LDS | HTTP / TCP / UDP listener IR;另有 Ready listener | Filter 链内核 |
| RDS | HTTP listener 上的 route IR | HCM 如何挂 RDS |
| CDS / EDS | backend cluster IR、合并集群、访问日志/tracing 附属 cluster | 连接池 / outlier |
| SDS | 证书与校验材料;另为外部 SDS unix URL 建 cluster | 证书轮转时序 |
HTTP 路径上,一条已附着的 HTTPRoute 不会在本层再做一次
Gateway API 匹配计算——那是第 4 篇 IR 的职责。本层把 IR
里已经排好的 host / route / cluster 名字写成 Envoy
proto。同文件后半把 HTTP listener 编成 LDS(内嵌或引用
RDS)、把 destination 编成 CDS/EDS;TCP/UDP 走
processTCPListenerXdsTranslation /
processUDPListenerXdsTranslation,不经
HCM。TCP/UDP listener 若没有任何 route,翻译器会放一个名为
EmptyCluster 的静态空 cluster(常量
emptyClusterName),让 listener
仍能生成,而不是留下悬空引用。L4
路由「静默跳过」时,数据面可能表现为这类空 cluster
或根本没有对应 listener,而不是 HTTP 500。
Translate 末尾还会:为访问日志与 tracing
建附属
cluster、patchGlobalResources(客户端证书、OIDC
HMAC、全局限流集群)、跑 EnvoyPatchPolicy JSON patch、调用
extension post-translation
hook。这些步骤的失败分属「系统错误」和「用户 patch
错误」,门闩在 runner,不在 Translator 自己删除半棵树。
JSON patch(EnvoyPatchPolicy)故意不阻断其余资源:runner 把「系统级翻译错误」与「用户 patch 错误」分开,见第二节。这是排障时必须分开的两格——patch 没匹配到资源,快照仍可能更新;翻译器自己失败,快照应停在上一份。
flowchart LR
xdsir["XdsIR"] --> xdstr["xDS Translator"]
xdstr --> lds["LDS"]
xdstr --> rds["RDS"]
xdstr --> cds["CDS"]
xdstr --> eds["EDS"]
xdstr --> sds["SDS"]
lds --> cache["Snapshot cache"]
rds --> cache
cds --> cache
eds --> cache
sds --> cache
cache --> envoy["Envoy v1.39"]
图:XdsIR 经 Translator 进入按 IR key 切片的快照,再由 xDS 流交给 Envoy。节点为英文;正文解释每条边停在哪一层。
二、go-control-plane Delta xDS
System Design 把 xDS Server 写成基于
go-control-plane、实现 Delta xDS 的 gRPC 服务。tag
里对应包是 internal/xds/runner 与
internal/xds/cache;internal/xds/server
在 v1.9.0 只剩 GatewayNamespaceMode 的 kubejwt
认证,不要按旧目录名去找主循环。
Runner 启动时:
cache.NewSnapshotCache(true, logger):true打开 ADS 模式的 go-control-planeSnapshotCache。serverv3.NewServer(ctx, r.cache, r.cache):cache 同时充当 snapshot 存储与 stream callbacks。registerServer挂上 ADS、SDS、CDS、EDS、LDS、RDS、Runtime 七类 gRPC 服务。- 订阅 XdsIR watchable;每次更新调用
Translator.Translate,成功则GenerateNewSnapshot。
internal/xds/cache/snapshotcache.go
的注释写明:按 Node ID 切片快照,是为了「do incremental xDS
properly」。文件头注明实现派生自 Contour 的
snapshotter。NewSnapshotCache(ads=true) 把
go-control-plane 的 cache 置于 ADS 模式;同一结构体既实现
SotW 的 OnStreamRequest,也实现 Incremental 的
OnStreamDeltaRequest /
OnDeltaStreamOpen。官方 System Design
把该服务器概括为 Delta
xDS;源码同时接两条回调,排障时不要假设集群里绝对没有 SotW
流。
两条路径在 NACK 上的处理一致:ErrorDetail
非空时增加 xdsNACKTotal(按 node ID 与 type
URL),并写错误日志——Envoy 继续服务上一份
known-good;重新拉起的代理会加载这份被拒配置并失败。这与
v1.9.0 Release Notes 新增 xdsNACKTotal
的口径一致。第一份 snapshot 尚未写出时,cache 对
DiscoveryRequest 直接返回,让 go-control-plane
先回空响应——「Envoy 已连上控制面」可以早于「LDS
里有内容」。
Runner 对翻译错误的门闩也写在源码里,不是口误:
Translate返回系统级err != nil:不更新 snapshot,代理继续用上一份。- EnvoyPatchPolicy JSON patch 失败:视为用户错误,仍允许其余资源进快照。
update.Delete:对应该 IR key 生成空快照。
v1.9.0 把 xDS gRPC maxReceiveMessageSize
默认从 4MiB 提到
32MiB(EnvoyGateway.xdsServer.maxReceiveMessageSize
可改)。发行说明写的失败模式是:大规模集群里 Envoy 重连时的
delta 请求超过旧上限,流以 “received message larger than
max” 断开,代理停在 last
known-good。这是控制面推送轴上的容量问题,不是 HTTPRoute
YAML 写错。
GatewayNamespaceMode 下,同一 runner 改用 JWT interceptor 与每次握手重读磁盘证书(v1.9 安全更新:修复 xDS 认证绕过,并为 SotW 请求做校验)。认证失败的表象是代理连不上控制面,而不是「IR 空」。展开见第 10 篇。
协议层 ACK/NACK、nonce、SotW 与 Incremental 的报文差,以 envoy/10 为准。本篇只钉:EG 把 IR 编成 go-control-plane 快照;Delta 回调存在;NACK 有指标;快照更新不等于 Envoy 已切流量。
三、Infra Manager 与 EnvoyProxy
XdsIR 只描述「代理应加载什么配置」。谁把 Envoy 进程跑起来,是另一条管线。System Design 规定 Infra Manager 消费 Infra IR,对数据面做 CRUD(Deployment、Service 等),并可选地拉起辅助控制面(例如全局限流服务)。
tag v1.9.0
internal/infrastructure/manager.go 把
Manager 收成三个实现:
| Provider | 包 | v1.9.0 含义 |
|---|---|---|
| Kubernetes | internal/infrastructure/kubernetes |
默认:用 kube API 管舰队 |
| Host | internal/infrastructure/host |
Custom provider 的本机进程 |
| Remote | internal/infrastructure/remote |
v1.9 新增 remote infrastructure provider |
kubernetes.Infra.createOrUpdate 按
ResourceRender 依次保证
ServiceAccount、ConfigMap、Deployment、DaemonSet、Service、HPA、PDB。渲染器在
internal/infrastructure/kubernetes/proxy(proxy.ResourceRender
实现该接口)。Gateway 对象触发这份 InfraIR,是第 4
篇已经钉过的翻译输出;本层不再读 HTTPRoute
来决定副本数。v1.9.0 修过:当配置了 HPA 时生成的 Deployment
省略 spec.replicas,避免
server-side apply 的 ForceOwnership 把副本数从 HPA
手里抢回来。这是 Infra 轴上的所有权问题,不是 xDS
翻译错误。
flowchart LR
infroir["InfraIR"] --> mgr["Infra Manager"]
proxycr["EnvoyProxy CR"] --> mgr
mgr --> sa["ServiceAccount"]
mgr --> cm["ConfigMap bootstrap"]
mgr --> deploy["Deployment or DaemonSet"]
mgr --> svc["Service"]
deploy --> envoy["Envoy v1.39 dataplane"]
svc --> envoy
图:InfraIR 与可选 EnvoyProxy 参数进入 Infra Manager,产物是可调度的数据面对象,与 xDS 快照并行,不互相替代。
EnvoyProxy 挂在 GatewayClass
parametersRef(或更具体的
Gateway)上,用来改默认基础设施:Service
类型、副本、bootstrap 片段。System Design
写明未指定则用默认参数。v1.9 的
mergeBackends、GatewayNamespaceMode、remote
infra 细节进第 10 篇;本篇只要求记住:没有舰队,xDS
没有消费者;有舰队而无对应 IR key
的快照,代理拿到的是空或旧配置。
两条管线失败时的表象不同:
| 失败点 | 典型表象 | 先查 |
|---|---|---|
| Infra 未创建/未就绪 | Gateway Programmed 不绿、没有 Envoy Service
地址 |
InfraIR、EnvoyProxy、调度 |
| Infra 在、XdsIR 空 | 有 Pod、监听器与路由对不上 CR | 第 4 篇翻译与 status |
| 快照在、NACK | xdsNACKTotal 涨;流量仍是旧图 |
patch 名、非法 proto、消息尺寸 |
| ACK 后仍旧路由 | 证书/cluster warming、RDS 早于 CDS | envoy/11 |
四、「控制面已推送」不等于「Envoy 在服务」
把这句话拆成四层,才能和本系列五轴对齐:
- IR 层:Gateway API Translator 是否为该 Gateway 产出了对应 Listener/Route。status 绿而 IR 空,是第 4、13 篇的题。
- xDS 层:
Translate是否成功写入该 IR key 的 snapshot;Envoy 是否 ACK/NACK。NACK 时用户侧可以完全无感——发行说明与 cache 注释都写了「仍服务 last known-good」。 - 数据面 warming:ACK 只表示本批资源通过隔离校验并意图应用。Cluster/Listener 还要等 RDS/EDS/SDS 依赖齐。这是 Envoy 协议的规范锚点,不是 EG 特有 bug。见 envoy/11。
- 入口之后:请求已经出 Envoy,失败在 CNI/identity。坐标换到 cilium/15,不要在本层改 HTTPRoute。
Runner 还有一处与「已推送」相关的时序:cache 在尚无 snapshot 时对 DiscoveryRequest 直接返回,让 go-control-plane 回空响应,等第一份 snapshot 再推。因此「控制器 Ready、Envoy 已连上」可以早于「第一份有内容的 LDS」。排障口令仍是先点名轴,而不是重装 Helm。
v1.9.0 把「部分翻译失败仍发半棵树」的风险收在系统级
err 上:有 err 就不
SetSnapshot。代价是一次扩展 hook 失败会让整个
Gateway
的配置冻结在旧快照——这是正确性选择,不是静默丢更新。用户可见信号应落在第
11 篇的 xdsNACKTotal 与第 13 篇的 IR/xDS
轴,而不是伪造一份 config_dump。
生产联调清单(语义级,本篇未跑):
- 先确认 Infra:Envoy Deployment/Service 是否存在、Gateway
地址是否从 Service 或
externalIPs回填(v1.9 修过无 LB 时AddressNotAssigned误报)。 - 再确认 xDS:有无 NACK 指标、控制面日志是否在报 type URL 级拒绝。
- 最后才问数据面:warming、证书、上游耗尽——方言在 envoy/15。
第 13 篇会把这三步收进「未 Programmed / IR 空 / xDS NACK / Envoy 旧快照」坐标系。本篇的结论只需记住:Infra 与 xDS 是并行管线;任一成功都不能代替另一条;二者都成功仍不能代替 Envoy warming。 把「egctl 显示已同步」理解成「客户端已经走到新 backend」,会在证书旋转和 RDS 早到窗口里误判。
五、谱系、争论与开放问题
谱系
可编程反向代理 + 动态发现(Matt Klein / Lyft Envoy 工程传统)
→ Envoy xDS 协议(LDS/RDS/CDS/EDS/SDS;SotW 与 Incremental)
→ go-control-plane 作为控制面库
→ Envoy Gateway:Gateway API → IR → xDS Translator + Infra Manager(v1.9.0)
→ 对照:Istio istiod 更直接的 CRD→xDS(外链 istio-xds/)
Envoy 把「配置」定义成按类型、按名字引用的资源树,而不是一份不可部分更新的静态文件。Incremental/Delta 要解决的是 SotW 整表推送在大规模节点上的带宽与收敛问题;EG 用 go-control-plane 的 snapshot cache 承接这一协议,而不是自研一套 gRPC schema。
争论:IR 多一跳是否值得
A 侧(EG 官方设计):IR 把 Gateway API 与 Envoy 资源树解耦,同一套 InfraIR 可以换 provider(Kubernetes / host / v1.9 remote),xDS 翻译可以独立演进。
B 侧(Istio 一类路径):少一层中间表示,排障时 CR 与 xDS 名字更容易对账;多一跳意味着「Accepted 绿、IR 空、快照旧」三种失败可以同时成立。
本系列不站队。v1.9.0 的工程事实是:IR 与 xDS 已经分开,值班必须会点名轴。若平台只有会改 HTTPRoute 的人、没有人读 snapshot/NACK,IR 的解耦收益会被定位时间吃掉。
开放问题
- IR 与 xDS
对账是否应成为一等公民:今天要对一份 HTTPRoute
问「进了哪棵 RDS」,仍依赖实现细节与 dump
工具。可检验:能否在不伪造
config_dump的前提下,用官方接口列出 IR key → snapshot 资源名。 - NACK
之后的用户可见性:
xdsNACKTotal在 v1.9 才成为一等指标;Route status 仍可能保持 Accepted。status 能否承担「配置已生效」SLO,留到第 13、16 篇。 - remote infra 的责任面:控制面推 xDS、别人管 Pod 时,warming 失败工单应先找哪一侧?第 10 篇只划边界,本篇不写未核厂商适配。
六、小结
- xDS Translator(
internal/xds/translator)把 XdsIR 编成 LDS/RDS/CDS/EDS/SDS;best-effort,系统级错误不发半棵树。 - xDS Server(
internal/xds/runner+internal/xds/cache)用 go-control-plane;官方设计点名 Delta;NACK 记xdsNACKTotal,代理守 known-good。 - Infra Manager(
internal/infrastructure)把 InfraIR 变成舰队;与 xDS 并行。 - 「已推送」必须拆成 IR / snapshot / ACK / warming 四层;数据面内核外链 envoy 系列。
下一篇回到用户最常改的对象:HTTPRoute / GRPCRoute
如何进入 RDS,以及 v1.9 如何用
RouteRulesOverlap 标出同 listener
上的阴影规则。
七、参考资料
规范 / 官方文档(A)
- Envoy Gateway System Design:xDS Translator、xDS Server(go-control-plane Delta)、Infra Manager。
- Envoy Gateway v1.9 Concepts:控制面监视并翻译,数据面 Envoy 按生成配置处理流量。
- Envoy Gateway v1.9.0 Release
Notes:
xdsNACKTotal、默认 32MiB 接收上限、GatewayNamespaceMode xDS 认证修复。 - Envoy v1.39.0 xDS REST and gRPC protocol(warming、ACK 语义)——经站内 envoy/09–11 引用,不在本篇复述。
源码(A)
envoyproxy/gatewaytag v1.9.0:internal/xds/translator/translator.go(Translate)、internal/xds/translator/sds.go(processSDSClusters)、internal/xds/runner/runner.go(registerServer、snapshot 门闩)、internal/xds/cache/snapshotcache.go(ADS snapshot、Delta callbacks、xdsNACKTotal)、internal/infrastructure/manager.go、internal/infrastructure/kubernetes/infra.go。
核心论文 / 设计传统
- Envoy xDS 协议(官方规范):把控制面/数据面契约定义成按类型发现的资源树;SotW 与 Incremental 是同一契约的两种推送经济学。
- Matt Klein,Lyft Envoy 工程叙述(维护者博客,B 级):可编程代理 + 动态配置的生产起源;不单独支撑 v1.9.0 行为。
- 对照:Istio 控制面 CRD→xDS(站内 istio-xds),用于「有无 IR」分叉,不在本篇展开 istiod。
实验 / 工具
- 本篇无集群命令输出;不写 RPS,不粘贴伪造
config_dump/egctl。
站内对照
→ 上一篇:Gateway API Translator 与 IR · 系列目录 · 下一篇:HTTPRoute / GRPCRoute
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【Envoy Gateway】EnvoyProxy 与基础设施:谁管数据面生命周期,GatewayNamespaceMode 与 remote infra
钉清 EnvoyProxy CR 与 Infra Manager 对数据面 Pod 的所有权;说明 GatewayNamespaceMode 的 xDS 认证修复、v1.9 remote infrastructure provider 的责任面,以及 mergeBackends 仍是默认关闭的 experimental 能力。
【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 的选型收束。