最近两个星期,不少 Kubernetes 运维群都被一条公告刷了屏。消息标题很直接:Kubernetes 官方再次发布公告,强调需要立即迁移 Ingress NGINX。我第一反应也和大家一样:升级控制器而已,至于用“立即”这种词吗?但把公告完整读下来就明白了,这次确实不是例行催更,而是给出了明确的兼容性与安全窗口。很多团队的控制器版本已经停在大版本若干年,业务没出问题就没人动它,直到公告出现,才发现升级难度已经被自己拖高了。
这里说的 Ingress NGINX,指的是社区维护的 ingress-nginx 控制器,不是云厂商控制台里那个一键绑定的负载均衡器,也不是那种随节点自带的 Nginx 反向代理。它是 Kubernetes 集群里最常用的入口流量组件之一,负责把集群外部的 HTTP/HTTPS 流量按 Ingress 规则转发到后端 Service。正因为用的人多、暴露面大,官方对它的版本生命周期和安全补丁节奏一向盯得很紧。这篇文章我会把公告背后的逻辑、受影响范围、迁移方案选型、实操步骤和踩坑点一次性梳理清楚,排期的时候你可以直接拿来对照。
1. 公告核心解读:这次“立即迁移”到底在催什么
1.1 公告内容拆解:EOL、API 移除与升级顺序
把公告完整读下来,核心信息其实可以拆成三块。第一块是版本生命周期问题,官方明确某些旧版本已经进入 EOL 状态,维护团队不再为这些版本提供安全补丁。第二块是 Kubernetes API 兼容问题,旧版本控制器内部依赖的部分 Kubernetes 内置 API 在后续集群版本中会被移除,也就是说,不是控制器想不想兼容的问题,是集群升级后控制器可能直接废掉的问题。第三块是升级顺序问题,官方强调如果集群版本已经接近或超过旧控制器的兼容范围,必须先把控制器迁移到受支持版本,再考虑集群本身的升级。
这三块内容放在一起,翻译成大白话就是:你的集群和控制器之间有一个安全的版本交集,这个交集正在不断缩小。旧版本控制器在旧集群上可能看起来一切正常,但一旦集群跨版本升级,控制器就会成为那个最先崩掉的组件。而且由于它承担着所有外部流量入口,崩掉的影响范围不会是几个 Pod 重启那么简单,而是整个入口流量中断。
为什么是“再出公告”?因为维护团队通常不会上来就下最后通牒。一般会先发预公告,给出建议迁移时间线,然后随着时间推移逐渐收窄支持窗口。这次用“立即”这种措辞,说明已经过了“可以观望”的阶段。我自己的判断是,还在用两年前甚至更早版本的集群,基本都踩在公告点名的范围内;即使你用的是相对新的版本,也值得把这次公告当成一次配置体检的契机。
1.2 受影响场景自查:三类集群最危险
第一类是控制器版本长期停留在两个大版本之前的集群。这类集群通常遵循“能不动就不动”的运维原则,业务运行稳定,没有明显的故障驱动,于是升级计划永远排在最低优先级。但 Ingress 控制器恰恰是会持续接收安全补丁的组件,版本越旧,已知漏洞清单越长,暴露在公网环境下的风险就越高。
第二类是控制器版本和集群版本严重不匹配的集群。比如集群已经升到比较新的 Kubernetes 版本,但控制器还是早期版本,这种组合在升级当时可能勉强可用,但官方兼容矩阵里已经找不到对应的支持组合。一旦触发某些新版本对 API 的校验逻辑,控制器可能连启动都会失败,更别提正常转发流量。
第三类是高度依赖默认配置或大量自定义 Annotation 的存量业务集群。这类集群最大的问题不是迁移本身,而是迁移过程中很难发现自己到底用了哪些旧行为。很多 Annotation 在旧版本里被宽松处理,新版本校验增强后直接静默失效,业务侧只报“502”或者“404”,排查起来非常痛苦。后面我会专门展开讲这个坑。
1.3 公告措辞背后:为什么这次特别着急
官方在公告里反复强调“立即”,我理解有三层现实原因。
第一层是安全补丁断供。Ingress 组件是整个集群的流量入口,通常直接暴露在公网或对公网提供服务,一旦出现路由绕过、权限提升这类漏洞,影响范围是所有使用该版本的用户。EOL 版本不再发布补丁,意味着这些漏洞会一直存在,而且攻击者会专门盯着旧版本打。
第二层是依赖链断裂。Kubernetes 本身也在持续演进,部分 API 资源从 v1beta1 到 v1 再到废弃移除,是一个固定流程。旧版控制器内部还在使用一些已经进入移除流程的 API 字段,在集群升级后可能无法正常调用。这个风险不像安全漏洞那么直观,但爆发时往往是整个控制器 crash 级别的故障。
第三层是运维排期风险。官方一旦把“立即”这个措辞放出来,意味着接下来的维护资源只会向新版本集中,旧版本的已知问题不会再修,社区问题也不会再给出适配旧版本的解决方案。越晚动手,遇到问题时的参考文档越少,可用的 help 资源也越少。很多团队在公告出来后还觉得可以再等等,实际等到的是更加狭窄的迁移窗口和更高难度的故障排查。
2. 迁移目标选型:先别急着升级,想清楚迁到哪
2.1 方案A:升级到受支持的 ingress-nginx 新版本
对绝大多数存量集群来说,最稳妥的方案不是推翻重来,而是把现有 Ingress NGINX 控制器平滑升级到当前受支持的新版本。这个方案的最大优势是兼容性好:原有的 Ingress 资源定义方式不变,绝大多数 Annotation 依然有效,DNS 解析、证书配置、Service 暴露方式都可以保留原样。整个迁移的核心动作是替换控制器镜像和更新 Helm Chart 配置,而不是重新梳理业务流量模型。
从工作量角度来说,这可能是性价比最高的路线。你不需要改造应用,不需要改 Ingress 资源,也不需要给业务方重新讲一遍新配置模型的用法。你需要做的,是把当前使用的 Helm values 完整导出来,对照新版本默认值逐项比对,跑一遍测试环境验证,然后执行升级。整个过程如果提前准备充分,快的话一个变更窗口就能完成。
不过这里有个前提:你的配置不能太“脏”。如果之前手动改过 Controller 的 ConfigMap、Service 或 RBAC 资源,且这些改动没有沉淀到 Helm values 里,升级时这些改动很可能会被覆盖。后面我在实操部分会讲怎么把这类风险用备份和 diff 提前拦下来。
2.2 方案B:借机评估 Gateway API 路线
第二种路线是放弃 Ingress NGINX 控制器,转向 Gateway API 体系。这是 Kubernetes 社区在流量治理方面主推的方向,优点是配置模型更统一,支持跨 namespace 的策略引用,灰度发布、Header 修改、traffic split 等能力是原生模型的一部分,而不是靠一堆 Annotation 拼出来的。
但说实话,我不建议在公告压力下临时切换。Gateway API 虽然成熟度已经不错,但它和 Ingress 的配置模型差异非常大,迁移相当于把所有入口流量配置重写一遍。你需要重新规划 GatewayClass、Gateway、HTTPRoute 这些资源,还要额外部署一个支持 Gateway API 的控制器实现。业务侧也需要重新学习和适配,如果团队里大家刚把 Ingress 的 Annotation 记熟,这时候突然换模型,学习成本和故障风险都会集中爆发。
最合理的使用场景是:新业务、新集群,或者你已经计划做大规模流量治理改造,Gateway API 可以顺势落地。而老集群里跑着几十上百个 Ingress 资源,就不建议在这种节点上做模型级迁移。
2.3 决策维度:版本、团队和流量的三角关系
选型本质上就是在这三件事之间找平衡。版本维度看你的集群版本和控制器版本相差多大,相差越大,滚动升级的中间态越难处理。团队维度看有没有熟悉新旧配置差异的人,平时是不是有人专门负责集群流量组件。如果团队只是顺手维护,别给自己找大工程,老老实实选方案A。
流量维度看你的业务对路由能力的依赖有多深。如果只用到了 path 转发、Host 转发和 TLS 终止这几个基础能力,Ingress 和 Gateway API 对你的区别不大,选哪个都行,选升级更省事。如果涉及复杂的灰度、流量权重、请求头改写、跨服务故障转移,Gateway API 长期来看会更顺手,但你要为这个“长期顺手”付一笔短期的迁移学费。
我个人的建议是:先做版本升级,把兼容性和安全性问题解掉;如果后续业务复杂度确实到了 Ingress 注解体系撑不住的程度,再单独立项评估 Gateway API。两个目标混在一起做,最后往往哪个都做不利索。
| 对比维度 | 升级 ingress-nginx | 迁移 Gateway API |
|---|---|---|
| 配置兼容成本 | 低,Ingress 资源基本无需改动 | 高,需要重写为 HTTPRoute 等新资源 |
| 团队学习成本 | 低 | 中高 |
| 灰度能力 | 依赖 Annotation 组合 | 原生支持,配置更清晰 |
| 迁移风险 | 中等,集中在版本差异 | 较高,模型级重构 |
| 适用时机 | 公告催迁移时优先选择 | 新集群或流量治理大改造时选择 |
3. 实操迁移步骤与参数校验
3.1 迁移前盘点:三条命令摸清家底
动手之前别急着升级,先把你集群里的 Ingress 控制器现状摸清楚。下面这几条命令是我每次做这类迁移前固定会跑的,你可以直接复制使用。
# 查看当前集群里的 IngressClass,并确认 controller 字段指向谁 kubectl get ingressclasses -o wide # 查看 ingress-nginx 命名空间下控制器的运行状态和镜像版本 kubectl get pods -n ingress-nginx -o wide | grep controller # 如果控制器是通过 Helm 部署的,导出当前 release 的 values helm list -n ingress-nginx helm get values ingress-nginx -n ingress-nginx > current-values.yaml第一条命令关注的是 IngressClass 的 controller 字段是否和 ingress-nginx 的期望值一致。很多团队在迁移过程中遇到过一个问题:IngressClass 里写的是某个云厂商的 controller 名称,但实际业务流量走的是 ingress-nginx,两边不匹配的时候,Ingress 资源创建后永远处于等待状态。
第二条命令看 Pod 的镜像 tag 就能快速判断版本新旧。如果你发现当前版本已经落后官方稳定版一到两个大版本,那基本就是这次公告直接覆盖的对象。第三条命令则是为了导出现有配置,升级时 diff 会非常有用。
如果你不是用 Helm 部署的,也没有现成的 release 记录,就需要手动导出控制器相关的 Deployment、Service、ConfigMap、ServiceAccount 和 ClusterRole。这里不展开 YAML 细节,但建议至少把kubectl get deployment -n ingress-nginx -o yaml的结果存一份,后面做回滚和对比都用得上。
3.2 备份与回滚设计:留好后路再动手
升级 Ingress 控制器最大的忌讳是“没备份就升级”。虽然 Helm 本身支持 rollback,但如果你过程中手动改过资源,或者升级时用了不完整的 values 文件,回滚可能不是简单的helm rollback就能解决。我的习惯是把状态备份做成三道保险。
第一道是配置备份。导出集群当前所有 Ingress 资源、控制器 ConfigMap、以及相关的 Secret 到本地文件。命令如下:
kubectl get ingress -A -o yaml > ingress-backup.yaml kubectl -n ingress-nginx get cm ingress-nginx-controller -o yaml > controller-cm-backup.yaml kubectl -n ingress-nginx get secret -o yaml > controller-secret-backup.yaml注意,如果集群里有自管的 ingress-nginx-token 或 Webhook 证书 Secret,升级过程可能涉及重建,这部分一定要提前确认,否则升级后 Webhook 会由于证书不匹配而反复报错。
第二道是 Helm 版本记录。在升级前用helm list -n ingress-nginx查看当前 revision 号,把它记下来。这样即使升级后不理想,也可以通过helm rollback ingress-nginx <上一版本号> -n ingress-nginx快速回到升级前状态。
第三道是测试环境验证。如果公司有预发或测试集群,先在那边跑一遍完整升级流程,把差异和坑点都处理完,再在生产环境执行。这一步听起来像废话,但很多团队在控制器升级时确实会跳过,理由是“业务流量又没有变化,升个镜像而已”。实际触发过的告警会告诉你,Inert 控制器升级引发的问题从来不只在控制器本身。
3.3 执行升级:Helm 升级与滚动更新细节
在正式升级前,先更新 Helm 仓库并查看目标版本的默认 values,了解新版本默认行为的变更点。这一步能预先发现大部分兼容性问题。
helm repo update helm show values ingress-nginx/ingress-nginx --version <目标版本> > new-default-values.yaml然后用现网导出的 current-values.yaml 作为基准,和新版本默认值做一次 diff,重点看几个高风险配置项:controller 镜像版本、admissionWebhook 开关、config 里的 proxy 相关参数、metrics 端口是否变化。这几个配置对流量转发行为影响最大。
确认没有意外差异后,执行升级:
helm upgrade ingress-nginx ingress-nginx/ingress-nginx \ --version <目标版本> \ -n ingress-nginx \ -f current-values.yaml \ --history-max 5升级过程会触发控制器的滚动更新。这时候要观察的是 Pod 能不能顺利 Ready,而不是急着刷页面看流量是否正常。用下面这条命令跟踪状态:
kubectl rollout status deployment/ingress-nginx-controller -n ingress-nginx如果控制器是通过 DaemonSet 部署的,命令换成kubectl rollout status daemonset/ingress-nginx-controller -n ingress-nginx。
滚动更新完成后,检查 Pod 是否都处于 Running 状态,然后看 controller 日志里有没有明显的报错。我在实际迁移中就遇到过一种情况:Pod 显示 Running,但 controller 一直往日志里打 “Error building handler” 之类的错误,入口流量实际上全部 502。所以 Pod Ready 只是一个最低条件,日志检查必须跟上。
如果升级后发现问题,第一时间回滚,不要尝试在故障状态下修配置。回滚命令如下:
helm rollback ingress-nginx <上一版本号> -n ingress-nginx3.4 Annotation 与 IngressClass 兼容检查
升级完成后,最容易被忽略的是存量 Ingress 资源里的 Annotation 是否在新版本下仍然生效。ingress-nginx 的 Annotation 体系非常庞大,新版本会在校验逻辑、参数语义、默认值上做调整。我自己的经验是把所有在用 Annotation 整理成一张清单,逐个核对两个关键点:是否还支持、语义是否有变化。
比较典型的几类检查项包括:
- 与代理超时相关的配置,比如
nginx.ingress.kubernetes.io/proxy-connect-timeout、proxy-read-timeout,这些通常变化不大,但数值单位或边界值可能收紧。 - 重写规则相关,比如
rewrite-target,新版本对正则表达式的转义要求更严格,旧的写法可能在新版本里静默失效。 - 灰度相关,比如
canary、canary-weight、canary-by-header这组 Annotation,新版本对权重和策略的优先级有更明确的定义,如果你之前依赖权重灰度,需要重新确认配置。
IngressClass 方面要确认 Ingress 资源中是否显式声明了ingressClassName,如果声明了,要确保这个 Class 对应的 controller 名称与新版本控制器的 controller 字段一致。不一致时,Ingress 会一直处于 pending 状态,流量完全不进来,排查时还容易误判成网络问题。
我习惯在升级前先用测试域名建一个最小 Ingress,指向一个固定返回当前版本信息的测试服务,然后在升级后逐个检查基础转发、TLS、重写和超时配置。这个小动作成本很低,但能帮你第一时间定位 Annotation 兼容性问题,而不是等业务侧反馈“某个接口突然 404”。
3.5 验证清单:从流量到 TLS 的一次完整体检
控制器滚动更新完成后,不能只看 Pod Ready 就宣布成功。我通常会按下面的顺序做一轮完整验证。
第一步检查后端服务连通性,确认 Ingress 到 Service 到 Pod 的链路没有断裂。可以用一条简单的请求命令验证:
curl -H "Host: your-domain.example" https://<INGRESS_NODE_IP>/healthz -k -v第二步检查 TLS 证书是否正确加载。如果你用的是 ingress-nginx 自动管理的证书 Secret,需要确认 Secret 在集群中依然存在,且 Ingress 中引用的 Secret 名称没有变化。证书加载错误时,curl -v里会直接看到证书相关的报错信息。
第三步检查 Webhook 是否正常工作。升级后ValidatingWebhookConfiguration里的 service 指向需要和新控制器的 service 名称、端口匹配。验证方式很简单,随便创建一个带非法字段的 Ingress,看是否能被拦截;如果完全不拦截,说明 Webhook 配置可能失效了。
kubectl get validatingwebhookconfigurations -o wide | grep ingress-nginx第四步是检查流量转发日志。查看 controller 日志历史,确认升级前后请求记录连续,没有出现大段时间盲区。同时观察错误状态码分布,如果突然出现一批 502、404,说明上面的某个检查项漏掉了一环。
4. 常见问题与坑点实录
4.1 Webhook 升级后反复报错
这是控制器升级后最常见的坑之一。现象是:控制器 Pod 正常运行,但创建或更新 Ingress 资源时,API Server 会返回 webhook 调用失败,通常是因为ValidatingWebhookConfiguration里的 service 指向的还是旧的 service 名称,或者证书不匹配。
新版本控制器如果调整了 service 名称,旧 webhook 配置就会失效。排查时先看 webhook 配置里指向的 service 是否存在、端口是否和新版本一致,然后检查 Secret 中保存的证书是否和 webhook 配置里的 caBundle 对应。证书不匹配时,把新的 caBundle 更新进去即可。
踩了这个坑之后的经验是:升级前先检查 webhook 配置,链路完整再动控制器。别等升级完再回头看,那时问题已经扩散到所有 Ingress 变更请求都失败了。
4.2 旧 Annotation 静默失效导致路由与重写异常
升级后业务方反馈某些接口 404 或返回异常,但 Ingress 资源本身没有变化,这是最让人头疼的情况。多数时候问题出在 Annotation 上,而且旧版本不会报任何警告,新版本把旧的无效 Annotation 直接忽略,导致路由规则和后端转发行为发生了变化。
比如rewrite-target里面的正则路径在旧版本里宽匹配了多个服务,新版本校验严格以后不再匹配,行为就变了。这类问题很难通过日志发现,因为控制器日志里往往干净得像个新装环境。我的处理方式是提前做一次 Annotation 全量扫描,把每一条 Annotation 都和新版本文档核对一遍,尤其是路径重写和灰度相关的高频配置。
这个方法很笨,但救过我好几次。遇到查不到头绪的 404 时,我会把对应 Ingress 的完整 YAML 拉出来,逐行看 annotation,而不是先怀疑后端服务。
4.3 金丝雀发布流量切分异常
版本升级后金丝雀异常也很常见。症状是设置了canary-weight的服务的灰度流量割接不正常,要么全部打到了旧版本,要么灰度比例完全错乱。
原因通常是新旧版本对灰度 Annotation 的优先级定义有差异。新版本对canary-by-header、canary-by-cookie、canary-weight同时存在时的处理顺序更严格,如果之前的配置同时声明了多个条件,新版本可能按照不同的规则决定流量去向。
处理方法是先砍掉大部分灰度配置,只保留一个条件验证,确认逻辑正确后再逐步加回。别指望一次把全部灰度策略都迁移过来,这种复杂场景需要分步验证。
4.4 快速排查参考表
| 故障现象 | 常见原因 | 优先排查命令 | 处理建议 |
|---|---|---|---|
| Pod 一直 CrashLoopBackOff | 镜像版本与集群版本不兼容 | kubectl logs -n ingress-nginx | 检查兼容矩阵,回退到受支持版本 |
| 创建 Ingress 失败,webhook 报错 | Webhook 配置指向旧 service | kubectl get validatingwebhookconfigurations -o wide | 更新 service 指向和 caBundle |
| 升级后大量 502 | ConfigMap 中 proxy 参数异常 | kubectl -n ingress-nginx logs <controller-pod> | 检查后端服务连通性和超时配置 |
| 部分路由 404 | Annotation 被静默忽略 | kubectl get ingress -A -o yaml | 逐项核对旧 annotation 是否存在 |
| 灰度流量比例异常 | 多个 canary 条件冲突 | kubectl describe ingress <name> | 先保留单一条件,验证后再加回 |
5. 最后说几句实操体会
控制器这类流量组件的升级,最忌讳的就是把它当成普通镜像升级来排期。它和业务服务不同,业务服务升级后最多影响自己这条链路,而 Ingress 控制器升级影响的是集群所有入口流量。我在实际踩坑后养成了一个习惯:无论升级范围多小,都把 Webhook 配置、证书 Secret、IngressClass 指向这三件事放在变更前检查清单里,缺一项都不动工。
另外,升级顺序也值得反复强调:优先升控制器,再升集群。有不少团队反过来操作,先升集群后发现入口控制器崩了,流量中断后只能紧急回滚集群版本,场面非常混乱。按公告的节奏来,该给的提醒官方早就给了,真正决定代价大小的,是咱们自己动手的速度。
这次迁移之后,我也建议你把控制器的版本检查加入定期巡检脚本。每个季度花几分钟看一次镜像 tag 和官方稳定版的差距,至少不会再被突然的公告打得措手不及。动一次手,把下一次的坑也提前填上,这笔账怎么算都划算。