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

【Envoy Gateway】Provider 与 watch:哪些对象进入翻译输入集

文章导航

分类入口
kubernetesnetwork
标签入口
#envoy-gateway#provider#watch#informer#status-manager#envoygateway-api#v1.9.0

目录

附着轴已经回答「规范上这条 Route 该不该生效」。下一步误区是:etcd 里有对象,Translator 就看得到。 Envoy Gateway 并不在每次 API 事件里单独翻译那一个 HTTPRoute。Kubernetes provider 用 informer watch 一组类型,把属于本 controllerName 的 GatewayClass 收成一份 resource.Resources 快照,再整份交给 Translator。漏 watch、CRD 不存在、watch 被收窄到错误 namespace、status 写回在 cache 滞后时被丢掉,表象都是「YAML 在、流量不在」,但失败发生在翻译之前

本文钉 Provider 轴:哪些对象进入翻译输入集,status 如何写回,漏 watch 长什么样。静态启动配置(EnvoyGateway API)只决定 provider 类型、watch 范围与 controllerName,不代替 Gateway API 对象。

本篇在系列中的位置

篇目 核心内容
第 2 篇 · 角色与附着 parentRefs、ReferenceGrant、Accepted / Programmed
第 3 篇 · Provider 与 watch 输入集、Status Manager、静态配置边界
第 4 篇 · Translator 与 IR 快照如何变成 XdsIR / InfraIR
系列目录 关键问题、阅读路径、全部价值点

版本锚定:Envoy Gateway v1.9.0(tag v1.9.0)。路径以该 tag 的 internal/provider/ 为准:不存在 internal/status 包;写回在 internal/provider/kubernetes/status_updater.go。官方 System Design 仍写「file provider 在 Issue #37 路线图」——与 tag 源码冲突,正文以 tag 为准。无集群则不伪造 informer 日志或 egctl


一、Kubernetes provider

官方 System Design 把 Provider 定义成 Envoy Gateway 用来建立运行时配置、做服务发现、持久化的基础设施组件。动态配置来自集群对象;静态配置来自启动时的配置文件(即 EnvoyGateway API)。组件之间的进程内通信由 Watching Components 设计描述。

tag v1.9.0 的 Provider 接口只有 StartTypeinternal/provider/resource_provider.go)。internal/provider/runner/runner.goEnvoyGateway.Provider.Type 分支:

Kubernetes provider(internal/provider/kubernetes/kubernetes.go)用 controller-runtime Manager 搭脚手架:Scheme、health probe :8081、可选 leader election(Lease ID 5b9825d2.gateway.envoyproxy.io)、cache。就绪检查 cacheReadyCheck 要求 informer cache 已经同步,避免多副本在资源抖动时对外提供不一致的 xDS。当选 leader 后关闭 svrCfg.Elected,让需要领导权的任务继续——status 写回的 UpdateHandler.NeedLeaderElection() 返回 true,非 leader 不写 status。

这是 Burns 等人描述的 Kubernetes 控制器回路的实例:期望状态在 API 对象上,调和循环把实际状态推过去。Envoy Gateway 的特殊点是:一次 Reconcile 不对应某一个 HTTPRoute 的 NamespacedName。 Reconcile 注释写明任何资源事件都 enqueue 同一 request(gateway controller name),以便多次更新合并成一次全量收集。

File provider:tag 有,System Design 仍写路线图

System Design 的 Provider 节仍写:截至 v0.2 只实现了 Kubernetes;file provider 在 Issue #37 路线图上。该句是历史叙述。

tag v1.9.0 存在完整包 internal/provider/file/file.gostore.gostatus.go),runner 在 ResourceProviderTypeFile 时调用 file.New。发行说明还修过 File provider 使用的 standalone offline 控制器索引。因此:不能把 file provider 写成 v1.9.0 未实现;也不能把它写成默认生产路径。本系列主线是 Kubernetes provider。Custom/File 用于离线/standalone 输入,不把集群 informer 当权威源;其 status 写回语义与 Kubernetes UpdateHandler 不同,本篇不展开文件格式。

v1.9.0 另有 remote infrastructure providerInfrastructureProviderTypeRemote),管的是数据面舰队谁来 CRUD,不是 Resource Watcher 的输入源。责任面见第 10 篇。


二、Resource Watcher 输入集

System Design 称 Resource Watcher 以 provider 相关机制(informer、cache)监视动态配置,输出给 Translator。tag 上对应 gatewayAPIReconciler.watchResources + Reconcile 里按 GatewayClass 填充的 resource.Resources

2.1 监视哪些 kind

watchResourcesinternal/provider/kubernetes/controller.go)为每个 kind 注册 source.Kind watch,handler 一律 enqueueClass。部分 kind 先 discovery 再决定是否 watch,避免集群缺少该 CRD 时控制器启动即 crash-loop。v1.9.0 发行说明把这写成针对 GKE 托管 Gateway API addon(缺 ListenerSet / GRPCRoute / TLSRoute)以及 OpenShift 托管集(缺 BackendTLSPolicy)的修复。

启动时按 CRD 是否存在而可选的 watch 包括:EnvoyProxy、ListenerSet、GRPCRoute、TLSRoute、UDPRoute、TCPRoute、ServiceImport、BackendTLSPolicy 等。HTTPRoute、Gateway、GatewayClass、Service、EndpointSlice、ReferenceGrant 走常规路径(仍受 namespace 选择器约束)。

这与第 8 篇的 L4 版本门是两层不同的静默

条件 表象
Watch 层 集群没有 TCPRoute/UDPRoute CRD 控制器跳过 watch 并打日志;对象根本进不了 informer
API 版本层 CRD 在,但 EG v1.9 只调和 gateway.networking.k8s.io/v1 未升到 Gateway API v1.6 时 TCP/UDP 静默跳过(Release Notes / Helm 升级说明)

值班若只看见「TCPRoute YAML 还在」,必须先问 CRD 的 served version,再问 provider 有没有 watch 该 kind。控制器不会把「我跳过了 v1alpha2」写成一条 Route 条件——对象可能根本不在输入集里。

所有 watch 的 handler 都 enqueueClass:不把 HTTPRoute 的 NamespacedName 放进 queue,而是 enqueue 本控制器名。结果是任意相关对象变更都触发同一条全量 Reconcile。managedGatewayClasses 先列出 controllerName 匹配的 GatewayClass;每个 class 单独 NewResources()parametersRef 处理失败(且非 transient)时,该 GatewayClass 被标 Accepted=False / InvalidParameters,status 立刻 store——不必等到 Translator。transient 错误(timeout、429、apiserver 不可用等)则返回 error 让 workqueue 重试,避免把暂时读失败写成永久非法参数。

2.2 收集成 resource.Resources

Reconcile 对每个本控制器管理的 GatewayClass NewResources(),然后填充 System Design 所说的三类东西:Gateway API 资源、EG 扩展资源、被引用的核心对象。tag 上 internal/gatewayapi/resource/resource.go 的结构体字段就是这份输入集的权威清单,包括:

跨 ns 的 Secret / Service / ConfigMap 只有在 findReferenceGrant 成功(或同 ns)后才进入树。策略跨 ns targetRef 另走 processPolicyTargetReferenceGrants:先粗过滤可能相关的 Grant,细匹配留在 Translator。漏掉 Grant,对象可以在 etcd 里,却不在 Resources.ReferenceGrants 里——Translator 只能报 RefNotPermitted 或根本看不到后端。

Namespace 范围由静态配置 provider.kubernetes.watch 收窄。WatchesNamespaces() 为真时,cache DefaultNamespaces 只覆盖所列 ns;v1.9.0 修复要求 namespace-scoped watch 始终包含 controller namespace,否则控制器读不到自己的基础设施对象。把应用 ns 写进 watch 列表却忘掉 envoy-gateway-system,表象是数据面 Deployment/Secret 像「丢了」,其实是 cache 里没有。

另一种收窄是 watch.Type = NamespaceSelectorbyNamespaceSelectorEnabled 要求 selector 至少有 matchLabels 或 matchExpressions,然后对 Gateway、Route、EnvoyProxy 等叠加 hasMatchingNamespaceLabels predicate。名单模式漏的是「没写进列表的 ns」;选择器模式漏的是「namespace 标签不匹配」。两种都不会在被排除的 HTTPRoute 上写「我不 watch 你」——对象直接不在输入集,status 保持空或过期。平台若用 selector 做多租户隔离,必须把「租户 ns 的标签契约」写进上线检查单,否则新 ns 里的 Route 会像「控制器坏了」。

2.3 刻意缩小的 cache

为降低内存与同步抖动,Kubernetes provider 对若干只读类型关闭 deepcopy(Secret、ConfigMap、Service、EndpointSlice、Node、ReferenceGrant),Pod cache 限制为 Envoy 数据面标签。这不是漏 watch:拓扑注入 webhook 仍能看到代理 Pod。发行说明把 EndpointSlice 字段索引默认打开(EndpointSliceIndex),并警告大规模 EndpointSlice 会抬高控制器内存——可禁用该 runtime flag。另一条修复禁止「安装了 HTTPRouteFilter CRD 之后,集群里任意 Secret 变更都触发全量 reconcile」,否则证书轮转控制器会造成持续风暴。

漏 watch 的值班口令:先问「该 kind 的 informer 在不在、namespace 在不在清单里、CRD 在不在」,再问 Translator。 把缺 CRD 当成翻译 bug,会在第 4 篇空转。

漏 watch / 输入集残缺 典型静态原因 表象(语义,非实测输出)
未装 TCPRoute/UDPRoute CRD discovery 跳过 watch L4 YAML 仍在 etcd,无 Route parent status、无 L4 监听
watch.namespaces 未含应用 ns DefaultNamespaces 收窄 该 ns 的 HTTPRoute 从不 enqueue 进本控制器的全量收集
watch.namespaces 未含 controller ns v1.9.0 已修;旧配置仍会踩 读不到自身 Deployment/Secret,舰队像消失
跨 ns Secret 无 Grant 不进 Resources.Secrets listener TLS 失败或 RefNotPermitted
enableBackend=false backendAPIDisabled() Backend CR 即使存在也不作为路由后端
HTTPRouteFilter CRD 已装、Secret 风暴 v1.9.0 前的索引过宽 控制器持续全量 reconcile,status 滞后

backendAPIDisabled 在 Backend CRD 不存在或静态 extensionApis.enableBackend 未打开时为真。这又是一层「对象在 etcd、不在输入集」:与缺 Grant 不同,这里是功能门,不是握手失败。

sequenceDiagram
  participant API as APIServer
  participant Watch as ResourceWatcher
  participant Rec as Reconcile
  participant Snap as ResourcesSnapshot
  participant Bus as WatchableBus
  participant Tr as Translator
  API->>Watch: Gateway Route Secret events
  Watch->>Rec: enqueueClass
  Rec->>Snap: collect per GatewayClass
  Snap->>Bus: ProviderResources
  Bus->>Tr: Translate input set

图:watch 事件不直接翻译单对象;合并进按 GatewayClass 划分的快照后再交给 Translator。节点与参与者为英文;正文解释顺序。


三、Status Manager

Translator Design:Translator 计算条件并经消息总线发布;Status Manager 订阅这些消息,用已配置的 provider 写回。tag 上这条链拆成三段,没有单名为 Status Manager 的包:

  1. 计算gatewayapi.TranslatorTranslate 里改对象的 Status(先 StatusDeepCopy 以免与 watchable coalesce 的 reflect.DeepEqual 竞态;该竞态在 v1.9.0 以 panic 形式修过)。
  2. 发布internal/gatewayapi/runner 订阅 provider 快照,调用 Translate,把 IR 写入 XdsIR/InfraIR,把 status 写入 ProviderResources 上各 *Statuses map;多 GatewayClass 共享的 Route/Policy 先聚合 parent/ancestor 再 store。
  3. 写回:Kubernetes provider 的 gatewayAPIReconciler.subscribeToResources 订阅这些 map;UpdateHandlerstatus_updater.go)在独立 goroutine 里 Status().Update。通道缓冲 1000;leader 才跑。

UpdateHandler.apply 在 cache Get 返回 NotFound 时,会再用 uncached apiReader 确认对象是否真的不存在。v1.9.0 发行说明(issue #9536):informer cache 落后 API Server 时,旧逻辑会把新建对象的 status 更新静默丢掉,直到控制器重启。高 churn 下「Route 一直没有 status」优先怀疑本段,而不是怀疑 Translator 没算。

写回前 isStatusEqual 忽略 LastTransitionTime,避免无意义的 status 抖动。支持的 kind 包括 GatewayClass、Gateway、五类 Route、各类 EG Policy、BackendTLSPolicy、Backend、以及 extension 的 Unstructured。

Gateway 的写回比 Route 多一跳。订阅到 Translator 算出的 GatewayStatus 之后,updateStatusForGateway 会再 Get 一次 Gateway,拉 Envoy Service 与 Deployment/DaemonSet:若尚未被翻译期显式拒绝,就从 listener 条件汇总 Accepted,并用活的基础设施对象填 status.addresses 与 Programmed。NodePort 且 externalTrafficPolicy: Local 时,v1.9.0 只把有就绪 Envoy 端点的节点写入地址,避免把没有代理 Pod 的节点 IP 标成已编程。remote infra 则走「远端基础设施可用」的 Programmed 文案,不再数副本。

这意味着 Provider 轴同时负责两件事:把对象送进翻译输入集,以及把翻译看不到的活基础设施折进 Gateway Programmed。只读 Translator 结果会漏掉「IR 已有、副本为零」。

File provider 有自己的 internal/provider/file/status.go,不走 Kubernetes API。主线排障不要把两种写回混为一谈。

与附着轴的分工:第 2 篇解释条件含义;本篇解释条件如何从 Translate 结果落到 etcd。status 空可能是:对象不在 scope(实现按规范不写)、写回被 NotFound 丢掉(已修,但仍受 cache 健康影响)、或非 leader 副本。不要用「没有 status」单独证明 YAML 非法。


四、与静态启动配置(EnvoyGateway API)的边界

System Design 把配置分成两截:

种类 载体 何时生效 例子
静态 启动配置文件 / Helm config.envoyGateway 进程启动(v1.9.0 另有 hot-reload 路径,校验前先 apply defaults) provider.type、watch namespace 列表、gateway.controllerNameextensionApis.enableLua / enableBackend、runtime flags
动态 Gateway API + EG CR reconcile 循环 Gateway、HTTPRoute、SecurityPolicy、EnvoyProxy CR

边界规则:静态配置决定「看哪些对象、叫什么控制器、哪些扩展 API 打开」;动态对象决定「译出什么 IR」。把 Lua 策略写在集群里却不在静态配置里 enableLua,对象可以进输入集,翻译仍按关闭处理——失败更像第 9 篇的功能门,但也是本篇的静态/动态交界。

v1.9.0 修过 EnvoyGateway 配置 hot-reload:reload 路径与启动路径一样,先 apply defaults 再校验,避免半初始化结构把合法配置判死。静态文件改了 watch 范围或 controllerName,等于换了一份输入集定义;正在调和的动态对象不会自动「解释」旧范围。换 watch 列表属于控制面重启/reload 事件,应在变更单里当输入集变更,而不是当 HTTPRoute 变更。

EnvoyProxy CR 是动态的,可通过 GatewayClass parametersRef 或 Gateway 级引用进入 ResourcesEnvoyProxyDefaultSpec 则从静态 EnvoyGateway 拷进每份快照,作为未指定 CR 时的最低优先级默认。三者冲突时的合并在 Translator 的 attachEnvoyProxy,本篇只要求:查「数据面副本数为什么不是我写的」时,先分清值来自静态默认、GatewayClass 级 CR,还是 Gateway 级 CR。

v1.9.0 把 xDS gRPC maxReceiveMessageSize 默认从 4MiB 提到 32MiB,字段在 EnvoyGateway API(静态)。超大集群重连时请求超过旧上限,流断掉、代理停在 known-good——这是静态配置与 xDS 轴的交界,展开在第 5、12 篇。不要在动态 HTTPRoute 上找这个旋钮。

开放争论:watch 完整性 vs 缺 CRD 时仍能启动。 A 侧要求标准通道 kind 必须存在,缺则失败,避免静默丢功能;B 侧(v1.9.0 选择)让 ListenerSet/GRPCRoute/TLSRoute/TCPRoute/UDPRoute/BackendTLSPolicy 的 watch 以 discovery 为准,托管发行才能跑起来。代价正是第 8 篇的静默跳过。平台若用 GKE/OpenShift 托管 CRD,必须把「本集群实际 served 的 kind 集合」写进上线检查单,不能假设 Helm chart 的 experimental 通道清单。


五、小结

  1. 一次 Reconcile 收集整个 GatewayClass 的输入集,不是按单个 Route 翻译。
  2. 输入集以 resource.Resources 字段为准;跨 ns 引用还要 Grant 才能进树。
  3. 缺 CRD 则跳过 watch;TCP/UDP 另有「只调和 v1」的静默层。
  4. Status Manager 在 tag 上是 runner 发布 + UpdateHandler 写回;cache NotFound 曾导致 status 永久空。
  5. 静态 EnvoyGateway API 不替代动态对象;file provider 在 v1.9.0 已实现为 Custom/File,但不是本系列默认路径。System Design 的「路线图」句已过时。

下一篇从这份快照进入 Translator:对象已在输入集,仍可能在 IR 里消失。


六、参考资料

规范 / 官方文档(A)

源码(A)

核心论文 / 奠基 work

实验 / 工具

站内对照


上一篇:角色与附着 · 系列目录 · 下一篇:Translator 与 IR

读完这篇,下一步读什么

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


By .