元鉴
返回中文阅读流

Kubernetes Blog

迁移之前:你需要知晓的五个令人意外的 Ingress-NGINX 行为

正如 2025 年 11 月所宣布的那样,Kubernetes 将于 2026 年 3 月停用 Ingress-NGINX。尽管其使用广泛,但 Ingress-NGINX 充满了令人惊讶的默认设置和副作用,这些可能目前就存在于您的集群中。本文重点介绍了这些行为,以便您可以安全地迁移,并有意识地决定保留哪些行为。本文还将 Ingress-NGINX 与 Gateway API 进行了比较,并展示了如何在 Gateway API 中保留 Ingress-NGINX 的行为。每个部分中反复出现的风险模式都是相同的:看似正确的转换仍可能导致服务中断,如果它...

中文内容

已翻译official company source英文原文2026-02-27

迁移之前:你需要了解的五个令人意外的 Ingress-NGINX 行为

By Steven Jin (Microsoft) | Friday, February 27, 2026

正如 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 过滤器等功能提升为稳定版。

  1. 你可以将 Istio 纯粹用作 Gateway API 控制器,而不使用其他服务网格功能。↩︎
  • ← 上一页
  • 下一页 →
Last modified February 27, 2026 at 2:57 PM PST: Amend following Gateway 1.5 release (6e58f27e1d)

原文标题

Before You Migrate: Five Surprising Ingress-NGINX Behaviors You Need to Know