把一条 HTTPRoute apply
进集群之后,最常见的误判是:对象存在就等于已经挂到网关上。
Gateway API 把 Ingress
拆成三层资源,正是为了让「谁允许附着」与「路由规则写了什么」分开;失败因此可以停在
GatewayClass
未被本控制器接受、Gateway listener 未
Programmed、parentRefs 指错 section、跨
namespace 缺 ReferenceGrant,或 Route 的
ResolvedRefs=False。把这些状态揉成一句「控制器没生效」,下一步只能重装。
本文钉附着轴:GatewayClass / Gateway / Route
各自保证什么,Accepted 与
Programmed 差在哪,以及跨 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/status与 Gateway API Translator Design 为准。无集群则不粘贴伪造kubectl get httproute -o yamlstatus。
一、角色分离(回指 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
是扩展,本篇只在附着失败时点到 parametersRef →
EnvoyProxy 这一条,字段百科留给第 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 的编译输入,必须同时满足:
spec.parentRefs指向该 Gateway(可选namespace、sectionName、port)。- 该 listener 的
allowedRoutes允许这条 Route 的 kind 与所在 namespace。 - hostname / 协议与 listener 相交(HTTP/HTTPS 还要看 hostname 交集;Translator Design 的 outline 把「get matching listeners」写成显式步骤)。
- 跨 namespace 引用后端、证书或其它对象时,目标 namespace 存在匹配的 ReferenceGrant(下一节)。
sectionName 把附着钉到具名
listener,而不是整个 Gateway。省略
sectionName 时,实现按规范尝试匹配所有兼容
listener。v1.9.0 有一处相关修复:mergeBackends
在 parentRef 省略 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
InvalidRouteKinds;supportedKinds
里不得出现实现不认识的 kind。协议与 kind 的默认对应(HTTP
listener 默认 HTTPRoute 等)是 Core 行为,写错 kind
不会「尽量挂上」,而是 listener 条件失败、Route
匹配集合为空。
Listener 兼容性(同一 Gateway 内同端口可否折叠)由 System Design 与 Translator 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 引用时调用
findReferenceGrant(internal/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
parent 的 ResolvedRefs。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 的
updateStatusForGateway(internal/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
controllerName、parentRefs
是否指向本实现拥有的 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。
六、小结
- 三角色:GatewayClass 认领实现,Gateway 认领入口与允许谁附着,Route 认领规则;缺任何一层都不是「YAML 写了就生效」。
- 两条跨 ns 边界:Route→Gateway 走
allowedRoutes;Route→Service/Secret 走 ReferenceGrant(GEP-709)。 - Accepted ≠ Programmed:前者是「将被接受并产生某些配置」,后者是「已交给数据面一侧、soon」。v1.9.0 把缺证书的 listener 未 Programmed 与 Route Accepted 拆开。
- scope:
parentRef指到非本实现拥有的父对象时,可能没有任何错误 status。 - 下一层:对象已 Accepted 仍无监听,离开本轴,进入 Provider 是否把它放进翻译输入集,以及 Translator 是否写出 IR。
七、参考资料
规范 / 官方文档(A)
- Gateway API v1.6.1 API 规范 —
gateway-api.sigs.k8s.io/reference/api-spec/1.6/spec/(Gateway/Route status 默认条件、certificateRefs跨 ns 与RefNotPermitted)。 - Gateway API Troubleshooting and Status —
Accepted/Programmed/ResolvedRefs;observedGeneration;scope 与「错误 parentRef 不写 status」。 - Gateway API Implementer’s Guide —
Accepted只保证「产生某些数据面配置」,不保证整份配置合法。 - GEP-1364 Status and Conditions Update —
Programmed相对Ready;正极性条件应始终存在。 - GEP-709 Cross Namespace References from Routes — ReferenceGrant 握手;CVE-2021-25740。
- GEP-713 Metaresources and Policy Attachment — 本篇只作谱系;Policy 展开见第 9 篇。
- Envoy Gateway v1.9 Concepts;Gateway API Translator Design(status 由 Translator 计算、Status Manager 写回)。
- Envoy Gateway v1.9.0 Release Notes — Route Accepted 与 listener Programmed 分离;ExternalName 后端拒绝。
源码(A)
envoyproxy/gatewaytag v1.9.0:internal/gatewayapi/status/gateway.go(UpdateGatewayStatusAccepted、UpdateGatewayStatusProgrammedCondition)internal/gatewayapi/status/route.go(SetRouteStatusCondition、TruncateRouteParents)internal/gatewayapi/translator.go(GetRelevantGateways)internal/provider/kubernetes/controller.go(findReferenceGrant、WatchReferenceGrant)internal/provider/kubernetes/status.go(updateStatusForGateway:汇总 Accepted,并用 Service/Deployment 填 Programmed)
核心论文 / 奠基 work
- GEP-1364、GEP-709 —— 定义本轴的条件模型与跨 ns 握手,而非后加的实现细节。
- Burns et al., Borg, Omega, and Kubernetes, ACM Queue 2016 —— status 写在 API 对象上的控制器传统;Gateway API 把该传统收紧为「尽量不看实现日志」。
实验 / 工具
- 本篇无
kubectl/egctl输出。实验台账:未跑。验证顺序(有集群时):先对observedGeneration,再读 Gateway 条件,再读 Routestatus.parents,最后才改 YAML。
站内对照
← 上一篇:南北向全景 · 系列目录 · 下一篇:Provider 与 watch
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【Envoy Gateway】南北向全景:缺口、五轴坐标系与 16 篇路线
相对 Gateway API 产品教程、Cilium 南北向边界、Envoy 消费侧与 istiod 翻译,钉清 Envoy Gateway v1.9.0 控制面内核缺口;定义附着/status、IR、xDS、Envoy 数据面、入口之后东西向五条轴,给出 16 篇阅读路线。
【Envoy Gateway】Gateway API Translator 与 IR:对象如何变成 XdsIR / InfraIR
钉清 Envoy Gateway v1.9.0 为何用中间表示解耦 Gateway API 与 Envoy 资源树:XdsIR 与 InfraIR 的分工、Translate 的顺序与依赖,以及 status 为何必须在同一次翻译里计算。
【Envoy Gateway】HTTPRoute / GRPCRoute:匹配进入 RDS,阴影要靠 RouteRulesOverlap
钉 HTTPRoute/GRPCRoute 的附着、hostname、matches/filters/backendRefs 如何进入 RDS;对照 Gateway API v1.6.1 匹配优先级,以及 v1.9 RouteRulesOverlap 对同 listener 相同匹配的警告。
【Envoy Gateway】L4 路由:不升 v1.6 CRDs,TCP/UDP 会静默消失
钉 TCPRoute/UDPRoute/TLSRoute 在 Gateway API v1.6 的版本;说明 EG v1.9 只 reconcile v1,standard channel 停服 v1alpha2,以及失败表象是无流量而不是 HTTP 500。