Helm 升了 gateway-helm,TCPRoute
却从数据面消失;Flux 把 ValidatingAdmissionPolicy 当成 CRD
抢所有权;GKE 已经管着 Gateway API CRD,却又 apply 了一份
install.yaml。这三类事故的共同结构是:控制器版本、CRD
存储版本、admission 策略、xDS
接收上限不在同一个所有权域里,失败可以没有报错事件,也可以没有
xds_nack_total。
本文只钉升级语义:两套 chart 各拥有什么;v1.9 safe-upgrades VAP 挪到 chart 之后必须做的两件事;TCP/UDP 存储版本迁移(细节回指 第 8 篇);以及默认 32MiB 的 xDS 接收上限如何避免大集群把代理钉死在旧快照。不写各云控制台点击步骤。
本文是「Envoy Gateway / Gateway API」系列第 12 篇(共 16 篇)。→ 系列目录
篇目 核心内容 第 11 篇 · 可观测 xdsNACKTotal、client sampling 0%第 12 篇 · 运维与升级 Helm/CRD 分装、VAP 挪 chart、存储版本、32MiB 第 13 篇 · 排障坐标系 未 Accepted / IR 空 / NACK / 旧快照
版本锚定:Envoy Gateway v1.9.0(源码 tag v1.9.0,2026-08-14);捆绑 Gateway API v1.6.1(系列钉)。对齐 v1.9 Install with Helm / Install with Kubernetes YAML 与 v1.9.0 Release Notes 的 Upgrade / Breaking。命令只写官方给出的语义,不粘贴未执行输出。实验台账:未跑。
一、gateway-helm vs gateway-crds-helm
官方提供两条安装面,所有权不同:
| 工件 | 装什么 | 升级时 Helm 会不会自动换 CRD |
|---|---|---|
oci://docker.io/envoyproxy/gateway-helm
--version v1.9.0 |
控制器 Deployment、RBAC、默认 EnvoyGateway 配置;默认还通过 chart 依赖装 Gateway API + Envoy Gateway CRDs | 不会更新 chart /crds
目录里那种 CRD。官方升级节写明:先手动升
CRD,再升控制器,否则新控制器可能找不到新版本、无法
reconcile |
oci://docker.io/envoyproxy/gateway-crds-helm
--version v1.9.0 |
只渲染 CRD:可选 Gateway API
channel(standard /
experimental)、是否包含 Envoy Gateway
CRDs |
用 helm template +
kubectl apply --server-side,因为 Helm 对
templates/ 里超大 CRD 有已知限制 |
默认
helm install eg oci://docker.io/envoyproxy/gateway-helm --version v1.9.0
会同时装 Gateway API CRD。若集群的
Kubernetes 提供方已经在管 Gateway API CRD,官方 Helm 与 YAML
安装页都写成硬约束:先核提供方的 bundle-version 与 channel
是否与本版本兼容矩阵及你要用的资源一致;兼容则 只装
Envoy Gateway CRDs,主 chart 加
--set crds.enabled=false。v1.9 新增
crds.enabled,用来关掉 gateway-helm 对 crds
子chart 的依赖。
install.yaml(kubectl apply --server-side -f https://github.com/envoyproxy/gateway/releases/download/v1.9.0/install.yaml)同样内含
Gateway API CRD 和 Envoy Gateway
CRD。提供方已管兼容的 Gateway API CRD 时,不要盲目用
install.yaml,改走「提供方 CRD +
gateway-crds-helm 只开 envoyGateway + gateway-helm
crds.enabled=false」。不兼容时也不要两套 CRD
混装;先换到兼容的 Gateway API 安装方式,再装控制器。
核验已装 Gateway API 版本与 channel,官方给出的是读 CRD
注解 gateway.networking.k8s.io/bundle-version
与
gateway.networking.k8s.io/channel。本篇未跑该命令,不伪造输出。
顺序不变量:CRD 先于控制器。Release Notes 与 Helm 升级节重复同一句:先升到 Gateway API v1.6,再升 Envoy Gateway v1.9.0,否则 TCP/UDP 会被静默跳过(第三节)。
GitOps 里「谁是 CRD 的 owner」必须写进 runbook。Helm
的经典缺口是:crds/ 目录对象在
helm upgrade 时不更新。Flux 若把 admission
策略也标成 CRD,会与下一节的迁移撞车。兼容矩阵(官方 Install
页链到 Version Compatibility Matrix)约束的是
Kubernetes 小版本 × Gateway API × Envoy
数据面镜像;本系列钉的是 EG v1.9.0 / GAPI v1.6.1 /
Envoy distroless-v1.39.0 / Kubernetes
1.33–1.36。提供方托管的 Gateway API 若 channel 是
standard、版本低于 v1.6,则 TCP/UDP 门在第 8
篇已经失败,Helm 再「装成功」也救不回来。
v1.9 安全更新还改变了 chart
默认:控制器容器只读根文件系统;certgen Job 带收紧的
pod-level
securityContext。这不是功能开关,但会让「往容器里写临时文件」的旧运维脚本失败。升级检查单应把它当成行为变化,而不是忽略的
hardening 脚注。
二、v1.9 safe-upgrades VAP 挪到 chart
Gateway API 的 safe-upgrades
ValidatingAdmissionPolicy 及其 Binding,在
v1.9.0 移出 CRD bundle,进入 gateway-helm 的 chart
templates。动机写在 Release Notes:避免 Flux
这类工具把它们 当成 CRD
处理。它们现在是普通 templated
资源,--skip-crds
不会跳过它们。
升级时有两种必须动手的情况:
(1)CRD 单独安装(例如
gateway-crds-helm,或
helm install --skip-crds)。safe-upgrades VAP
与 Binding 改由 gateway-helm 渲染。升级前要给已有对象补上
Helm 所有权元数据,否则 Helm 无法接管:
- annotation:
meta.helm.sh/release-name、meta.helm.sh/release-namespace - label:
app.kubernetes.io/managed-by=Helm
对象名是
ValidatingAdmissionPolicy/safe-upgrades.gateway.networking.k8s.io
与
ValidatingAdmissionPolicyBinding/safe-upgrades.gateway.networking.k8s.io。官方指向
Install 文档的 Installing CRDs separately。
(2)Gateway API CRD 与 safe-upgrade
策略由云提供方或其他 chart 外机制管理。 因为
--skip-crds 跳不过
templates,必须显式关掉渲染:crds.gatewayAPI.safeUpgradePolicy.enabled=false。官方指向
Clusters with compatible provider-managed Gateway API
CRDs。Helm 安装页在该场景的示例命令里强调:提供方已管
CRD 时不要再让本 chart 画一份 VAP。
漏做(1)的典型表象是 helm upgrade
报资源已存在且无 Helm 所有权,VAP
停在旧内容。漏做(2)的典型表象是 chart 与提供方各管一份同名
VAP,admission
行为以集群里实际生效的那份为准——可能不是你以为的那份。本篇
未跑 Helm,不粘贴错误字符串;升级检查单以
Release Notes 的两条动作的有无为准。
不要把 VAP 挪移理解成「safe-upgrades 取消了」。策略意图仍是:阻止不兼容的 Gateway API CRD 升级毁掉已有对象。变的是 Helm/Flux 眼里它还算不算 CRD。CRD 有独立的 apply 顺序与所有权注解;普通 template 则跟 release 走。Flux 若继续用「CRD 清单」去 apply 这两份对象,会与 chart template 双写。v1.9 把它们从 bundle 拿出来,就是为了结束这种双写。
同版本还修过 install.yaml 生成重复
VAP/Binding、导致 kustomize build
因重复资源失败。若仍用 YAML 安装路径,应以 v1.9.0
发布工件为准,不要沿用旧的拼接清单。另:v1.9 修了在缺少
ListenerSet / GRPCRoute / TLSRoute / BackendTLSPolicy 等 CRD
的提供方打包(如部分 GKE addon、OpenShift Ingress Operator
集)上控制器启动即 crash-loop 的问题——watch 改为「CRD
存在才注册」。升级后若某类对象从未出现,先确认提供方 bundle
有没有该 CRD,再查翻译;这与 TCPRoute
静默跳过同类,但是启动路径而不是 reconcile 路径。
三、TCP/UDP 存储版本迁移
机制正文在 第 8 篇。升级篇只保留门禁,避免把同一篇文章写两遍。
v1.9.0 改为经 gateway.networking.k8s.io/v1
reconcile TCPRoute /
UDPRoute。必须先把 Gateway API CRDs 升到
v1.6。未装 v1.6 CRDs 时,TCP/UDP 路由被
静默跳过——不是控制器 500,也不是必然的
NACK,而是翻译输入集里没有这些对象。egctl x status
在缺 CRD 时会跳过(v1.9 修复),更容易让人以为「集群里没有
L4 路由」。
channel 差异(官方 Install 升级节):
- standard:v1.6 不再 serving
v1alpha2。升 CRD 之前必须把清单改成apiVersion: gateway.networking.k8s.io/v1,否则对象停止 serving,流量掉光。 - experimental:升 CRD 后
v1与v1alpha2仍同时 serving,旧清单暂时还能用;仍建议改v1。
存储版本迁到 v1。在 v1alpha2
被最终删除之前,需要一次 storage-version 迁移。Kubernetes
多版本 API 的规则是:etcd 里存的是 storage
version;served 版本只影响能否用旧
apiVersion 读写。只升控制器、不 rewrite
对象,存储版本可以长期停在旧值,直到某次 CRD 停止 serving
旧版,kube-apiserver 读不出来。官方给出的语义是:把全部
TCPRoute / UDPRoute 读出再 kubectl replace
写回,迫使 API server
按新存储版本落盘。本篇不把该命令的输出当实验结果。这与「清单
apiVersion 写成 v1」是两步:清单不改,experimental channel
仍可能 serving;存储不迁,将来停 serving 时才爆。
另一处 Gateway API v1.6
破坏面:HTTPRoute 的
SessionPersistence.IdleTimeout 已删除。Envoy
Gateway 不再校验或拒绝仍写该字段的路由;升 Gateway
API CRDs 到 v1.6 之前必须从清单里删掉
sessionPersistence.idleTimeout,否则 CRD
升级会卡在非法字段。这与 TCPRoute
静默跳过不同:一个是存储/serving 版本,一个是字段从 spec
消失。SessionPersistence 其余字段仍在;不要把「删
IdleTimeout」理解成「会话亲和被拿掉」。
四、xDS 接收消息上限(默认 32MiB)与大集群 NACK
第 10
篇 已引入
xdsServer.maxReceiveMessageSize。升级篇要钉的是它与
NACK / 旧快照 的关系。
Delta xDS
在流(重新)连接时,代理会把当前持有的每种资源的名字与版本回声给控制面。资源很多时,上行
DiscoveryRequest 可能超过旧默认 4MiB。gRPC 以
received message larger than max
断流。此时:
- 代理未必发出带
ErrorDetail的协议 NACK——可能根本建不成新流 xds_nack_total因此 可以一直是零- 代理停在上一份 known-good,和新配置「没翻译」看起来一样
v1.9.0 把默认上限改为 32MiB,并允许在
EnvoyGateway API 里改
xdsServer.maxReceiveMessageSize。该限制
只作用于控制器收到的消息,不限制下发给
Envoy
的配置体积。下发过大仍可能在数据面失败,那是另一条轴。
升级检查应把「大集群重连」从「看 NACK 面板」扩成「看控制器日志里的 message size 错误 + 流是否立刻断开」。有集群再核;本篇不编日志。
把 32MiB 理解成「配置再大也能推下去」是错的:上限管的是
上行 回声,不是下行快照。下行仍然受 Envoy
与 gRPC 其他限制约束。若集群在 4MiB 时代已经靠拆 Gateway /
关 mergeGateways 活着,升到 32MiB
只是把悬崖移远,不是容量规划的终点。EndpointSliceIndex
默认开启会在 EndpointSlice
很多时抬高控制器内存,和消息上限是两条容量轴:一个
OOM,一个断流,都可以让数据面停在旧快照。
同版本默认打开的 EndpointSliceIndex
会增加控制器内存,属于升级容量规划,不是功能开关彩蛋。生产应在升
v1.9 前复核 memory limit,或显式 disable。
flowchart TD
pre["Before upgrade"] --> crd{"Gateway API CRDs v1.6?"}
crd -->|"no"| skip["TCP/UDP silently skipped"]
crd -->|"yes"| idle{"idleTimeout still in HTTPRoute?"}
idle -->|"yes"| reject["CRD apply rejects field"]
idle -->|"no"| helm{"Who owns VAP?"}
helm -->|"separate CRDs"| meta["Add Helm ownership metadata"]
helm -->|"provider managed"| off["safeUpgradePolicy.enabled false"]
helm -->|"this chart owns CRDs"| up["Upgrade CRDs then controller"]
meta --> up
off --> up
up --> size["Review xDS 32MiB and EG memory"]
图:语义级升级检查单。不包含命令输出。节点为英文。
| 检查项 | 不做的失败表象 | 证据落在哪一轴 |
|---|---|---|
Gateway API CRDs ≥ v1.6(TCP/UDP v1) |
L4 路由消失,无 500 | 翻译输入集(第 8 篇) |
清单去掉
sessionPersistence.idleTimeout |
升 CRD 被拒 | API serving |
| 先 CRD 后控制器 | 新字段不被 reconcile | 控制面版本门 |
| 单独装 CRD 时给 VAP 补 Helm owner | helm upgrade 无法接管 VAP |
admission 所有权 |
| 提供方管 VAP 时关掉 chart 渲染 | 双份 VAP / 意外策略 | admission |
提供方已管 Gateway API 时不用
install.yaml |
抢 CRD 所有权、channel 被覆盖 | CRD 所有权 |
| 复核 32MiB 与控制器内存 | 重连失败、旧快照、NACK 仍为零 | xDS 传输 |
| GatewayNamespaceMode 集群吃下 xDS 认证修复 | 未认证客户端曾能连 xDS | 安全(第 10 篇) |
| Lua / Patch 扩展门与旧 xDS 名 | 策略写了无副作用 | 第 9 篇,不是 Helm 失败 |
升级不是单一
helm upgrade。CRD、VAP、控制器、数据面镜像、xDS
传输上限分属不同所有权;漏任何一层都可以在没有 NACK
的情况下改变流量。检查单要按层勾,不要按「chart 版本号已经是
v1.9.0」勾。
五、谱系、争论与开放问题
Kubernetes 多版本 API 与存储版本(必须 migrate 才能停 serving 旧版)
→ Helm:CRD 生命周期与普通 template 分家(upgrade 不碰 crds/)
→ GitOps:Flux 把「像 CRD 的对象」按 CRD 语义处理
→ Gateway API safe-upgrades VAP:防止不兼容 CRD 升级
→ v1.9:VAP 从 CRD bundle 挪到 chart template,避开「被当成 CRD」
ValidatingAdmissionPolicy 是 Kubernetes 用声明式策略做 admission 的核心 API(1.30 起逐步稳定)。Gateway API 用它保护「升级 CRD 时不要破坏已有 Gateway 对象」。Envoy Gateway v1.9 不改变 VAP 的校验意图,只改变 包装与所有权:CRD bundle 里的对象会被部分 GitOps 工具特殊对待。这是包装问题,不是网关算法问题,但足以让升级在 admission 层静默偏离。
Helm 不升级 crds/ 是长期工程争议:避免
helm upgrade 毁掉集群级 schema,也造成「控制器
chart 版本看起来已经是 1.9、CRD 仍是 1.5」。Envoy Gateway
用第二张 gateway-crds-helm
把选择权交回操作者,但 没有
消除「两步必须按序」。静默跳过 TCPRoute
把这个缺口从「报错」改成「无流量」,比硬失败更难发现。
争论:把 schema
与控制器绑在同一工件(一次升级、失败域清晰)vs 分装(提供方
/ 多控制器共享 Gateway API CRD)。Gateway API
的设计假设是集群级 CRD 被共享;Envoy Gateway
必须在「不要覆盖提供方 CRD」和「自己的 v1.9 控制器需要 v1.6
serving」之间同时成立。install.yaml
图省事,却在提供方已管 CRD
的集群上成为抢所有权的捷径——官方因此写了禁止句。Helm
社区长期接受「upgrade 不碰 CRD」以降低误删 schema
的风险,代价就是本篇的静默 skew:chart
appVersion 已经是 1.9,存储版本仍可能是
v1alpha2。
开放问题:version skew 能否变成控制面一等公民的 status 条件(「我需要的 TCPRoute v1 CRD 不存在」),而不是静默 skip?现在要靠 runbook 与第 8 篇的版本门。第 13 篇可以把「无 L4 流量且 NACK 为零」映射到输入集,但无法从指标上区分「CRD 未装」与「集群里本来就没有 TCPRoute」。另一问:32MiB 默认是否只是把悬崖移到更大的集群——没有按 node 的 request size 直方图时,下一次断流仍可能没有 NACK。VAP 所有权迁到 chart 之后,多 release 共用同一集群级 VAP 名字如何避免第二个 Envoy Gateway Helm release 抢绑定,也尚未在官方升级节写成通用算法。
六、小结
gateway-helm管控制器;CRD 要单独按序升。提供方已管 Gateway API 时不要用install.yaml盲装。- v1.9 把 safe-upgrades VAP 挪进 chart:单独装 CRD 时补 Helm owner;提供方管 VAP 时关掉渲染。
- TCP/UDP 未升到 v1.6 CRDs
则静默跳过;
IdleTimeout必须先从 HTTPRoute 删除。 - xDS 接收默认 32MiB,断流可以没有 NACK,代理停在 known-good。
下一篇用五轴把这些失败从症状翻译回层。
七、参考资料
规范 / 官方文档(A)
- Envoy Gateway v1.9.0 Release Notes(VAP
挪 chart 的两种升级动作;TCP/UDP
v1静默跳过;IdleTimeout删除;xdsServer.maxReceiveMessageSize4MiB→32MiB;EndpointSliceIndex)。 - Envoy Gateway v1.9 Install with
Helm(
crds.enabled、gateway-crds-helm、提供方已管 CRD、升级顺序、storage-version 迁移命令语义)。 - Envoy Gateway v1.9 Install with Kubernetes
YAML(不要对提供方已管 CRD 的集群盲目使用
install.yaml)。 - Gateway API v1.6:TCPRoute/UDPRoute
v1;HTTPRoute SessionPersistence 字段变更。
源码(A)
- GitHub tag v1.9.0
发布工件:
install.yaml、gateway-helm/gateway-crds-helmchart 版本v1.9.0。仓库内charts/gateway-helm/Chart.yaml在 tag 上仍为开发占位v0.0.0-latest,以发布的 OCI chart 版本为准。
核心论文 / 规范
- Kubernetes API 多版本与存储版本(官方 API 变更文档)——必须 migrate 才能停 serving 旧版。
- Helm CRD 生命周期限制(Helm 文档:
crds/在 upgrade 时不更新)——本篇所有权分叉的工程前提。
实验 / 工具
- 无
helm upgrade/kubectl replace输出;检查单为语义级。
站内对照
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【Envoy Gateway】南北向全景:缺口、五轴坐标系与 16 篇路线
相对 Gateway API 产品教程、Cilium 南北向边界、Envoy 消费侧与 istiod 翻译,钉清 Envoy Gateway v1.9.0 控制面内核缺口;定义附着/status、IR、xDS、Envoy 数据面、入口之后东西向五条轴,给出 16 篇阅读路线。
【Envoy Gateway】xDS Translator 与 Infra:推送不等于在服务
把 XdsIR 钉到 LDS/RDS/CDS/EDS/SDS,把 InfraIR 钉到 Envoy 舰队;说明 go-control-plane Delta xDS 只保证快照送达,不能代替 Envoy warming 与 ACK。
【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。
【Envoy Gateway】EnvoyProxy 与基础设施:谁管数据面生命周期,GatewayNamespaceMode 与 remote infra
钉清 EnvoyProxy CR 与 Infra Manager 对数据面 Pod 的所有权;说明 GatewayNamespaceMode 的 xDS 认证修复、v1.9 remote infrastructure provider 的责任面,以及 mergeBackends 仍是默认关闭的 experimental 能力。