本文介绍如何使用 ingress-nginx 将工作负载从应用程序路由加载项 迁移到 应用程序路由网关 API 实现,该实现通过 Kubernetes 网关 API 为流量提供服务。 这两个数据平面可以在同一 AKS 群集上并行运行,从而可以通过使用 DNS 切换客户端流量来执行零停机切换,而不是进行原地替换。
迁移原因
Caution
Kubernetes SIG Network和安全响应委员会宣布即将停用Ingress NGINX项目, 维护将于2026年3月结束。 目前,使用 NGINX 的应用程序路由加载项的 AKS 群集无需立即执行任何操作。 Microsoft将为应用程序路由加载项的NGINX Ingress资源的重要安全补丁提供正式支持,支持截止至2026年11月。
AKS 正在迁移到
- 应用程序路由扩展用户:生产工作负荷在 2026 年 11 月之前仍受全面支持。 迁移到 应用程序路由网关 API 实现,以获得基于网关 API 的入口流量管理体验。
-
OSS NGINX 用户 有多个选项:
- 迁移到 使用 NGINX 的应用程序路由加载项 ,在规划长期网关 API 迁移的同时,从官方支持(截至2026年11月)中受益。
- 迁移到 应用程序路由网关 API 实现,以获得基于网关 API 的入口流量管理体验。
- 服务网格用户:如果计划采用服务网格,请考虑 基于 Istio 的服务网格加载项。 今天使用 Istio 入口,计划在正式发布为 GA 时迁移到 Istio 网关 API 支持。
对于基于 ingress-nginx 的应用程序路由外接程序,由 Microsoft 提供支持的安全补丁将于 2026 年 11 月终止。 AKS 上受支持的后继方案是应用程序路由 Gateway API 实现,它会部署一个轻量级的 Istio 控制平面,该控制平面仅协调属于 approuting-istioGatewayClass 的 Gateway API 资源,不支持 Istio CRD 或数据平面边车注入。
迁移的工作原理
ingress-nginx 加载项以及 Gateway API 实现的部署和服务各自都会获得自己的 Azure 负载均衡器 前端 IP。 在其解析得到的主机名指向 Gateway API 代理之前,客户端会一直访问 ingress-nginx 的 IP。
没有可原地将 ingress-nginx 负载均衡器 IP 替换为 Gateway API 负载均衡器 IP 的标志。 策略是并行运行这两个数据平面,验证新路径,然后将客户端移动到新的 IP。 如果上游系统(Azure Front Door 源站、Traffic Manager 终结点、防火墙允许列表)绑定了现有的 ingress-nginx IP,请将该上游配置更新为新的 Gateway IP,而不要尝试重新分配现有 IP——当拥有这些 IP 的 Service 被删除时,AKS 会删除其托管的公共 IP,即使将其标记为 Static 也是如此,因此你无法安全地执行原地 IP 替换。
主要差异
| 方面 | ingress-nginx 插件 | Gateway API(Istio)实现 |
|---|---|---|
| API |
networking.k8s.io/v1
Ingress
|
gateway.networking.k8s.io/v1
Gateway 和 HTTPRoute |
| Az CLI Enable 标志 | --enable-app-routing |
--enable-app-routing-istio(与 --enable-gateway-api 一起使用;需要 Managed Gateway API 加载项支持) |
| 类名 | webapprouting.kubernetes.azure.com |
approuting-istio |
| 数据平面服务 |
nginx 在 app-routing-system 中 |
<gateway-name>-approuting-istio 在 Gateway 的命名空间中 |
| Azure DNS/密钥保管库 TLS 连接 | 内置 | 尚不支持。 请参阅 安全应用程序路由网关 API 入口流量 |
先决条件
Azure CLI 2.86.0 或更高版本。
如果群集已启用 Istio 服务网格加载项,请先禁用它,并删除遗留的
networking.istio.ioCRD 和istioGatewayClass,然后再开始。 应用程序路由网关 API 控制平面在存在这些 CRD 时无法启动。 请参阅清理命令 的限制 。
设置本文其余部分使用的环境变量:
export RG=<resource-group>
export CLUSTER=<cluster-name>
步骤 1:清点当前入口
列出所有使用 ingress-nginx 类的 Ingress 以及指向 ingress-nginx 负载均衡器 IP 的主机名。 包括任何上游引用项,例如 Azure Front Door 源站、流量管理器终结点和防火墙允许列表。
kubectl get ingress -A -o jsonpath='{range .items[?(@.spec.ingressClassName=="webapprouting.kubernetes.azure.com")]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}'
捕获当前的入口-nginx IP,以便进行后续验证:
INGRESS_NGINX_IP=$(kubectl get svc -n app-routing-system nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
步骤 2:降低 DNS TTL
注释
如果客户端通过 IP 而不是 DNS 访问群集(步骤 7 中的直接转换是 DNS 更改),则跳过此步骤,因此,仅当 DNS 位于路径中时,低 TTL 才相关。
对于解析为 INGRESS_NGINX_IP的每个主机名,将记录 TTL 降低到 60 秒(或提供程序允许的最小值)。 等待上一个 TTL 窗口完全过期,然后再继续。 如果上一个 TTL 为 1 小时,请等待至少一小时。 跳过此步骤意味着缓存的解析程序在删除 Ingress后继续将客户端发送到 ingress-nginx。
步骤 3:启用网关 API 实现
启用托管网关 API CRD 和应用程序路由网关 API 实现。 Ingress-nginx 继续处理流量。
az aks update --resource-group $RG --name $CLUSTER --enable-gateway-api
az aks update --resource-group $RG --name $CLUSTER --enable-app-routing-istio
验证控制平面和 GatewayClass 是否已就绪:
kubectl -n aks-istio-system rollout status deploy/istiod --timeout=5m
kubectl get gatewayclass approuting-istio
步骤 4:将每个 Ingress 转换为 Gateway 和 HTTPRoute
为每个 Ingress 创建与之匹配的 Gateway 和 HTTPRoute。 有关各字段的转换说明(包括如何转换路径匹配规则、重定向、重写和标头处理),请参照 Kubernetes Gateway API 迁移指南。 若要批量转换现有清单,而不是手动编写,可以考虑使用 ingress2gateway;这是一个 Kubernetes SIG Network 工具,可读取 Ingress 资源(包括 ingress-nginx 注解)并生成等效的 Gateway API 资源——请将其输出视为起点,并在应用前进行审核。
以下示例将一个用于主机Ingress、路径前缀为httpbin.contoso.com、后端为/get的 ingress-nginx httpbin:8000 进行转换。 作为参考,源内容 Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httpbin
namespace: demo
spec:
ingressClassName: webapprouting.kubernetes.azure.com
rules:
- host: httpbin.contoso.com
http:
paths:
- path: /get
pathType: Prefix
backend:
service:
name: httpbin
port:
number: 8000
等效的 Gateway 和 HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: httpbin-gateway
namespace: demo
spec:
gatewayClassName: approuting-istio
listeners:
- name: http
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin
namespace: demo
spec:
parentRefs:
- name: httpbin-gateway
hostnames: ["httpbin.contoso.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /get
backendRefs:
- name: httpbin
port: 8000
等待 Gateway 完成配置并获取其公网 IP:
kubectl wait --for=condition=Programmed -n demo \
gateways.gateway.networking.k8s.io/httpbin-gateway --timeout=5m
GW_IP=$(kubectl get gateway -n demo httpbin-gateway -o jsonpath='{.status.addresses[0].value}')
步骤 5:在网关上预配 TLS
如果原来的Secret 引用生成的 Kubernetes certificateRefs。 用于应用程序路由的 Gateway API 实现的原生 Azure 密钥保管库 集成正在开发中;在其正式发布之前,上述 Secrets Store CSI 驱动程序工作流是当前受支持的方法。
步骤 6:在切换客户端流量之前验证网关
使用 curl --resolve 固定 DNS 解析来探测这两个数据平面。 当你将探测程序置于循环中连续运行几分钟时,两者都应持续返回 200。
Important
在对网关 IP 进行充分验证之前,请勿切换。 步骤 7 中的 DNS 切换是真实客户端流量切入新数据平面的时刻;在这一刻,网关侧的任何故障都会变成客户可感知的服务中断。 那些通过直接访问 GW_IP 很容易发现的缺陷,在混入生产流量后就更难分类排查了。
# New Gateway path
curl -sS -o /dev/null -w '%{http_code}\n' \
--resolve "httpbin.contoso.com:80:${GW_IP}" \
http://httpbin.contoso.com/get
# Old ingress-nginx path (should still work)
curl -sS -o /dev/null -w '%{http_code}\n' \
--resolve "httpbin.contoso.com:80:${INGRESS_NGINX_IP}" \
http://httpbin.contoso.com/get
如果在步骤 5 中添加了 TLS 侦听器,还要通过将端口切换为 443 并将方案切换为 https:// 来探测 HTTPS。
在翻转 DNS 之前,请至少针对 GW_IP 以下项(使用 --resolve 或 Host 标头)进行验证:
-
每个主机名和路由,即原始
Ingress提供服务的所有主机名和路由,而不只是某个理想情况下的 URL。 对于共享同一主机但路径前缀、请求头匹配条件或方法不同的路由,必须分别访问到每一个路由。 - 由 提供生产证书的
Gateway。 验证证书链和 SAN 列表是否与客户端预期匹配;不依赖--insecure。 - 应用程序行为,而不仅仅是 HTTP 状态代码。 提交 POST 请求正文,测试身份验证,并在你的工作负载使用长时间运行的响应(流式处理、WebSocket、gRPC)时,确认它们能够端到端正常工作。
-
持续负载至少数分钟,来自探测循环,并观察是否出现间歇性的 5xx 响应、延迟回退,或
Gateway/HTTPRoute状态条件的频繁波动。 - 尽可能在非生产配置中进行上游集成——例如,将暂存 Front Door 源站或 Traffic Manager 终结点指向
GW_IP,并验证健康探测通过。
每次前面的检查都一致通过后,才继续执行步骤 7。 如果出现任何问题,请在 Gateway 侧进行修复,此时 ingress-nginx 仍在为实际客户端提供服务——在 DNS 变更之前,你始终有无限的回滚余地。
步骤 7:剪切
- 将该主机名的 DNS A 记录更新为指向
GW_IP。 由于已在步骤 2 中降低 TTL,因此在几分钟内就会耗尽。 - 如果任何上游系统固定了 ingress-nginx IP(例如 Azure Front Door 源站、Traffic Manager 终结点或客户防火墙允许列表),请同时将其更新为
GW_IP。 请勿尝试将现有 IP 重新指派给网关:集群的云提供商会创建并管理 ingress-nginx Service 的公网 IP,并且当该 Service 被删除时,即使您将其设置为Static,这个公网 IP 也会被删除。 - 观察 ingress-nginx 上的流量下降情况。 抓取
ingress-nginx请求速率指标,直到请求数降至零。 - 将 DNS TTL 保持低等待数小时观察,然后将其提升回正常值。
步骤 8:清理
在 Gateway API 路径上的流量稳定后,停止加载项在集群上协调默认的 NginxIngressController,然后删除所有现有的 NginxIngressController 自定义资源。 此步骤中的 az aks approuting update --nginx None 命令会使控制器停止协调新资源,但它先前创建的 nginx Deployment 和 Service 仍会保留在集群中,直到您删除其所属的 NginxIngressController。
更新集群,使应用程序路由加载项不再管理默认的 ingress-nginx 控制器:
az aks approuting update --resource-group $RG --name $CLUSTER --nginx None
然后删除剩余 NginxIngressController 的资源:
kubectl delete nginxingresscontrollers.approuting.kubernetes.azure.com --all
验证稳定状态后,将 DNS TTL 提升回其正常值。
回滚
这两个数据平面可以共存,因此在禁用 ingress-nginx 插件之前的任何时刻,都可以进行对称回滚。 此过程会在整个切换过程中始终保留原始 Ingress 资源,因此回滚时只需还原 DNS:
如果在切换后删除了
Ingress资源,请重新创建它们。将 DNS 翻回到
INGRESS_NGINX_IP并等待 TTL 窗口清空。(可选)禁用网关 API 实现:
az aks update --resource-group $RG --name $CLUSTER --disable-app-routing-istio
运行 kubectl delete nginxingresscontrollers.approuting.kubernetes.azure.com 后,ingress-nginx 控制器就不存在了。 回退到该状态后,需要重新启用 Default NginxIngressController 加载项,并重新创建您删除的所有 Ingress 资源。 在禁用 ingress-nginx 之前验证所选路径。