中文内容
迁移之前:你需要了解的五个令人意外的 Ingress-NGINX 行为
正如 2025 年 11 月所宣布的,Kubernetes 将于 2026 年 3 月退役 Ingress-NGINX。尽管 Ingress-NGINX 被广泛使用,但它包含许多令人意外的默认设置和副作用,而这些很可能已经存在于你今天的集群中。本文重点介绍这些行为,以便你能够安全地迁移,并有意识地决定保留哪些行为。本文还将 Ingress-NGINX 与 Gateway API 进行比较,并展示如何在 Gateway API 中保留 Ingress-NGINX 的行为。每一节反复出现的风险模式都是相同的:如果没有考虑 Ingress-NGINX 的特殊行为,即使看似正确的转换仍可能导致故障。
我将假设你作为读者,对 Ingress-NGINX 和 Ingress API 有一定了解。大多数示例使用 httpbin 作为后端。
另外,请注意 Ingress-NGINX 和 NGINX Ingress 是两个不同的 Ingress 控制器。Ingress-NGINX 是由 Kubernetes 社区维护和治理的 Ingress 控制器,将于 2026 年 3 月退役。NGINX Ingress 是 F5 提供的 Ingress 控制器。两者都使用 NGINX 作为数据平面,但除此之外并无关联。从现在开始,本文仅讨论 Ingress-NGINX。
1. 正则表达式匹配是基于前缀且不区分大小写的
假设你想将所有路径仅由三个大写字母组成的请求路由到 httpbin 服务。你可以创建以下 Ingress,并添加 nginx.ingress.kubernetes.io/use-regex: "true" 注解以及 /[A-Z]{3} 的正则表达式模式。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: regex-match-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
rules:
- host: regex-match.example.com
http:
paths:
- path: "/[A-Z]{3}"
pathType: ImplementationSpecific
backend:
service:
name: httpbin
port:
number: 8000
但是,由于正则表达式匹配是前缀匹配且不区分大小写,Ingress-NGINX 会将路径以任意三个字母开头的任何请求路由到 httpbin:
curl -sS -H "Host: regex-match.example.com" http://<your-ingress-ip>/uuid
输出类似如下:
{
"uuid": "e55ef929-25a0-49e9-9175-1b6e87f40af7"
}
注意:httpbin 的 /uuid 端点会返回一个随机 UUID。响应正文中出现 UUID 意味着请求已成功路由到 httpbin。
借助 Gateway API,你可以使用类型为 RegularExpression 的 HTTP 路径匹配来进行正则表达式路径匹配。RegularExpression 匹配是实现特定的,因此请向你的 Gateway API 实现确认 RegularExpression 匹配的语义。流行的基于 Envoy 的 Gateway API 实现,例如 Istio 1、Envoy Gateway 和 Kgateway,会执行区分大小写的完整匹配。
因此,如果你不知道 Ingress-NGINX 模式是前缀匹配且不区分大小写,并且在你不知情的情况下,客户端或应用程序将流量发送到 /uuid(或 /uuid/some/other/path),你可能会创建以下 HTTP 路由。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: regex-match-route
spec:
hostnames:
- regex-match.example.com
parentRefs:
- name: <your gateway> # Change this depending on your use case
rules:
- matches:
- path:
type: RegularExpression
value: "/[A-Z]{3}"
backendRefs:
- name: httpbin
port: 8000
但是,如果你的 Gateway API 实现执行区分大小写的完整匹配,上述 HTTP 路由将不会匹配路径为 /uuid 的请求。因此,上述 HTTP 路由会导致服务中断,因为 Ingress-NGINX 路由到 httpbin 的请求会在网关处以 404 Not Found 失败。
要保留不区分大小写的正则表达式匹配,可以使用以下 HTTP 路由。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: regex-match-route
spec:
hostnames:
- regex-match.example.com
parentRefs:
- name: <your gateway> # Change this depending on your use case
rules:
- matches:
- path:
type: RegularExpression
value: "/[a-zA-Z]{3}.*"
backendRefs:
- name: httpbin
port: 8000
或者,上述代理支持使用 (?i) 标志来表示不区分大小写的匹配。使用该标志时,模式可以是 (?i)/[a-z]{3}.* 。
2. nginx.ingress.kubernetes.io/use-regex 适用于某个主机在所有(Ingress-NGINX)Ingress 中的所有路径
现在,假设你有一个带有 nginx.ingress.kubernetes.io/use-regex: "true" 注解的 Ingress,但你想将路径恰好为 /headers 的请求路由到 httpbin 。遗憾的是,你输入有误,把路径设置成了 /Header,而不是 /headers 。
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: regex-match-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
rules:
- host: regex-match.example.com
http:
paths:
- path: "<some regex pattern>"
pathType: ImplementationSpecific
backend:
service:
name: <your backend>
port:
number: 8000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: regex-match-ingress-other
spec:
ingressClassName: nginx
rules:
- host: regex-match.example.com
http:
paths:
- path: "/Header" # typo here, should be /headers
pathType: Exact
backend:
service:
name: httpbin
port:
number: 8000
大多数人会预期,对 /headers 的请求会返回 404 Not Found,因为 /headers 与 /Header 的 Exact 路径不匹配。然而,由于 regex-match-ingress Ingress 带有 nginx.ingress.kubernetes.io/use-regex: "true" 注解以及 regex-match.example.com 主机,所有带有 regex-match.example.com 主机的路径都会在所有(Ingress-NGINX)Ingress 中被视为正则表达式。由于正则表达式模式是不区分大小写的前缀匹配,/headers 会匹配 /Header 模式,Ingress-NGINX 会将此类请求路由到 httpbin。运行命令
curl -sS -H "Host: regex-match.example.com" http://<your-ingress-ip>/headers
输出如下:
{
"headers": {
...
}
}
注意:httpbin 的 /headers 端点会返回请求头。响应正文中包含请求头这一事实意味着该请求已成功路由到 httpbin。
Gateway API 不会静默地将 Exact 和 Prefix 匹配转换或解释为正则表达式模式。因此,如果你将上述 Ingress 转换为以下 HTTP 路由,并保留拼写错误和匹配类型,那么对 /headers 的请求将返回 404 Not Found,而不是 200 OK。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: regex-match-route
spec:
hostnames:
- regex-match.example.com
rules:
...
- matches:
- path:
type: Exact
value: "/Header"
backendRefs:
- name: httpbin
port: 8000
要保留不区分大小写的前缀匹配,可以将
- matches:
- path:
type: Exact
value: "/Header"
改为
- matches:
- path:
type: RegularExpression
value: "(?i)/Header"
或者更好的是,你可以修正这个拼写错误,并将匹配改为
- matches:
- path:
type: Exact
value: "/headers"
3. 重写目标意味着正则表达式
在这种情况下,假设你想在将路径为 /ip 的请求路由到 httpbin 之前,将其路径重写为 /uuid,并且如第 2 节所述,你想将路径恰好为 /headers 的请求路由到 httpbin。然而,你不小心打错了字,将路径设置成了 /IP 而不是 /ip,并将 /headers 设置成了 /Header。
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rewrite-target-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: "/uuid"
spec:
ingressClassName: nginx
rules:
- host: rewrite-target.example.com
http:
paths:
- path: "/IP"
pathType: Exact
backend:
service:
name: httpbin
port:
number: 8000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rewrite-target-ingress-other
spec:
ingressClassName: nginx
rules:
- host: rewrite-target.example.com
http:
paths:
- path: "/Header"
pathType: Exact
backend:
service:
name: httpbin
port:
number: 8000
nginx.ingress.kubernetes.io/rewrite-target: "/uuid" 注解会使匹配 rewrite-target-ingress Ingress 中路径的请求,在路由到后端之前将其路径重写为 /uuid。
即使没有任何 Ingress 带有 nginx.ingress.kubernetes.io/use-regex: "true" 注解,rewrite-target-ingress Ingress 中 nginx.ingress.kubernetes.io/rewrite-target 注解的存在也会导致所有带有 rewrite-target.example.com 主机的路径都被视为正则表达式模式。换句话说,nginx.ingress.kubernetes.io/rewrite-target 会静默地添加 nginx.ingress.kubernetes.io/use-regex: "true" 注解,并带来上文讨论的所有副作用。
例如,对 /ip 的请求会将其路径重写为 /uuid,因为 /ip 匹配 rewrite-target-ingress Ingress 中 /IP 的不区分大小写前缀模式。运行该命令后
curl -sS -H "Host: rewrite-target.example.com" http://<your-ingress-ip>/ip
输出类似于:
{
"uuid": "12a0def9-1adg-2943-adcd-1234aadfgc67"
}
与 nginx.ingress.kubernetes.io/use-regex 示例类似,Ingress-NGINX 会将 rewrite-target.example.com 主机下其他 Ingress 的路径视为不区分大小写的前缀模式。运行命令
curl -sS -H "Host: rewrite-target.example.com" http://<your-ingress-ip>/headers
会得到类似如下的输出
{
"headers": {
...
}
}
你可以在 Gateway API 中使用 HTTP URL rewrite 过滤器配置路径重写,该过滤器不会悄悄地将你的 Exact 和 Prefix 匹配转换为正则表达式模式。不过,如果你不了解 nginx.ingress.kubernetes.io/rewrite-target 注解的副作用,也没有意识到 /Header 和 /IP 都是拼写错误,你可能会创建以下 HTTP 路由。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: rewrite-target-route
spec:
hostnames:
- rewrite-target.example.com
parentRefs:
- name: <your-gateway>
rules:
- matches:
- path:
type: Exact
value: "/IP"
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplaceFullPath
replaceFullPath: /uuid
backendRefs:
- name: httpbin
port: 8000
- matches:
- path:
# This is an exact match, irrespective of other rules
type: Exact
value: "/Header"
backendRefs:
- name: httpbin
port: 8000
与第 2 节一样,由于 /IP 现在是你的 HTTP 路由中的精确匹配类型,对 /ip 的请求将返回 404 Not Found,而不是 200 OK。同样,对 /headers 的请求也将返回 404 Not Found,而不是 200 OK。因此,该 HTTP 路由会破坏依赖 /ip 和 /headers 路由的应用程序和客户端。
要解决此问题,你可以将 HTTP 路由中的匹配改为正则表达式匹配,并将路径模式改为不区分大小写的前缀匹配,如下所示。
- matches:
- path:
type: RegularExpression
value: "(?i)/IP.*"
...
- matches:
- path:
type: RegularExpression
value: "(?i)/Header.*"
或者,你可以保留精确匹配类型并修正拼写错误。
4. 缺少尾部斜杠的请求会被重定向到带有尾部斜杠的相同路径
请考虑以下 Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: trailing-slash-ingress
spec:
ingressClassName: nginx
rules:
- host: trailing-slash.example.com
http:
paths:
- path: "/my-path/"
pathType: Exact
backend:
service:
name: <your-backend>
port:
number: 8000
你可能会预期 Ingress-NGINX 对 /my-path 返回 404 Not Found,因为 /my-path 与 Exact 路径 /my-path/ 并不完全匹配。然而,Ingress-NGINX 会以 301 Moved Permanently 将请求重定向到 /my-path/,因为 /my-path 和 /my-path/ 之间唯一的区别是末尾的斜杠。
curl -isS -H "Host: trailing-slash.example.com" http://<your-ingress-ip>/my-path
输出如下所示:
HTTP/1.1 301 Moved Permanently
...
Location: http://trailing-slash.example.com/my-path/
...
如果你将 pathType 更改为 Prefix,同样适用。不过,如果路径是 regex 模式,则不会发生重定向。
符合规范的 Gateway API 实现不会静默配置任何类型的重定向。如果客户端或下游服务依赖此重定向,那么迁移到未显式配置请求重定向的 Gateway API 将导致服务中断,因为对 /my-path 的请求现在会返回 404 Not Found,而不是 301 Moved Permanently。你可以使用 HTTP 请求重定向过滤器显式配置重定向,如下所示:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: trailing-slash-route
spec:
hostnames:
- trailing-slash.example.com
parentRefs:
- name: <your-gateway>
rules:
- matches:
- path:
type: Exact
value: "/my-path"
filters:
requestRedirect:
statusCode: 301
path:
type: ReplaceFullPath
replaceFullPath: /my-path/
- matches:
- path:
type: Exact # or Prefix
value: "/my-path/"
backendRefs:
- name: <your-backend>
port: 8000
5. Ingress-NGINX 会规范化 URL
URL 规范化是指在将 URL 与 Ingress 规则进行匹配并路由之前,将其转换为规范形式的过程。URL 规范化的具体细节定义在 RFC 3986 第 6.2 节中,以下是一些示例:
- 移除仅为 . 的路径段:my/./path -> my/path
- 包含 .. 路径段会移除前一个路径段:my/../path -> /path
- 对路径中连续的斜杠进行去重:my//path -> my/path
Ingress-NGINX 会先规范化 URL,然后再将其与 Ingress 规则进行匹配。例如,请看以下 Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: path-normalization-ingress
spec:
ingressClassName: nginx
rules:
- host: path-normalization.example.com
http:
paths:
- path: "/uuid"
pathType: Exact
backend:
service:
name: httpbin
port:
number: 8000
Ingress-NGINX 会将以下请求的路径规范化为 /uuid。由于该请求现在匹配 /uuid 的 Exact 路径,Ingress-NGINX 会响应 200 OK,或响应 301 Moved Permanently 并重定向到 /uuid。
对于以下命令
curl -sS -H "Host: path-normalization.example.com" http://<your-ingress-ip>/uuid
curl -sS -H "Host: path-normalization.example.com" http://<your-ingress-ip>/ip/abc/../../uuid
curl -sSi -H "Host: path-normalization.example.com" http://<your-ingress-ip>////uuid
输出类似于
{
"uuid": "29c77dfe-73ec-4449-b70a-ef328ea9dbce"
}
{
"uuid": "d20d92e8-af57-4014-80ba-cf21c0c4ffae"
}
HTTP/1.1 301 Moved Permanently
...
Location: /uuid
...
你的后端可能依赖 Ingress/Gateway API 实现来规范化 URL。也就是说,大多数 Gateway API 实现默认都会启用某种路径规范化。例如,Istio、Envoy Gateway 和 Kgateway 都开箱即用地对 . 和 .. 段进行规范化。有关更多详细信息,请查看你所使用的各个 Gateway API 实现的文档。
结论
在我们都争相应对 Ingress-NGINX 退役之际,我希望这篇博客文章能让你相信,尽管 Ingress-NGINX 存在诸多复杂之处,你仍然可以安全而有效地完成迁移。
SIG Network 也一直在 Ingress2Gateway 中支持最常见的 Ingress-NGINX 注解(以及其中一些意外行为),以帮助你将 Ingress-NGINX 配置转换为 Gateway API,并为不受支持的行为提供替代方案。
SIG Network 今天早些时候(2026 年 2 月 27 日)发布了 Gateway API 1.5,该版本将 ListenerSet(允许应用开发者更好地管理 TLS 证书)以及允许进行 CORS 配置的 HTTPRoute CORS 过滤器等功能提升为稳定版。
- 你可以将 Istio 纯粹用作 Gateway API 控制器,而不使用其他服务网格功能。↩︎
- ← 上一页
- 下一页 →