升 Envoy Gateway 到 v1.9.0 却仍使用旧
Gateway API CRDs 时,TCPRoute / UDPRoute 不会改成返回 HTTP
500,控制器也不保证弹一条「请升级
CRD」的错误来提醒你。发行说明写的是:经
gateway.networking.k8s.io/v1
reconcile;若未安装 v1.6 CRDs,这些路由被静默跳过。
数据库、DNS、游戏隧道的入口会变成「端口听着、没有后端」,像防火墙,不像应用故障。
本文只钉版本门:v1.6 里三种 L4/L7-ish Route 的 API
版本、EG v1.9 只盯 v1、standard 与 experimental
channel 对 v1alpha2
的差异、以及失败表象。不把「控制器会报错提醒」写成 v1.9
保证。存储版本迁移的运维步骤与 Helm 分装见第 12 篇。
本文是「Envoy Gateway / Gateway API」系列第 8 篇(共 16 篇)。→ 系列目录
篇目 核心内容 第 7 篇 · TLS / SDS 两层 TLS 与 SDS 破坏面 第 8 篇 · L4 路由 v1 reconcile;CRD 未升则静默跳过 第 9 篇 · Policy attachment 五类 Policy 与 mergeType 上一篇:TLS / SDS · 下一篇:Policy attachment
版本锚定:Envoy Gateway v1.9.0 Release Notes 破坏性变更;Gateway API v1.6.1 CRD(standard / experimental 的
served/storage字段已核 raw YAML);TLSRoute 自 v1.5.0 起已在 Standard 且为v1。实验台账:未跑。
一、TCPRoute / UDPRoute / TLSRoute 在 v1.6 的 API 版本
Gateway API v1.6.0(2026-06-30)把
TCPRoute、UDPRoute 从 Experimental 推到 Standard,API
版本改为
v1。v1alpha2
自该版本起 deprecated,将来删除。Kubernetes
博客与 GEP-2644 / GEP-2645
把它们定义成:只按协议与端口转发,没有 HTTP 层。
TLSRoute 更早:Gateway API 文档写 Standard
Channel since v1.5.0,资源已是
gateway.networking.k8s.io/v1。它按 TLS 握手的
SNI 选后端,可 Passthrough 或 Terminate,不是「又一个
TCPRoute」。
| 资源 | 进入 Standard | v1.6.1 推荐 apiVersion | 匹配面 |
|---|---|---|---|
| TLSRoute | v1.5.0 | gateway.networking.k8s.io/v1 |
SNI hostname(字段必填) |
| TCPRoute | v1.6.0 | gateway.networking.k8s.io/v1 |
listener 协议 TCP + 端口;无 L7 |
| UDPRoute | v1.6.0 | gateway.networking.k8s.io/v1 |
listener 协议 UDP + 端口;无 L7 |
TCPRoute 只能挂 protocol: TCP 的
listener;挂到 HTTP/HTTPS 上不会被接受。UDP
同理。规范允许同一端口上 TCP 与 UDP listener 并存(例如 DNS
53)。Kubernetes 博客里的最小例子:Gateway listener
protocol: TCP +
allowedRoutes.kinds: TCPRoute,TCPRoute
parentRefs.sectionName 指向该
listener,backendRefs 给出 Service 端口。省略
sectionName 与 port
时,路由附着到该 Gateway 上每一个 TCP
listener——比 HTTPRoute 更容易「一条 Route
打到多个端口」,升级或复制 Gateway 时要核对。
GEP-2644 写明 TCPRoute 补的是 HTTPRoute/TLSRoute
盖不住的那类 TCP 工作负载(非 HTTP 的 TLS 数据库、纯
TCP)。它几乎没有匹配算法:没有 path,没有
header。版本门因此比匹配门更致命——对象一旦没进
informer,翻译器没有「部分匹配失败返回 404」可报。该 GEP 把
Standard 毕业条件写成:列出 feature name
TCPRoute、补齐 conformance
场景、至少三份实现提交通过报告。v1.6
宣布毕业,并不自动改写集群里已经 applied 的
v1alpha2 清单。
EG v1.9 TCP routing 任务演示:Gateway 上两个 TCP
listener(8088 / 8089),两条 TCPRoute 用
parentRefs.sectionName
分别附着。该任务页示例仍写
apiVersion: gateway.networking.k8s.io/v1alpha2。
这与同版本 Breaking notes 以及 standard-channel CRD 的
served: false
不一致,应视为文档滞后。照抄到只装了 v1.6.1
standard CRDs 的集群时,apiserver
不会再服务
v1alpha2(第三节)。新清单应使用
v1。
TLSRoute 的 Terminate 与 TCPRoute 的差别见第 7 篇:后者不看 SNI,一条 listener 对一个后端;前者按 SNI 扇出。Passthrough 则根本不在网关终止 TLS。
二、EG v1.9 只 reconcile
v1
v1.9.0 Breaking changes 原文:
Envoy Gateway now reconciles
TCPRoute/UDPRoutevia thegateway.networking.k8s.io/v1API. You must upgrade the Gateway API CRDs to v1.6 with this release. Existingv1alpha2manifests continue to work (both versions are served), but if the v1.6 CRDs are not installed, TCP/UDP routes will be silently skipped. The stored version moves to v1; a storage-version migration will be required before v1alpha2 is eventually removed.
拆成三条互不替代的事实:
- 控制器 watch 的是
v1。 集群里若只有旧的v1alpha2CRD、没有 v1.6 的v1,EG 看不到这些对象,翻译输入集为空。这就是「静默跳过」:不是 NACK,不是 HTTPRoute 那种 404 业务响应。 - 「现有 v1alpha2 manifest 仍可用、两个版本都 served」 描述的是 已经装上 v1.6、且 channel 仍服务 v1alpha2 的情况(实验频道 CRD,见第三节)。不能理解成「永远不用改 yaml」。
- 存储版本改为 v1。 对象若仍以 v1alpha2 为 storage version,在未来 CRD 去掉该版本之前必须做存储版本迁移(Gateway API CRD Management 指南;工具如 kube-storage-version-migrator)。第 12 篇展开,本篇只点名风险。
EG 对「CRD
根本不存在」另有一条长期策略:TCPRoute/UDPRoute 等 watch 在
CRD 缺失时跳过,避免整控制器 crash-loop(历史 issue #3387
一类;v1.9 还把 ListenerSet、GRPCRoute、BackendTLSPolicy
做成存在性检查)。跳过启动失败 ≠ 在 status 里提醒你
L4 没了。 egctl x status 在 v1.9
也改为:缺 CRD 时静默跳过,或 -v 时打到
stderr。排障信号可能是「工具没列出
TCPRoute」,不是一条醒目的 Failed condition。
因此升级检查单的第一项不是 curl
HTTP,而是:Gateway API CRDs 是否已到 v1.6.x,且
TCPRoute/UDPRoute 的 v1 是否 served。
Helm 只升 gateway-helm 而
--skip-crds 留着旧 CRD,正好踩进这个门。
三、standard
vs experimental channel 与 v1alpha2
停服风险
Gateway API 用 release channel 而不是 SemVer 单独表达稳定性。Standard 的版本删除分四步(官方 CRD Management):新版本成为 storage → 旧版本 deprecated → 旧版本不再 served 但仍留在 CRD 里做转换 → 彻底从 CRD 去掉。
对 v1.6.1 的 TCPRoute CRD,本仓库核过 GitHub raw YAML:
| Channel | 文件 | v1 |
v1alpha2 |
|---|---|---|---|
| standard | config/crd/standard/gateway.networking.k8s.io_tcproutes.yaml |
served: true,storage: true |
served: false,storage: false,带
deprecationWarning |
| experimental | config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml |
served: true,storage: true |
served: true,storage: false |
这把发行说明里「both versions are served」钉到
experimental channel。Standard channel 在
v1.6.1 已经走到「v1alpha2 不再 served」。manifest 若仍写
apiVersion: gateway.networking.k8s.io/v1alpha2:
- standard:apiserver 拒绝该版本;Route
停止被服务(无法再经 v1alpha2
读写)。必须把清单改为
v1。 - experimental:v1alpha2 仍可 apply;存储是 v1。EG 仍只 watch v1——kube-apiserver 的转换让对象以 v1 形态出现在 informer 里,这才是 notes 说「v1alpha2 manifests continue to work」的前提。
UDPRoute 的 standard CRD 已同样核过 tag
v1.6.1
config/crd/standard/gateway.networking.k8s.io_udproutes.yaml:v1
为 served: true /
storage: true,v1alpha2 为
served: false / storage: false,与
TCPRoute 一致。
Experimental channel 还可能包含尚未毕业的资源,且允许更剧烈的破坏。平台若声明「只用 standard」,就不能靠 experimental 的双版本 served 来拖延改 yaml。
flowchart TD
subgraph crds["Gateway API CRDs"]
old["pre-v1.6 TCPRoute v1alpha2 only"]
std["v1.6.1 standard"]
exp["v1.6.1 experimental"]
end
watch["EG v1.9 watches gateway.networking.k8s.io/v1"]
old -->|"no v1 API"| skip["routes silently skipped"]
std -->|"v1 served"| ok["v1 manifests translated"]
std -->|"v1alpha2 served false"| noserve["manifests must be v1"]
exp -->|"v1 and v1alpha2 served"| conv["v1alpha2 apply; stored as v1"]
watch --> skip
watch --> ok
conv --> watch
图:CRD 版本与 channel 决定对象会不会进入 EG 的
v1 reconcile。静默跳过发生在「EG 已升、CRD 未到
v1.6」这一格。
四、失败表象:不是 HTTP 500,是无流量
L4 没有「路径写错返回 404」这种应用语义。TCPRoute
没进翻译,Envoy 侧对应 listener 要么不存在、要么挂着第 5
篇提到的空
EmptyCluster。客户端看到的是连接失败、超时、RST,或端口通了但协议无响应——取决于还有没有其它
listener 占用该端口。UDP
更不容易从应用日志里看出「网关没挂上」:报文直接消失,没有
TCP 握手可抓。
对照:
| 层 | HTTPRoute 常见表象 | TCPRoute 未翻译时 |
|---|---|---|
| 应用 | 404 / 500 / UNIMPLEMENTED | 没有 HTTP 状态码 |
| Route status | Accepted / ResolvedRefs / Overlap | 可能根本没有 v1 对象可写 status;旧 v1alpha2 对象停在 EG 视野外 |
| 控制器 | 通常仍 Running | 仍 Running;不保证因缺 v1 CRD 而 CrashLoop |
| 数据面 | RDS 缺 host | LDS 缺 TCP listener 或空 cluster |
所以值班口令不能是「看有没有 5xx」。应先问:这是不是
TCP/UDP 入口、CRD channel 与版本、EG 是否在 watch
v1。再用第 5 篇的 Infra/xDS
轴确认舰队与快照。
可操作的语义检查(本篇未跑,不粘贴输出):
- CRD:
TCPRoute/UDPRoute是否存在v1,standard 安装下v1alpha2是否已served: false。 - 清单:
apiVersion是否已改为gateway.networking.k8s.io/v1。仍写v1alpha2时,在 standard v1.6.1 上会在 apiserver 被拒,而不是「EG 帮你转换」。 - 存储:升级前是否已把旧对象迁到 v1 storage(否则将来删版本时硬挡)。
- 对照:同集群 HTTPRoute 仍工作,不能证明 L4 也被 reconcile。
v1.9 文档与 notes 没有承诺:「缺 v1.6
CRDs 时控制器会 Error 提醒」。相反,notes 用了 silently
skipped;egctl 对缺 CRD 也是
skip。把「会报错」写成保证,会让 runbook
在最需要信号的时候去等一条永不出现的事件。
TLSRoute 不在这次「升 v1 才 reconcile」的同一句话里。它已是 v1 Standard。L4 升级事故的主因是 TCP/UDP 与 CRD 频道,不是 TLSRoute 突然改组。
存储版本:若对象仍记着 v1alpha2 storage,将来 CRD 去掉该版本时升级会硬挡(Gateway API 指南中 GatewayClass 的历史例子)。v1.9 notes 要求在 v1alpha2 最终删除前完成迁移。这是未来删除的门,与「现在静默跳过」是两件事:现在跳过是因为 EG 不 watch 旧 API;将来挡升级是因为 etcd 里还躺着旧 storage version。
五、谱系、争论与开放问题
谱系
Kubernetes Service / NodePort / Ingress TCP 注解(不可移植)
→ Gateway API 实验频道 TCPRoute/UDPRoute v1alpha2
→ GEP-2644 / GEP-2645 补全毕业条件与一致性测试
→ v1.6 Standard + v1;CRD channel 决定 served 集合
→ EG v1.9:控制器只 reconcile v1;与 CRD 升级解耦则静默无流量
L4 路由的学术/规范问题不是匹配算法(几乎没有 L7 匹配),而是 API 生命周期:alpha 版本如何从 served 集合里拿掉,而不把现有对象留在控制器视野外。Gateway API 用 channel + 四步删版本回答;EG 用「watch v1 + 缺 CRD 则跳过」回答控制器存活。两者叠加,用户可见性最弱。
争论:静默跳过 vs 启动失败
A 侧(EG 实现选择):托管集群常只装 Gateway API 子集(GKE addon 缺 ListenerSet 等)。缺一种 CRD 就 crash-loop,爆炸半径是整个南北向。跳过让 HTTP 入口仍活。
B 侧(运维):TCP 入口消失且无 Error,平均定位时间上升。正确性要求至少有一条 Ready/Degraded 条件说「TCPRoute CRD 不是 v1」。
v1.9 发行说明站在 A 侧写事实。本系列要求 runbook 显式检查 CRD,而不是等待控制器良心发现。
开放问题
- 控制器能否在仍 Running 时对 Gateway 打出「L4 CRD 版本不匹配」条件,而不违反「缺 CRD 不崩溃」?可检验:装 v1.5 CRDs + EG v1.9,看 status 是否出现任何非静默信号(本文未跑)。
- Standard channel 已
served: false时,发行说明「both versions are served」容易误导只读 notes、不看 CRD YAML 的升级者。文档与 notes 的 channel 分叉如何收敛? - 与第 12 篇交接:storage-version migrator 是否应成为 EG 升级 chart 的硬依赖,而不是独立工具。
六、小结
- v1.6 起 TCPRoute/UDPRoute 为 Standard
v1;TLSRoute 已是 v1。EG v1.9 按v1reconcile。 - 未装 v1.6 CRDs:L4 路由静默跳过,不是 500,也不是保证有的控制器 Error。
- v1.6.1 standard
TCPRoute:
v1alpha2不再 served;清单必须改v1。experimental 仍可 served v1alpha2,存储为 v1。 - 失败表象是无流量 / 连不上;先查 CRD 版本与 channel,再查 IR/xDS。
下一篇进入 Policy:SecurityPolicy 等如何挂到 Route,以及
v1.9 mergeType 为何不能再挂 Gateway。
七、参考资料
规范 / 官方文档(A)
- Envoy Gateway v1.9.0 Release
Notes:TCP/UDP 经
v1reconcile;未装 v1.6 CRDs 则 silently skipped;storage version 迁到 v1。 - Gateway API v1.6 发布说明(Kubernetes Blog,2026-08-03):TCPRoute/UDPRoute 毕业、v1alpha2 弃用。
- Gateway API v1.6.0 GitHub Release:TCP/UDP 推荐使用
v1。 - TCPRoute、TLSRoute、CRD Management(四步删版本、storage 迁移)。
- EG v1.9 TCP Routing:机制示例;示例 apiVersion 仍为 v1alpha2,与 standard CRD 停服冲突,正文已标明。
源码 / CRD YAML(A)
kubernetes-sigs/gateway-apitag v1.6.1:config/crd/standard/gateway.networking.k8s.io_tcproutes.yaml与…_udproutes.yaml(二者v1alpha2均为served: false/storage: false);config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml(v1alpha2served: true,storage: false)。- GEP-2644 TCPRoute、GEP-2645 UDPRoute。
核心论文 / 规范传统
- Kubernetes API 版本与 CRD storage version 约定(官方 CRD 指南)——「改 CRD 的 storage 不会改写已存在对象」是静默事故的根。
- GEP-2644 / GEP-2645 —— 为何 L4 必须成为可移植 Route,而不是各实现私有 CRD。
实验 / 工具
- 实验台账:未跑。未在真实集群验证「装
v1.5 CRDs + EG v1.9」的 status 空窗;结论以发行说明与 CRD
served字段为准。 - 存储迁移工具:官方指南提及 kube-storage-version-migrator;本篇不提供未执行的 kubectl 输出。
站内对照
→ 上一篇:TLS / SDS · 系列目录 · 下一篇:Policy attachment
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【Envoy Gateway】南北向全景:缺口、五轴坐标系与 16 篇路线
相对 Gateway API 产品教程、Cilium 南北向边界、Envoy 消费侧与 istiod 翻译,钉清 Envoy Gateway v1.9.0 控制面内核缺口;定义附着/status、IR、xDS、Envoy 数据面、入口之后东西向五条轴,给出 16 篇阅读路线。
【Envoy Gateway】角色与附着:GatewayClass / Gateway / Route 各保证什么
钉清 Gateway API v1.6.1 角色分离在 Envoy Gateway v1.9.0 里的失败语义:parentRefs 与跨 namespace、ReferenceGrant 握手、Accepted 相对 Programmed / ResolvedRefs,以及 YAML 已 apply 仍无流量时该先查哪条条件。
【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 相同匹配的警告。