T
traeai
登录
返回首页
Stack Overflow Blog

我们如何在 Stack Overflow 替换 NGINX-Ingress

8.7Score
我们如何在 Stack Overflow 替换 NGINX-Ingress

TL;DR · AI 摘要

因 Ingress-NGINX 被弃用,Stack Overflow 评估多种方案后选择符合 Gateway API 的控制器,并通过真实用例与压测完成迁移验证。

核心要点

  • Ingress-NGINX 的退役迫使 Stack Overflow 加速向 Gateway API 迁移。
  • 选型聚焦于符合 Gateway API 规范、跨云支持及可维护性,排除非标准扩展方案。
  • 通过 HTTPBin 和自定义 Go 服务构建测试环境,实现路由正确性与高并发性能双验证。

结构提纲

按章节快速跳转。

  1. Ingress-NGINX 宣布退役,推动迁移决策。

  2. 基于合规性、多云支持缩小至三选一。

  3. 参考功能矩阵、基准测试与独特特性进行对比。

  4. 使用 HTTPBin 和自研 Go 服务模拟真实流量。

  5. 从生产配置提取典型场景并自动化测试。

  6. 放弃 Ingress 方案,全面转向 Gateway API

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • 替换 Ingress-NGINX 的实践
    • 迁移动因
      • Ingress-NGINX 退役
      • 推动架构升级
    • 候选方案
      • NGINX Gateway Fabric
      • Traefik
      • Istio
    • 评估维度
      • Gateway API 合规性
      • 跨云支持
      • 性能与可维护性
    • 测试策略
      • HTTPBin 验证请求头重写
      • Go 服务压测性能边界

金句 / Highlights

值得收藏与分享的关键句。

  • We were pretty sure we’d like to use this opportunity to move to Gateway API, rather than another Ingress controller, and take advantage of the new features and better role separation.

    Paragraph 2

    ⬇︎ 下载 PNG𝕏 分享到 X
  • Our first criteria was that our replacement had to be on the list of fully-conformant implementations.

    Paragraph 4

    ⬇︎ 下载 PNG𝕏 分享到 X
  • HTTPBin has a /headers endpoint that will return the request headers back as a JSON response. That allows us to write a test case where we send host header X, and expect the server to get host header

    Paragraph 7

    ⬇︎ 下载 PNG𝕏 分享到 X
  • A parameter was added to the web server to allow us to pass in a latency value to simulate slower responses, in order to test performance where the number of connections and active requests begin to p

    Paragraph 8

    ⬇︎ 下载 PNG𝕏 分享到 X
  • The F5 NGINX ingress implementation looked to potentially be a safe bet, but we found the advanced routing relied on implementation-specific resources rather than the standard Kubernetes types.

    Paragraph 6

    ⬇︎ 下载 PNG𝕏 分享到 X
  • At the time of writing, it’s moved to fully conformant.

    Paragraph 5

    ⬇︎ 下载 PNG𝕏 分享到 X
#Kubernetes#Gateway API#Ingress#Traefik#Istio
打开原文

标题:我们在 Stack Overflow 是如何替换 NGINX Ingress 的

来源网址:http://stackoverflow.blog/2026/05/06/how-we-replaced-nginx-ingress-at-stack-overflow/

Markdown 内容: 去年11月,当宣布 Ingress-NGINX 将被弃用 时,我们和许多人一样,感到有些措手不及。自从迁移到 Kubernetes 后,Ingress-NGINX 一直负责我们的流量路由。虽然此前曾讨论过新的 Gateway API —— 它将取代 Ingress —— 以及是否值得迁移过去,但并未投入太多精力。毕竟 Ingress-NGINX 运行良好,而我们还有大量其他工作要做。如今,由于项目被正式弃用,我们不得不被迫制定计划,并将其纳入近期路线图中。

面对 众多可选方案,我们必须在开始安装和测试前缩小选择范围。我们基本确定希望借此机会迁移到 Gateway API,而不是再换一个 Ingress 控制器,以便利用其新功能和更清晰的职责分离。然而我们也需要快速完成迁移,因此如果转向 Gateway API 存在较大阻力,切换到另一个 Ingress 实现也可能是备选方案。

经过初步调研,我们制定了若干标准,将候选范围缩小至三个 Gateway 实现,另外保留两个 Ingress 方案作为备用:

  • F5 NGINX Ingress
  • Traefik

我们的第一个标准是:替代方案必须出现在 完全兼容实现列表 中,这为我们提供了良好的比较基准。由于我们在 GCP 和 Azure 上运行,因此排除了所有云厂商专属的解决方案。接着我们参考了 1.4 版功能矩阵、一份 第三方性能基准测试,以及各实现的独特功能,最终剩下上述三个选项。

遗憾的是,在我们启动该项目时,老朋友 HAProxy 仍处于“停滞实现”列表中。在将 Stack Overflow 迁移到 GKE 之前,我们在数据中心多年稳定地使用 HAProxy,它本应进入测试名单。不过在撰写本文时,它已升级为完全兼容状态。

至于继续使用 Ingress 的备用方案,Traefik 因其对 NGINX 注解的良好兼容性而颇具吸引力。但实际上,我们使用的大多数 注解 并未被支持,因此这一优势迅速消失。F5 NGINX Ingress 实现在当时看似稳妥,但我们发现其高级路由功能依赖于特定实现的资源类型,而非标准的 Kubernetes 类型。这会导致问题,因为当你尝试集成其他依赖标准接口的控制器时会出现不兼容。

最终,我们无法为切换至另一款 Ingress 提供充分理由,因此很早就将其从测试名单中移除。

为了构建测试用例,我们将主生产集群中的所有 Ingress 对象导出为 YAML 文件,并使用 Claude 分析并归类到不同的使用场景中。我们的大部分路由逻辑非常简单,只有少数例外情况。最终我们整理出大约六种典型使用场景需要编写测试,外加两种不同的可扩展性基准测试。

在测试环境搭建方面,主后端服务采用 HTTPBin 部署。这是一个测试 HTTP 相关功能的绝佳工具,允许你从测试客户端检查请求和响应的详细信息。举个例子:我们有一个场景需要动态重写特定流量的 Host 头。HTTPBin 提供了一个 /headers 接口,会将收到的请求头以 JSON 格式返回。这样我们就可以编写测试用例:发送 Host 头 X,期望服务器实际接收到 Host 头 Y。

此外还部署了第二个后端服务 perf.,这是一个非常简单的 Go 编写的 Web 服务器,能够快速响应大量请求。这使我们能将网关的请求率推到极高水平,从而观察其性能瓶颈所在。我们还在该服务器中添加了一个参数,用于注入延迟值以模拟慢速响应,进而测试在连接数和活跃请求数持续堆积时的性能表现。我原本不知道的是,HTTPBin 其实也有类似功能的接口。但由于不确定 HTTPBin 在高负载下的性能表现,测试中我们仍选择了 Go 服务器。整个测试架构如下所示。

![图片1:一张详细的流程图,展示了外部流量通过各种网关、Ingress 控制器和后端服务进行路由的架构。

  • 外部流量:客户端请求通过 Istio Gateway、Traefik Gateway 和 NGINX Gateway Fabric 等网关进行路由,每个网关处理 HTTP(端口 80)和 HTTPS(端口 443)流量,并支持 301 重定向。
  • Ingress 控制器:包括 ingress-nginx、traefik-ingress 和 f5-nginx-ingress,各自关联特定域名。
  • 路由规则:根据路径(如 /、/perf 和 /{random_id})进行流量分发。
  • 后端服务:请求被路由至 ingresstest-httpbin(端口 80)、perf-test(端口 8080)和 path-test(端口 8080)等服务。
  • 部署实例:后端服务使用特定镜像部署,例如 kennethreitz/httpbin:latest 和 go-server。

该图使用颜色编码的方框和箭头来表示组件之间流量和连接的流向。](https://cdn.stackoverflow.co/images/jo7n4k8s/production/7333227413d71b1caf2d02f675f5689d5f7b3215-1542x721.jpg?auto=format)

三种实现的设置都相对简单,不过我在使用 Traefik 时遇到的一个初始困扰是必须配置一个 Traefik “entrypoint”(入口点)。这是 Traefik 特有的配置项,用于设置 TCP 监听器。如果你不添加此项并创建网关,则在网关上添加的监听器会抛出错误。这在某种程度上破坏了网关的抽象性。

这三种实现都能满足我们的使用场景。就我们感兴趣的 Gateway API 功能而言,Istio 支持的功能最多,而 Traefik 最少。我们很快发现,某些 Gateway API 功能虽然在纸面上看起来不错,但缺乏我们需要的深度。例如,HTTPRoute 中的请求头修改功能仅支持静态值。在前述需要动态正则表达式的案例中,我们必须回退到各实现提供的扩展点。这些扩展机制都足够灵活,能够根据你的需求和执行的功能,在 xRoute 或 Gateway 的上下文中引用不同的行为。在我们测试的用例中,Istio 过滤器的语法复杂度明显高于 NGINX 或 Traefik 的对应实现。

在某些情况下,不同实现之间的行为差异较大,导致我们必须修改应用程序才能使其正常工作。目前我们正在使用 ngx_http_auth_request_module 将请求转发到认证服务。其他实现也具备类似功能,但 Istio 的外部授权机制行为上有显著差异。这类复杂的集成在迁移到新解决方案时会成为痛点,并显著拖慢进度。

我非常喜爱性能分析,因此很容易陷入细节并收集大量详细信息。但在本次评估中,由于时间和资源都非常有限,我必须确保保持简洁和实用。在更大规模下,可能会有非常明确的优劣之分,但我们并非要进行通用基准对比,而只是需要确认这些实现在当前是否能满足我们的可扩展性要求。

我们有两个主要的扩展因素需要测试。对于我们的公共 Stack Exchange 网站网络,我们需要验证每种实现能否以合理数量的副本处理我们日常流量的若干倍;对于我们的企业产品,每位客户目前拥有八个 Ingress 资源,因此我们希望测试 HTTPRoute 数量的扩展能力。

在对网关进行 RPS 基准测试时,我们设定了 10,000 RPS 的目标,这将使我们留有一定的余量以应对常规稳态流量。经过几轮初步测试后,我们将测试环境配置为每个网关运行四个副本,且分别位于独立节点上。这些节点为 GCP 的 e2-standard-4 计算实例。测试客户端运行 K6,部署在一台能够处理高并发连接的机器上,具体为 Azure 的 Standard_DC8as_cc_v5 实例。

我们分别在模拟服务器延迟为 0ms、150ms 和 350ms 的条件下运行测试。三种实现均无故障地通过了这些初步测试,结果也相对接近。以下是 150ms 延迟测试的结果:

http_req_duration..............: avg=188.27ms min=176ms

med=180.22ms max=820.56ms p(90)=195.11ms p(95)=219.31ms

http_req_duration..............: avg=205.34ms min=176.15ms

med=183.13ms max=1.83s p(90)=244.14ms p(95)=297.59ms

http_req_duration..............: avg=186.73ms min=176.03ms

med=180.52ms max=3.48s p(90)=194.34ms p(95)=216.32ms

最初,我们以 5000 个 HTTPRoute 为目标运行此测试。尽管三种实现都能成功收敛到该路由数量,但我们后来发现实际可用的上限要低得多。我曾在 第三方基准测试 中读到关于 Traefik 和 NGINX 在处理路由变更时存在的一些问题。于是我们也设置了类似的测试:创建 5000 个 HTTPRoute,每个包含一条路径规则,并并发向这些路径发送请求,直到返回正确响应为止。这样我们可以验证所有路由是否都被正确转发。

虽然我们未完全复现第三方测试中 Traefik 未能加载全部路由的问题,但确实观察到其耗时显著长于另外两种实现。如下所示,原始测试中 Traefik 因五分钟后超时而失败。但实际上 Traefik 最终加载了所有路由,只是耗时略超过五分钟。

code
=== RUN TestRoutedPaths

=== RUN TestRoutedPaths/gw-nginx

gateway_test.go:443: 5000/5000 paths pending, retrying in 5s

gateway_test.go:443: 105/5000 paths pending, retrying in 5s

gateway_test.go:443: 105/5000 paths pending, retrying in 5s

gateway_test.go:443: 87/5000 paths pending, retrying in 5s

gateway_test.go:335: applied 5000 HTTPRoutes

gateway_test.go:443: 3/5000 paths pending, retrying in 5s

gateway_test.go:431: all 5000 routes converged in 42.047s

=== RUN TestRoutedPaths/gw-istio

gateway_test.go:443: 5000/5000 paths pending, retrying in 5s

gateway_test.go:443: 1167/5000 paths pending, retrying in 5s

gateway_test.go:443: 93/5000 paths pending, retrying in 5s

gateway_test.go:443: 93/5000 paths pending, retrying in 5s

gateway_test.go:443: 32/5000 paths pending, retrying in 5s

gateway_test.go:443: 32/5000 paths pending, retrying in 5s

gateway_test.go:335: applied 5000 HTTPRoutes

gateway_test.go:431: all 5000 routes converged in 41.981s

=== RUN TestRoutedPaths/gw-traefik

gateway_test.go:443: 5000/5000 paths pending, retrying in 5s

gateway_test.go:443: 4939/5000 路径待处理,5 秒后重试

gateway_test.go:443: 4921/5000 路径待处理,5 秒后重试

gateway_test.go:335: 已应用 5000 个 HTTPRoutes

gateway_test.go:443: 4871/5000 路径待处理,5 秒后重试

gateway_test.go:443: 4856/5000 路径待处理,5 秒后重试

gateway_test.go:443: 4833/5000 路径待处理,5 秒后重试

gateway_test.go:443: 4827/5000 路径待处理,5 秒后重试

gateway_test.go:443: 4823/5000 路径待处理,5 秒后重试

gateway_test.go:443: 4820/5000 路径待处理,5 秒后重试

gateway_test.go:443: 4816/5000 路径待处理,5 秒后重试

gateway_test.go:443: 4811/5000 路径待处理,5 秒后重试

...

--- FAIL: TestRoutedPaths (441.53s)

--- PASS: TestRoutedPaths/gw-nginx (56.68s)

--- PASS: TestRoutedPaths/gw-istio (79.96s)

--- FAIL: TestRoutedPaths/gw-traefik (304.88s)

--- FAIL: TestRoutedPaths (410.94s)

--- PASS: TestRoutedPaths/gw-nginx (60.82s)

--- PASS: TestRoutedPaths/gw-istio (46.98s)

--- FAIL: TestRoutedPaths/gw-traefik (303.13s)

code

在将网关加载了 5000 条路由后,我们发现这个目标过于激进。之前用于通过网关生成 10k RPS 的 K6 压测完全失败:延迟极高,活跃请求数持续上升,最终导致测试客户端崩溃。考虑到我们几乎不会在单个网关上配置超过 1000 条路由,于是我们将目标下调并重新运行 K6 基准测试。这一次的响应时间与最初的测试结果相近。

我们还测试了在 K6 流量测试运行期间动态修改路由的情况。当配置了 1000 条路由时,Istio 和 Traefik 表现稳定,但 NGINX 在仅更新一条 HTTPRoute 时就出现了明显的延迟峰值。从下图中可以看到,在路由更新期间出现了两次显著的延迟高峰。

![图 2:来自性能测试工具 k6 的仪表盘,包含四个图表:

1. 虚拟用户数:折线图显示随时间变化的虚拟用户数量,在 17:45 至 17:46 之间达到约 3,500 用户的峰值。
2. HTTP 请求耗时:折线图显示 HTTP 请求的持续时间(秒),在 17:45 左右出现明显峰值。
3. 每秒请求数:折线图显示每秒请求数量,在 17:45 至 17:46 之间达到约 10,000 的峰值。
4. 响应时间(95 百分位):柱状图显示响应时间(秒),在 17:45 左右有显著峰值。

这些图表提供了负载测试期间系统性能的洞察。](https://cdn.stackoverflow.co/images/jo7n4k8s/production/d855868ae2f6d57adb675a8f59fbd84380a825ff-1394x432.jpg?auto=format)

在完成所有测试并对这三种实现的功能和特性进行分析后,我们决定采用 Istio。主要原因是 Istio 在所有测试中表现出的稳定性与性能。我认为这三种方案中的任何一种我们都可能成功使用,但最终 Istio 显得最为可靠。此外,它还具备许多超出我们当前使用场景的高级功能,这一点也令人感兴趣。

在接下来的几周内,我们将进行迁移工作。如果过程中遇到值得注意的问题,我很乐意撰写后续文章分享我们的发现。

AI 可能会生成不准确的信息,请核实重要内容

我们如何在 Stack Overflow 替换 NGINX-Ingress | Stack Overflow Blog | traeai