找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2017

积分

0

好友

285

主题
发表于 3 天前 | 查看: 10| 回复: 0

本文介绍了 Zalando 如何通过引入路由服务器(RouteSRV)帮助 Skipper Ingress 应对高速增长的流量,以更有效管理控制平面并确保集群稳定运行的实践。原文:Scaling Beyond Limits: Harnessing Route Server for a Stable Cluster [1]。

简介

在 Zalando,我们曾面临严峻挑战:传统的入口控制器有可能使 Kubernetes 集群不堪重负。我们需要一种能够应对流量指数级增长、并实现高效扩展的解决方案。本文将详细介绍我们如何引入一个路由服务器,以更有效地管理控制平面流量,从而确保整个集群稳定运行的实践经验。

Skipper:Ingress 控制器

我们使用 Skipper [2](一个 HTTP 反向代理)来实现 Kubernetes Ingress [3] 和 RouteGroups [4] 的控制平面与数据平面。

具体来说,创建一个 IngressRouteGroup 资源会触发以下流程:

  1. 生成一个带有 TLS 终止功能的 AWS 负载均衡器 (LB) [5]。
  2. 这个 LB 会通过 kube-ingress-aws-controller [6] 与 Skipper 通信。
  3. 在 Skipper 上设置相应的 HTTP 路由规则。
  4. 通过 external-dns [7] 将 DNS 域名指向该负载均衡器。

AWS账户中Skipper Ingress的架构与流量示意图

为了让你更直观地了解我们的部署环境规模,以下是一些关键数据:

  • 资源量:管理着 15,000 个 Ingress 和 5,000 个 RouteGroup。
  • 流量峰值:每秒处理高达 2,000,000 次请求。
  • 流量构成:80% - 90% 是经过认证的服务间调用,每日调用次数在 500,000 到 1,000,000 次。
  • 集群规模:覆盖 200 个 Kubernetes 集群。

挑战

扩容的痛点

最初,每个 Skipper 实例都直接从 Kubernetes API 获取 Ingress 和 RouteGroups 信息。这种方式在早期运行良好,但随着业务增长,Skipper 实例的数量迅速膨胀,达到每个集群约 180 个,开始超出后端 etcd 基础设施的承载能力。

这种过重的负载直接导致了严重的 Kubernetes API CPU 限流问题,从而引发了关键的控制平面稳定性风险。主要表现为两种后果:集群失去了有效调度新 Pod 的能力,同时现有 Pod 的管理操作也开始出现故障。这些问题直接威胁到了整个 Kubernetes 基础设施的整体稳定性和可靠性。

实施路由服务器

在引入路由服务器之前,每个 Skipper 都需要独立完成以下四项工作:

  1. 从 Kubernetes API 获取 Ingress 和 RouteGroups 信息。
  2. 解析并处理这些资源,将其转换为 Eskip [8] 格式(Skipper 的路由定义语言)。
  3. 验证生成的 Eskip 格式的正确性。
  4. 更新自身的路由表。

为了解决上述痛点,我们引入了路由服务器(Route Server) [9] 作为自定义的代理层。它的核心作用是更高效地处理控制平面流量,在 Skipper 与 Kubernetes API 服务器之间,充当一个带有 HTTP ETag 缓存层 [10] 的智能代理。

现在,路由服务器统一负责执行轮询和解析操作,极大减轻了每个 Skipper 实例的计算负担,同时实现了清晰的职责分离。

缓存层

路由服务器的工作流程如下:它每隔 3 秒向 Kubernetes API 发送一次请求,以获取最新的 IngressRouteGroup 列表。然后,它会根据这些信息生成统一的路由表,并计算出一个对应的 ETag 值。

当 Skipper 向路由服务器请求更新时,会在请求头中附带自己当前持有的 ETag。如果这个 ETag 与路由服务器当前计算出的 ETag 相匹配,就意味着路由信息没有发生变化。此时,路由服务器会以一个轻量的 HTTP 304 (Not Modified) 状态码 [11] 进行响应,避免了不必要的数据传输。反之,如果 ETag 不同,路由服务器就会将全新的路由表发送给 Skipper,Skipper 随后更新其本地配置和 ETag。

路由服务器不可用

尽管路由服务器极大地提高了系统效率,但我们也必须考虑其可能出现的故障场景。当路由服务器不可用时,主要有两种情况:

  1. Skipper 没有初始路由表(例如首次启动时)。
  2. Skipper 已有一个初始路由表,但无法从路由服务器获取更新。

对于第一种情况,如果启用了 -wait-first-route-load 标志 [12],Skipper 容器将无法启动。在第二种情况下,Skipper 会降级为继续使用最后已知的有效路由表。这是一种在服务可用性和数据强一致性之间做出的权衡。

在这两种情况下,运维团队都会收到告警。我们需要根据情况决策:是修复路由服务器,还是临时禁用它,让 Skipper 回退到直接访问 Kubernetes API 的旧模式。目前,我们尚未实现自动回退机制。

集成路由服务器后的最终架构与数据流向如下图所示:

引入路由服务器后的架构与流量示意图

部署策略

部署路由服务器绝非易事。哪怕出现一个微小错误,也可能导致 Kubernetes API 与所有 Skipper 实例的连接中断,从而直接影响线上服务的可用性,甚至冲击销售业绩和商品交易总额(GMV)。因此,我们必须极其谨慎,并遵循一套完善的渐进式部署策略。

我们的计划是以可控的方式逐步推进:首先在测试集群中部署,生产集群则被划分为不同的优先级层级。路由服务器将逐层部署,每完成一层的部署,都会进行严格的监控和观察,确认无误后再进行下一层。

为了实现这一精细化的部署控制,我们为路由服务器设计了三种不同的运行模式,并通过配置项 [13] 进行切换:

  • 模式:False - 禁用模式,路由服务器不工作。
  • 模式:Pre - 预处理模式,用于验证和对比。
  • 模式:Exec - 执行模式,正式代理流量。

默认模式为 false,意味着路由服务器被禁用,Skipper 仍使用原有的、直接访问 Kubernetes API 的控制平面通信方式。

禁用模式(False)下的流量示意图

预处理模式

在该模式下,路由服务器会与 Skipper 并行工作。它会从 Kubernetes API 获取 IngressRouteGroup,并进行预处理,生成自己的路由表。此模式主要用于测试、调试,也是我们分阶段上线策略的核心。

关键操作在于对比验证:我们同时获取 Skipper 自己生成的路由表和路由服务器生成的路由表,并进行逐项比对,以确保路由服务器的处理逻辑完全正确,没有任何路由差异。要知道,任何细微的路由表偏差都可能导致服务中断,这就是为什么我们必须如此谨慎。

# 使用一个极大的 limit 参数获取所有 Skipper 实例的路由
curl -i http://127.0.0.1:9911/routes\?limit\=10000000000000\&nopretty > skipper_routes.eskip

# 获取路由服务器的所有路由(我们决定不使用分页以减少请求数,因为Skipper是目前唯一的消费者)
curl -i http://127.0.0.1:9090/routes > routesrv_routes.eskip

# 使用 git diff 对比两个文件,确保内容完全一致
git diff --no-index -- skipper_routes.eskip routesrv_routes.eskip

预处理模式(Pre)下的架构示意图

执行模式

这是用于生产环境的最终模式。在该模式下,路由服务器正式成为 Skipper 与 Kubernetes API 之间的唯一代理。Skipper 的所有控制平面请求都发送给路由服务器,由路由服务器转发至 Kubernetes API。路由服务器会缓存响应,并利用 ETag 机制高效地返回给 Skipper。

执行模式(Exec)下的最终生产架构

产品上线

在经过全面的负载测试和预处理模式下的长期验证后,我们以高度可控的方式将路由服务器推向了生产环境:

  1. 首先部署到所有测试集群,并进行为期两周的持续监控,确保一切稳定。
  2. 随后,按照事先划分的层级,逐层部署到生产集群。每完成一个层级的部署,都会进行细致的监控和分析,确认无负面影响后,再继续下一层的部署。

替代方案

在方案选型时,我们曾考虑使用 Kubernetes Informer [14] 来监听 Kubernetes API 的资源变化,这是一种在 K8s 生态中常见的模式。然而,经过分析,我们认为这种方法本质上仍需要 Kubernetes API 向所有订阅的 Skipper 实例广播变更事件,这很可能导致我们最初遇到的相同问题——流量洪峰。问题的核心在于突发流量可能超出 HPA(水平自动伸缩)对 Kubernetes API 和 etcd 的扩展速度。

未来改进措施

  • 自动回退机制:建立一套健壮的回退机制,确保在路由服务器完全不可用的情况下,Skipper 能够自动、平滑地切换回直接访问 Kubernetes API 的模式,最大限度地保障服务连续性。

总结

通过引入路由服务器,我们取得了显著的成果:

  • 平稳上线:在整个上线过程中实现了零停机时间,且未出现任何商品交易总额(GMV)损失。
  • 显著扩容:成功将 Skipper 的 HPA 上限扩展到 300 个 Pod,满足了业务增长需求。
  • 卓越性能:单个路由服务器实例能够轻松处理高达 100 RPS 的请求,足以支撑约 300 个 Skipper Pod,且未出现任何性能瓶颈。
  • 核心地位:路由服务器现已稳定运行,并成为我们技术平台中不可或缺的核心组件之一。

参考资料
[1] Scaling Beyond Limits: Harnessing Route Server for a Stable Cluster: https://engineering.zalando.com/posts/2025/02/scaling-beyond-limits-harnessing-route-server-for-a-stable-cluster.html
[2] Skipper: https://opensource.zalando.com/skipper
[3] Kubernetes Ingress: https://kubernetes.io/docs/concepts/services-networking/ingress
[4] RouteGroups: https://opensource.zalando.com/skipper/kubernetes/routegroup-crd
[5] 带有 TLS 终止功能的 AWS LB: https://engineering.zalando.com/posts/2025/02/scaling-beyond-limits-harnessing-route-server-for-a-stable-cluster.html#fn:1
[6] kube-ingress-aws-controller: https://github.com/zalando-incubator/kube-ingress-aws-controller
[7] external-dns: https://github.com/kubernetes-sigs/external-dns
[8] Eskip: https://engineering.zalando.com/posts/2025/02/scaling-beyond-limits-harnessing-route-server-for-a-stable-cluster.html#fn:2
[9] 路由服务器: https://pkg.go.dev/github.com/zalando/skipper/routesrv
[10] HTTP ETag 缓存层: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
[11] HTTP 304 (Not Modified) 状态码: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304
[12] -wait-first-route-load 标志: https://github.com/zalando-incubator/kubernetes-on-aws/blob/4f2e04e3e056ba6c647d85e64fd842ada44deff3/cluster/manifests/skipper/deployment.yaml#L171
[13] 配置项: https://github.com/zalando-incubator/kubernetes-on-aws/blob/32658df25ee29049d5495cf422f85ab536b599bb/cluster/config-defaults.yaml#L223
[14] Kubernetes Informer: https://pkg.go.dev/k8s.io/client-go/informers

本文分享了 Zalando 在超大规模 Kubernetes 集群中优化 Ingress 控制平面的实战经验。更多关于云原生架构系统设计的深度讨论,欢迎访问云栈社区进行交流。




上一篇:2026年Web开发学习方法论:通过构建微项目高效掌握React与原生API
下一篇:软件工程师的职业现状反思:黄金时代结束与AI冲击下的适应之道
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-9 17:59 , Processed in 0.259937 second(s), 37 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表