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

【Envoy Gateway】角色与附着:GatewayClass / Gateway / Route 各保证什么

文章导航

分类入口
kubernetesnetwork
标签入口
#envoy-gateway#gateway-api#parentRefs#referencegrant#accepted#programmed#v1.9.0

目录

把一条 HTTPRoute apply 进集群之后,最常见的误判是:对象存在就等于已经挂到网关上。 Gateway API 把 Ingress 拆成三层资源,正是为了让「谁允许附着」与「路由规则写了什么」分开;失败因此可以停在 GatewayClass 未被本控制器接受、Gateway listener 未 Programmed、parentRefs 指错 section、跨 namespace 缺 ReferenceGrant,或 Route 的 ResolvedRefs=False。把这些状态揉成一句「控制器没生效」,下一步只能重装。

本文钉附着轴:GatewayClass / Gateway / Route 各自保证什么,AcceptedProgrammed 差在哪,以及跨 namespace 引用如何被 ReferenceGrant 否决。角色模型的 YAML 百科见 k8s-network/18;本篇不重写那套示例,也不把 18 的 v1.2 字段全集当成 v1.9.0。

本篇在系列中的位置

篇目 核心内容
第 1 篇 · 南北向全景 缺口、五轴坐标系、16 篇路线
第 2 篇 · 角色与附着 parentRefs、ReferenceGrant、status 条件
第 3 篇 · Provider 与 watch 哪些对象进入翻译输入集
系列目录 关键问题、阅读路径、全部价值点

版本锚定:Envoy Gateway v1.9.0(tag v1.9.0);Gateway API v1.6.1。status 语义以 Gateway API v1.6 规范、GEP-1364、官方排障文档为准;Envoy Gateway 如何计算这些条件以 tag 下 internal/gatewayapi/statusGateway API Translator Design 为准。无集群则不粘贴伪造 kubectl get httproute -o yaml status。


一、角色分离(回指 18,不重写长文)

Gateway API 的设计目标写在 GEP 过程与概念文档里:角色导向、可移植核心、标准化扩展点、协议多样性。 相对 Ingress 的单一对象,三层资源对应三种组织权限:

角色 对象 保证 不保证
基础设施提供者 GatewayClass 声明一种实现(spec.controllerName 集群里已经有数据面 Pod
集群运维 Gateway 声明入口:端口、协议、TLS、allowedRoutes 已有 Route 挂上来;地址已分配
应用开发者 HTTPRoute / GRPCRoute / TLSRoute / TCPRoute / UDPRoute 声明匹配与后端 父 Gateway 允许该 namespace / kind;引用的 Service 存在

Envoy Gateway v1.9 Concepts 把 GatewayClass、Gateway、五类 Route 标为 Gateway API 侧必选资源;EG 自己的 EnvoyProxy、各类 Policy、Backend 是扩展,本篇只在附着失败时点到 parametersRefEnvoyProxy 这一条,字段百科留给第 9–10 篇。

控制器认领范围由 GatewayClass.spec.controllerName 决定。官方 System Design:Envoy Gateway 通过比较自己配置的 controller name 与 spec.controllerName 来消费 GatewayClass;parametersRef 可指向 EnvoyProxy 以改数据面基础设施默认值。tag v1.9.0 的 GetRelevantGateways 会跳过 gatewayClassName 不匹配的 Gateway;指向无效 EnvoyProxy 的 Gateway 进入 failed 集合,并被标 Accepted=False / InvalidParameters

常见误区:在错误的 GatewayClass 下建 Gateway,Route 的 parentRefs 再正确也不会被本控制器更新 status。Gateway API 排障文档写死:实现只能给在自己所有权链上的对象写 status;parentRef 指到一个它不拥有的父对象时,不会出现「指错了」的 status——因为实现无法判断那是不是别人的责任。表象是 Route status.parents 为空或缺少本 controller 的条目,而不是一条醒目的错误条件。

GatewayClass 自身也有 Accepted。tag 上若 parametersRef 指向的 EnvoyProxy 校验失败,SetGatewayClassAccepted(..., false, InvalidParameters, ...) 会在 Provider 收集阶段就写 false,不必等到译 IR。集群里可以同时存在多个 GatewayClass;只有 controllerName 匹配的那些进入 managedGatewayClasses。多实现共存时,A 控制器留下的空 status 不证明 A「拒绝」了该 Route,只证明 A 认为对象不在自己的 scope。

站内 k8s-network/18 已经用图说明了这三层。本篇只回收这条不变量:权限边界写在 Gateway listener 的 allowedRoutes 上,路由意图写在 Route 上,二者没有自动合并。


二、parentRefs 与跨 namespace

Route 进入某个 Gateway listener 的编译输入,必须同时满足:

  1. spec.parentRefs 指向该 Gateway(可选 namespacesectionNameport)。
  2. 该 listener 的 allowedRoutes 允许这条 Route 的 kind 与所在 namespace。
  3. hostname / 协议与 listener 相交(HTTP/HTTPS 还要看 hostname 交集;Translator Design 的 outline 把「get matching listeners」写成显式步骤)。
  4. 跨 namespace 引用后端、证书或其它对象时,目标 namespace 存在匹配的 ReferenceGrant(下一节)。

sectionName 把附着钉到具名 listener,而不是整个 Gateway。省略 sectionName 时,实现按规范尝试匹配所有兼容 listener。v1.9.0 有一处相关修复:mergeBackendsparentRef 省略 sectionName 时,必须按 Route 实际附着到的 listener 判断 cluster 是否可合并,而不是按字面 sectionName。本篇不展开 mergeBackends(experimental,第 10 篇),只借它说明:省略 section 不是「随便挂」,是「按匹配规则展开成多个 listener」。

跨 namespace 有两条不同的边界,不要混:

边界 谁允许 失败条件
Route → Gateway Gateway listener 的 allowedRoutes.namespaces(Same / All / Selector) Route 所在 ns 不在允许集合;kind 不在 supportedKinds
Route → Service / Secret 等 目标 ns 的 ReferenceGrant 无 grant,或 From/To kind、namespace 不匹配

把「应用 ns 的 HTTPRoute 挂到 infra ns 的 Gateway」写成需要 ReferenceGrant,是错的:那是 allowedRoutes 的活。ReferenceGrant 管的是引用穿越 namespace——典型是 backendRefs.namespace、Gateway listener certificateRefs.namespace

allowedRoutes.kinds 不合法时,规范要求 listener 的 ResolvedRefs=False,reason InvalidRouteKindssupportedKinds 里不得出现实现不认识的 kind。协议与 kind 的默认对应(HTTP listener 默认 HTTPRoute 等)是 Core 行为,写错 kind 不会「尽量挂上」,而是 listener 条件失败、Route 匹配集合为空。

Listener 兼容性(同一 Gateway 内同端口可否折叠)由 System DesignTranslator Design 给出:按端口分组,HTTP 与 HTTP 可叠、HTTPS/TLS 可叠,但每组 hostname 必须唯一;一个 listener 可省略 hostname 作为兜底。Envoy Gateway 不跨多个 Gateway 合并 listener。冲突按 Gateway API 冲突解决规则处理,并反映到 listener status。展开匹配顺序见第 6 篇;本篇只要记住:两个同 hostname 的 HTTP listener 在同一端口上是不兼容的,会在附着轴上直接失败,到不了 IR。


三、ReferenceGrant

GEP-709(Standard)把跨 namespace 引用做成一次双向握手:源对象写引用,目标 namespace 必须有 ReferenceGrant 明确允许「哪种 From」指向「哪种 To」。资源曾名 ReferencePolicy,为避免与 Policy Attachment(GEP-713)混淆而改名。

握手的安全动机写在 GEP 正文:跨 namespace 转发若无目标侧同意,会出现 confused deputy。GEP-709 点名 CVE-2021-25740 作为前车。实现可以选择在有等效 NetworkPolicy 等机制时不尊 ReferenceGrant,但必须文档化;Ingress 类实现很少能做这个例外。Envoy Gateway 作为南北向入口实现,tag v1.9.0 的 Kubernetes provider 会 Watch ReferenceGrant,并在收集 backend / Secret / ConfigMap 跨 ns 引用时调用 findReferenceGrantinternal/provider/kubernetes/controller.go)。匹配逻辑是:grant 必须在目标 namespace;spec.from 命中源 kind+namespace;spec.to 命中目标 kind,且 to.name 为空或等于目标名。

Gateway listener 的 certificateRefs 跨 ns 时,v1.6 规范要求:无 Grant 则该 listener ResolvedRefs=False / RefNotPermitted,不得把证书装上。这与 Route 后端跨 ns 是同一握手,失败落点不同:证书失败停在 listener 条件,后端失败停在 Route parentResolvedRefs。v1.9.0 起 listener 未 Programmed(缺证书)不再把 Route Accepted 打假,所以会出现「Route Accepted=True、HTTPS listener 未 Programmed」——证书 Grant 补在 infra ns,不是改 HTTPRoute。

GEP-713 把 ReferenceGrant 与 Policy Attachment 区分为两类 metaresource:Grant 是「同意被引用」,Policy 是「改另一个对象的行为」。本篇不展开 SecurityPolicy;只要记住跨 ns targetRef 在 v1.9.0 也走 Grant(extension server policy 已支持),不要把 Policy 的 Discoverability 问题误诊成 parentRefs 写错。

Translator Design 的历史注释写过「ReferenceGrant is not fully implemented as of v0.2」。那是设计稿对 v0.2 的快照,不是 v1.9.0 事实。v1.9.0 还把 ReferenceGrant 扩到 extension server policy 的跨 ns targetRef。跨 ns 策略附着的细节在第 9 篇。

规范把失败钉在 ResolvedRefs=False,reason 为 RefNotPermitted:引用存在,但握手不同意。这与「对象根本不存在」(InvalidRef / BackendNotFound 一类 reason)不是同一条失败。排障时先看 reason,再决定是补 Grant 还是补 Service。

GEP-709 有意不把资源名写进 From(To 的 name 可选):能在该 kind 下写对象的人,本来就能改名绕过按名白名单。更细的限制应落在 RBAC 与 NetworkPolicy,而不是把 Grant 写成第二套 ACL 百科。


四、status 条件语义

Gateway API 把 Conditions 当成排障的第一证据。官方排障文档要求:先看 status.conditions;并核对 observedGeneration 是否等于 metadata.generation——对不上则 status 过期,可能是控制器故障,也可能是对象已离开该实现的 scope。

三条跨对象复用的正极性条件:

条件 规范含义(Gateway API 排障文档 / GEP-1364) 在 Envoy Gateway v1.9.0 的落点
Accepted 语义与句法可接受,将产生某些数据面配置,且已被控制器接受 Gateway:由各 listener 的 Accepted 汇总(全接受 / 部分 ListenersNotValid / 全否)。Route:写在 status.parents[],按 parentRef
Programmed 配置已解析并送往数据面,即将就绪;宣称数据面此刻已在服务流量 Gateway:地址已分配且 Envoy Deployment/DaemonSet 有可用副本;remote infra 时改为「远端基础设施可用」。缺地址 → AddressNotAssigned;无副本 → NoResources
ResolvedRefs 对象内引用均存在且对该字段合法 Route / listener:缺 Secret、缺 Service、跨 ns 无 Grant、不支持的 backend kind

GEP-1364 把 Programmed 从旧的 Ready 语义里拆出来:实现已经看见配置、备齐依赖、解析完毕、送到数据面,「soon」故意不定义。这不是文档含糊,而是承认数据面 warming、IP 分配、副本就绪的时间尺度因实现而异。站内 envoy/09–11 已经证明:xDS ACK ≠ Worker 在服务新快照。因此 Gateway Programmed=True 仍可能落在 IR 轴或 xDS 轴上失败——本轴只保证「控制器认为已经交给数据面一侧」。

Envoy Gateway 的计算职责写在 Translator Design:Translator 在译 IR 的同时计算 status,经消息总线交给 Status Manager,由 provider 写回 API Server。tag 上没有名为 internal/status 的包;条件辅助函数在 internal/gatewayapi/status

Gateway 级 Programmed 不是 Translator 单方面写完的。Kubernetes provider 的 updateStatusForGatewayinternal/provider/kubernetes/status.go)在订阅到 Gateway status 之后,若 Gateway 尚未被显式拒绝,会再读集群里的 Envoy Service 与 Deployment/DaemonSet,调用 UpdateGatewayStatusAccepted(从各 listener 的 Accepted 汇总)以及 UpdateGatewayStatusProgrammedCondition(填 status.addresses,再按副本或 remote infra 打 Programmed)。Translator Design 也写过:对 Kubernetes provider,Envoy Deployment 与 Service 状态要纳入 Gateway status。因此「Accepted 已绿、Programmed 仍 NoResources」是附着轴上合法的中间态——IR 可能已经有 listener,舰队还没有可用副本。写回通道见 第 3 篇

Implementer’s Guide 对 Accepted 另钉一句:它表示整份配置都合法,只表示「足够合法,能在实现所控的数据面产生某些效果」。部分 listener 失败时,v1.9.0 用 ListenersNotValid 仍把 Gateway Accepted 打成 True(至少一枚 listener 可接受)。把 Accepted 当成「全部 listener 健康」会漏掉半残 Gateway。

v1.9.0 发行说明有一条直接服务本轴的修复:listener 因缺 TLS 证书而未 Programmed 时,不再把已附着 Route 的 Accepted 打成 False。 规范层面 Route Accepted 只要求「能对某个父产生某些配置」;监听器证书未就绪是 Gateway/listener 的 Programmed 问题。升级到 v1.9.0 之后,若仍把「Route 未 Accepted」当成缺证书的信号,会误判。

Route status 不在对象根上的 conditions,而在 status.parents。每个 parentRef 一条,带 controllerName。超过 32 个 parent 时,tag 上 TruncateRouteParents 会截断并在最后一条打上实现条件 Aggregated。多 GatewayClass 场景下,gatewayapi runner 会先按资源聚合各 parent 的 status 再写回,避免多个 GatewayClass 互相覆盖。

v1.9 新增的 RouteRulesOverlap警告条件:同一 listener 上两条 Route 的 match 完全相同,后者会被静默阴影。本篇只登记它属于附着/status 轴的可观测信号;匹配算法见第 6 篇,排障口令见第 13 篇。

flowchart TD
  gc["GatewayClass"] --> gw["Gateway"]
  gw --> lis["Listener"]
  route["HTTPRoute"] -->|"parentRefs"| lis
  allow["allowedRoutes"] -.-> lis
  rg["ReferenceGrant"] -.->|"cross-ns refs"| route
  lis --> accL["Listener Accepted"]
  gw --> accG["Gateway Accepted"]
  gw --> prog["Gateway Programmed"]
  route --> accR["Route parent Accepted"]
  route --> res["Route ResolvedRefs"]

图:附着关系与条件落点。allowedRoutes 约束 Route→Gateway;ReferenceGrant 约束 Route→后端/证书等跨 ns 引用。Accepted 与 Programmed 不在同一层。


五、失败表象

下表把值班常见现象映射到本轴可核对的条件。没有集群实测输出;「应看到」是规范与 tag 源码推导的语义,不是伪造的 kubectl 片段。

表象 先看 典型原因 不是
Route status.parents 无本 controller GatewayClass controllerNameparentRefs 是否指向本实现拥有的 Gateway 指错 Gateway / 错 class;对象不在 scope,实现按规范不写 status 「控制器挂了」的唯一解释
Gateway Accepted=False InvalidParameters parametersRef / Gateway 级 EnvoyProxy EnvoyProxy 校验失败(GetRelevantGateways HTTPRoute 写错 path
Gateway Programmed=False AddressNotAssigned Service 类型、LB 控制器、spec.externalIPs 尚无地址;v1.9.0 允许在无 LB ingress 时回退 externalIPs Route match 错误
Gateway Programmed=False NoResources Envoy Deployment/DaemonSet 可用副本 数据面 Pod 未就绪 IR 翻译 bug(先排除本轴)
Listener Programmed=False,Route Accepted=True listener TLS certificateRefs v1.9.0 起二者拆开:缺证书不否定 Route Accepted 仍用 v1.8 以前的「Route 也会 False」直觉
Route ResolvedRefs=False RefNotPermitted 目标 ns 的 ReferenceGrant 跨 ns 握手失败 Service 不存在(那是另一 reason)
Route ResolvedRefs=False,backend 为 ExternalName 发行说明:ExternalName 被显式拒绝 应改用 EG Backend FQDN 「DNS 还没好」
条件绿但仍 404 本轴已排除后进入 IR 轴 阴影路由、hostname 未相交、IR 无对应 route 继续改 parentRefs

本轴明确不承诺的事Accepted=True 不是 SLO「流量已达后端」;Programmed=True 不是「Envoy Worker 已切换快照」。GEP-1364 把「soon」留给实现,Envoy Gateway 把 Gateway Programmed 钉在地址 + 副本(或 remote infra 就绪),而不是钉在 xDS ACK。后者属于第 5 轴之前的第 3 轴。

开放问题因此具体可检验:用 Gateway/Route 的正极性条件能不能组成「配置已生效」SLO? 若 SLO 定义为「Accepted ∧ Programmed ∧ ResolvedRefs」,漏掉 IR 空洞与 NACK;若再加 xDS ACK,仍漏 warming。第 13 篇用五轴否证顺序回答值班该怎么叠这些信号,而不把本轴条件宣传成端到端 SLO。


六、小结

  1. 三角色:GatewayClass 认领实现,Gateway 认领入口与允许谁附着,Route 认领规则;缺任何一层都不是「YAML 写了就生效」。
  2. 两条跨 ns 边界:Route→Gateway 走 allowedRoutes;Route→Service/Secret 走 ReferenceGrant(GEP-709)。
  3. Accepted ≠ Programmed:前者是「将被接受并产生某些配置」,后者是「已交给数据面一侧、soon」。v1.9.0 把缺证书的 listener 未 Programmed 与 Route Accepted 拆开。
  4. scopeparentRef 指到非本实现拥有的父对象时,可能没有任何错误 status。
  5. 下一层:对象已 Accepted 仍无监听,离开本轴,进入 Provider 是否把它放进翻译输入集,以及 Translator 是否写出 IR。

七、参考资料

规范 / 官方文档(A)

源码(A)

核心论文 / 奠基 work

实验 / 工具

站内对照


上一篇:南北向全景 · 系列目录 · 下一篇:Provider 与 watch

读完这篇,下一步读什么

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


By .