[译] Istio Ambient 模式:无 Sidecar Istio 如何让应用更快?

探索如何使用 Fortio 与 Istio 集成,在使用 Bookinfo 应用和流行的 DevOps 工具如 Kubernetes、Prometheus 和 Grafana 的微服务架构中进行高效的性能测试和监控。

声明
此文为个人翻译,仅供参考,不代表我个人立场。翻译过程中可能有删改或遗漏,如需了解原文,请自行查阅。如有疏漏,欢迎指正。
查看本文大纲

Ambient 模式是 2022 年在 Istio 中引入的新型无 sidecar 数据平面。当今年 5 月 Ambient 模式 达到 Beta 阶段时,我观察到用户开始试用并运行负载测试,以理解将应用添加到网格后的性能影响。

受到 Quentin Joly 的博客 关于 Istio 在 Ambient 模式下的惊人性能的启发,以及来自社区其他用户有时应用在 Ambient 模式 下稍快的反馈,我决定自己验证这些结果。

测试环境

我使用了一个拥有 256GB RAM 和每个节点 32 核 CPU 的三节点 Kubernetes 集群。

image

Istio 使用了一些工具来简化一致性的基准测试。首先,我们使用一个叫做 Fortio 的负载测试工具,它以指定的每秒请求数 (RPS) 运行,记录执行时间的直方图并计算百分位数,例如 P99,即 99% 的请求在此时间内完成。

我们还提供了一个叫做 Bookinfo 的示例应用,其中包括用 Python、Java、Node.js 和 Ruby 编写的微服务。

每个 Bookinfo 部署都有两个副本,这些副本均匀分布在三个工作节点上。使用 pod anti-affinity rule,我确保 Fortio 被放置在与 details 服务不同的节点上。

初始测试结果

我从 Istio v1.22.3 版本安装了 Bookinfo 应用。使用 Fortio 工具对单个 Bookinfo 服务(例如 details)或完整的 Bookinfo 应用进行负载驱动,我注意到在将所有服务添加到 ambient 网格后,延迟影响 接近零。大多数时间它们的增加范围在 0-5% 之间,用于平均值或 P90。我一致注意到 Istio 的 details 服务在 ambient 模式下稍微快一点,就像 Quentin 在他的博客中报告的那样。

对 Details 服务进行负载测试

我进行了与 Quentin 相同的测试,通过 10 个连接发送 100 RPS 到 details 服务,并收集了无网格和 ambient 的结果。

image
无网格:details 服务 100 RPS。
image
Ambient:details 服务 100 RPS。

就像 Quentin 一样,我不得不进行多次测试以验证 ambient 模式比无网格模式略有性能提升——这很难让人相信!对于 Bookinfo 的 details 服务来说,加入 ambient 模式平均降低了 6-11% 的延迟——以及添加了 mTLS 和 L4 观测!

Fortio 对 details 平均 P50 P75 P90 P99 差异
无网格运行 1 0.89ms 0.64ms 0.74ms 0.85ms 2.67ms 平均慢 11%,P90 慢 5%
Ambient 运行 1 0.80ms 0.6ms 0.71ms 0.81ms 1.4ms
无网格运行 2 0.86ms 0.65ms 0.75ms 0.86ms 1.71ms 平均慢 6%,P90 慢 4%
Ambient 运行 2 0.81ms 0.61ms 0.72ms 0.83ms 1.56ms
无网格运行 3 0.90ms 0.65ms 0.76ms 0.88ms 1.92ms 平均慢 10%,P90 慢 5%
Ambient 运行 3 0.82ms 0.63ms 0.72ms 0.84ms 1.5ms

表 1: Fortio 对 details 服务 100 RPS 10 连接。

为什么应用有时在 Ambient Mesh 中更快?

我们被教导说服务网格会增加延迟。Quentin 的结果,这里复制的结果,展示了一个工作负载在通过服务网格运行时更快的案例。这是怎么回事?

第一种理论

当您的应用位于 ambient 模式 中时,负载请求首先通过一个轻量级的本地节点代理叫做 ztunnel,然后传送到目的地 ztunnel,再向服务传送。details 服务使用带有 Webrick 库的 HTTP/1.1,我们已经看到旧的或配置不良的 HTTP 库中存在连接管理和保持活动状态的行为不佳。我的第一个假设是,当客户端和服务器位于不同节点时,通过客户端和服务器 ztunnels 代理实际上可能更快,如果应用没有使用高效的 HTTP/2 连接的话。Ztunnel 使用连接池和 HTTP Connect 建立节点之间的安全通道,以在负载下利用并行性和 HTTP/2 流多路复用。

image

然而,这个理论有一些挑战。为什么我只在 details 服务上一致观察到这个,而不是任何其他 Bookinfo 服务?

进一步研究,我发现我们的 Fortio 负载工具默认启用了连接保持活动。使用 10 个来自 Fortio 的连接到 details 服务和 details 服务(使用 WEBrick Ruby 库)尊重连接保持活动设置,连接可以有效地被重用,无需 ambient。

用 Connection Close 进行负载测试

接下来,我探索了使用设置 Connection: close 标头的同样负载测试。这强制禁用任何 HTTP 连接池,这是测试这个假设的好方法。

curl -v -d '{"metadata": {"url":"http://details:9080/details/0", "c":"10", "qps": "100", "n": "2000", "async":"on", "save":"on"}}' "localhost:8081/fortio/rest/run?jsonPath=.metadata" -H "Connection: close"
image
无网格:details 服务 100 RPS 10 连接带有 connection close。
image
Ambient:details 服务 100 RPS 10 连接带有 connection close。
Fortio 对 details 平均 P50 P75 P90 P99 差异
无网格 1.90ms 1.72ms 2.28ms 2.77ms 3.98ms
Ambient 2.06ms 2.15ms 2.65ms 2.94ms 4ms 平均慢 8%,P90 慢 6%

表 2: Fortio 对 details 服务 100 RPS 10 连接带有 connection close。

与表 1 的结果相比,表 2 的响应时间明显更高,这是预期的,因为每个连接在 details 服务响应后立即关闭。考虑到 P50、P75、P90 和 P99 都从带有 connection close 的 ambient 运行中变慢,似乎可以安全排除第一理论中的 ztunnel 连接池可能使请求更快。

第二种理论

我注意到在我们新的 Istio v1.23 版本中的 details 和 productpage 服务中有一个与性能相关的 PR。对于 details 服务,PR 为 details WEBrick 服务器启用了 TCP_NODELAY 标志,这将减少来自 details 服务响应时间的不必要延迟(高达 40ms)。对于 productpage 服务,PR 在传入请求上启用了保持活动状态,这将重用现有的传入连接,从而提高性能。

包含修复的新更新的 details 部署中,我重复了通过 10 个连接发送 100 RPS 到 details 服务的相同测试。无网格和 ambient 的结果非常接近,所以我进行了三次测试以确保结果的一致性。下面是每个场景的第一次运行的截图:

image
无网格:新 details 服务 100 RPS 10 连接。
image
Ambient:新 details 服务 100 RPS 10 连接。

我为每个场景的三次运行建立了一个表格:

image

表 3: Fortio 对新 details 服务 100 RPS 10 连接。

与表 1 的先前结果相比,表 3 的无网格数字有了相当大的改进(在更高百分比下更显著地超过 ambient 数字),现在接近于 ambient 数字。Ztunnel 默认启用了 TCP_NODELAY,这有助于 ambient 性能在表 1 中超过无网格,当旧的 details 服务没有启用 TCP_NODELAY 时。当新的 details 服务启用了 TCP_NODELAY 时,它也稍微提高了 ambient 的响应时间。

表 3 还显示,在此类型的负载测试中,无网格和 ambient 运行之间的平均、P50、P75 和 P90 几乎没有差异。这些运行之间的差异可能只是噪音,除了 P99,无网格始终比 ambient 慢 8% 或更多。

第三种理论

继续审查表 3 的测试结果,为什么在有额外跳转到 ztunnel pod 和 ambient 提供的如 mTLS 和 L4 观测等显著优势时,无网格和 ambient 之间的延迟相似?对于 P99 情况,为什么 details 服务在 ambient 模式下始终更快?

Ztunnel 提供了出色的读写缓冲管理,并通过 HTTP/2 多路复用,可以有效地最小化或有时甚至消除通过客户端和服务器 ztunnel pod 的额外跳转所增加的开销。我决定通过在两个 Fortio 和 details 服务的 Kubernetes 工作节点上进入并附加 strace 来测量这一点,同时过滤掉无关的跟踪:

strace -fp {pid} -e trace=write,writev,read,recvfrom,sendto,readv

无网格和 ambient 情况下的 details 服务的 strace 输出是相似的:

…read(9, "GET /details/0 HTTP/1.1\r\nHost: d"..., 8192) = 118write(9, "HTTP/1.1 200 OK\r\nContent-Type: a"..., 180) = 180write(9, "{"id":0,"author":"William Shakes"..., 178) = 178write(2, "192.168.239.19 - - [13/Aug/2024:"..., 80) = 80…

输出 1: 无网格或 ambient —— 附加 strace 到 details 服务的 PID。

无网格和 ambient 情况下的 Fortio 服务的 strace 输出不同。在无网格情况下,我们看到 Fortio 执行了两次读取,一次用于 HTTP 头部,另一次用于正文。

…read(13, "HTTP/1.1 200 OK\r\nContent-Type: a"..., 4096) = 180read(13, "{"id":0,"author":"William Shakes"..., 4096) = 178…write(19, "GET /details/0 HTTP/1.1\r\nHost: d"..., 118) = 118 …

输出 2: 无网格 —— 附加 strace 到 Fortio 的 PID。

在 ambient 情况下,我们始终只看到一个读取,用于同时获取头部和正文。

…read(19, "HTTP/1.1 200 OK\r\nContent-Type: a"..., 4096) = 358…write(19, "GET /details/0 HTTP/1.1\r\nHost: d"..., 118) = 118…

输出 3: Ambient 模式 —— 附加 strace 到 Fortio 的 PID。

为什么会这样?这是有道理的,因为 write 调用完全基于应用行为,而这在这种情况下没有变化。Ambient 将这些多个应用写入合并为单个网络写入,并隐含地在对等端进行单个读取。

在上述测试场景中,我观察到 Fortio 服务在启用 ambient 时系统调用总数减少了 60%。这非常重要,并解释了在 peak 时 Fortio pod 的延迟和 CPU 使用量减少约 25% 的大部分原因。系统调用的减少超过了 mTLS 和 ztunnel 的其他功能的成本。我预计这种模式在企业中会相当常见,因为一些 HTTP 库和应用在缓冲和刷新方面做得更好,而一些则不太好。这通常与应用的年龄和它们构建时使用的 SDK 相关。

image
无网格和 ambient 运行:details 服务 100 QPS 10 连接。

整个 Bookinfo 应用怎么样?

在更新了 details 和 productpage 部署之后,我开始通过 100 个连接发送 1000 RPS 到 Bookinfo 应用,并观察到无网格和 ambient 的优异结果。

image
无网格:新 Bookinfo 应用 1000 RPS 100 连接。
image
无网格:新 Bookinfo 应用 1000 RPS 100 连接。
Fortio 对 Bookinfo 平均 P50 P75 P90 P99 平均差异
无网格 1.39ms 1.32ms 1.42ms 1.67ms 2.19ms
Ambient 1.40ms 1.34ms 1.48ms 1.68ms 2.94ms 平均和 P90 均慢不到 1%

表 4: Fortio 对新 Bookinfo 应用 1000 RPS 100 连接。

作为对比,我还针对 v1.22.3 版本中附带的旧 Bookinfo 示例进行了同样的测试,你可以看到新 Bookinfo 在响应时间上取得了 5-10 倍 的提升,无论是无网格还是 ambient!

Fortio 对 Bookinfo 平均 P50 P75 P90 P99 平均差异
无网格 6.35ms 4.68ms 7.44ms 11.4ms 36.63ms
Ambient 6.74ms 4.9ms 7.79ms 12.12ms 41.14ms 慢 6%

表 5: Fortio 对旧 Bookinfo 应用 1000 RPS 100 连接。

将负载增加到 4000 RPS 和 400 连接,并使用新 Bookinfo 部署:

image
Ambient:新 Bookinfo 应用 4000 RPS 400 连接。
image
Ambient:新 Bookinfo 应用 4000 RPS 400 连接。

响应时间依然很好,远远优于只有 1000 RPS 和 100 连接的旧 Bookinfo 应用(表 5):

Fortio 对 Bookinfo 平均 P50 P75 P90 P99 平均差异
无网格 1.54ms 1.33ms 1.54ms 2.25ms 3.98ms
Ambient 1.58ms 1.37ms 1.57ms 2.33ms 4.9ms 平均慢 3%,P90 慢 4%

表 6: Fortio 对新 Bookinfo 应用 4000 RPS 400 连接。

很高兴看到 Bookinfo 能够在 4000 RPS 下无错误地运行,而且 ambient 模式比无网格慢 3-4%,但带来了传输中加密的 mTLS 和 L4 观测的所有好处。我记得我之前只能在旧 Bookinfo 应用中达到最高 1200 RPS,这已经导致了少量的错误。现在我可以增加到 4000 或更高 RPS 而不出现错误。

总结

在 L4 上,Ambient 模式只引入了非常微小的影响——偶尔甚至可以自动改善!— 用户应用的延迟。结合简单的用户体验,通过标记命名空间以将您的应用注册到 ambient 而无需重启任何工作负载,它为用户提供了我们初衷中预期的愉快体验。

我想感谢所有 Istio 维护者,他们构建了这样一个令人愉快的项目,以及为 Istio 项目提供测试基础设施的 CNCF。我还要感谢 Quentin Joly 和许多提供了“ambient 有时比无网格稍快”的反馈的用户,这促使我进行了上述基准测试,亲身体验了在负载下的改善或微小的延迟影响。

最后更新于 2024/09/18