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

【Envoy Gateway】运维与升级:CRD 与 Helm 所有权分叉如何静默失败

文章导航

分类入口
kubernetesnetwork
标签入口
#envoy-gateway#helm#crd#validating-admission-policy#tcproute#xds#upgrade#v1.9.0

目录

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.yamlkubectl 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-versiongateway.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 无法接管:

对象名是 ValidatingAdmissionPolicy/safe-upgrades.gateway.networking.k8s.ioValidatingAdmissionPolicyBinding/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 升级节):

存储版本迁到 v1。在 v1alpha2 被最终删除之前,需要一次 storage-version 迁移。Kubernetes 多版本 API 的规则是:etcd 里存的是 storage versionserved 版本只影响能否用旧 apiVersion 读写。只升控制器、不 rewrite 对象,存储版本可以长期停在旧值,直到某次 CRD 停止 serving 旧版,kube-apiserver 读不出来。官方给出的语义是:把全部 TCPRoute / UDPRoute 读出再 kubectl replace 写回,迫使 API server 按新存储版本落盘。本篇不把该命令的输出当实验结果。这与「清单 apiVersion 写成 v1」是两步:清单不改,experimental channel 仍可能 serving;存储不迁,将来停 serving 时才爆。

另一处 Gateway API v1.6 破坏面:HTTPRouteSessionPersistence.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 断流。此时:

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 抢绑定,也尚未在官方升级节写成通用算法。


六、小结

  1. gateway-helm 管控制器;CRD 要单独按序升。提供方已管 Gateway API 时不要用 install.yaml 盲装。
  2. v1.9 把 safe-upgrades VAP 挪进 chart:单独装 CRD 时补 Helm owner;提供方管 VAP 时关掉渲染。
  3. TCP/UDP 未升到 v1.6 CRDs 则静默跳过;IdleTimeout 必须先从 HTTPRoute 删除。
  4. xDS 接收默认 32MiB,断流可以没有 NACK,代理停在 known-good。

下一篇用五轴把这些失败从症状翻译回层。


七、参考资料

规范 / 官方文档(A)

源码(A)

核心论文 / 规范

实验 / 工具

站内对照


上一篇:可观测 · 系列目录 · 下一篇:排障坐标系

读完这篇,下一步读什么

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


By .