您的位置:  首页 > 技术杂谈 > 正文

落地 eBPF 可观测性之 DeepFlow Agent 性能揭秘

2023-06-01 11:00 https://my.oschina.net/u/3681970/blog/9698278 云杉网络 次阅读 条评论

DeepFlow 基于 eBPF 实现了零插桩(Zero Code)的云原生应用可观测性,能够在不改代码、不改启动参数、不重启进程的前提下实现分布式追踪。这是一种全新的技术手段,因此不少用户在选型和落地 DeepFlow 的过程中会对它的性能开销存在疑问。到底 Agent 的运行会对业务造成什么样的影响?而 Agent 自身的资源开销又如何?这些问题我们在 SIGCOMM 2023 论文《Network-Centric Distributed Tracing with DeepFlow: Troubleshooting Your Microservices in Zero Code》中都有体系化的回答,论文将于九月份正式公开。在此之前,为了尽快帮助大家扫清落地 eBPF 可观测性的最后障碍,最近我们也将 DeepFlow Agent 的自动化测试结果放到了线上 Demo 页面中[1],本文将结合 Agent Daily Build 的测试数据,系统性的阐述我们的测试方法和测试结果,揭示 Agent 的业务影响和资源开销,帮助大家扫清落地 eBPF 可观测性的最后障碍。

欢迎报名6月10日 蓝鲸 x DeepFlow 可观测性Meetup 将首次向公众披露论文细节,并畅谈 DeepFlow 社区发展、用户案例、以及未来迭代计划。

通过分析 Agent 的处理流程,我们设计了六个场景进行全方位的评测。测试结果表明 Agent 对典型高负载生产业务的 TPS 无任何影响、CPU 增长仅 0.46%、平均单个调用的 RT 增长小于 1ms。Agent 对 HTTP 流量的处理性能不低于 Nginx,通过 eBPF uprobe 采集 Golang 协程信息的性能为 Pixie 方案的 2.5 倍。Agent 在 1 核 1GB 内存限制下可采集 90K RPS HTTP 流量,或 122K CPS 并发的 TCP 流量,或 20+Gbps(未到极限) TCP Flood 流量,或 1.28Mpps UDP Flood 流量。详细的测试方法和测试数据见正文,详细的总结见文末章节。

01 | Agent 处理流程

为了确定性能测试的方法,我们首先需要对 Agent 的处理流程有一定了解,做到有的放矢。如下图所示,Agent 主要从三个 eBPF 接口获取数据,自下而上依次是:

  • 通过 AF_PACKET 结合 BPF 过滤程序,获取应用的网络包数据,用于采集 NET Span

  • 通过 kprobe 和 tracepoint 接口,获取应用的系统调用数据,用于采集 SYS Span,以及每个调用生命周期内的慢文件 IO 事件

  • 通过 uprobe 接口,获取应用程序的函数调用数据,用于实现 Golang 等协程语言的零插桩分布式追踪,也用于采集 HTTP2/HTTPS 协议的 SYS Span

DeepFlow Agent 处理流程

DeepFlow Agent 处理流程

当获取到 Packet/Socket/Function 数据之后,DeepFlow Agent 需要解析数据中的应用协议,获取 Span,并关联、聚合形成 Trace、Metrics、Logs 数据。特别的,对于 Packet 数据,Agent 还需要基于包聚合生成 TCP/UDP Flow,用于生成流日志,并计算网络层的吞吐、时延、异常等性能指标。

理解 Agent 处理流程以后,我们希望设计一组测试例,通过他们我们希望能评估:

  1. Agent 的运行对业务性能有什么样的影响?
    • 业务的 TPS(Transactions Per Second)降低了多少?

    • 业务的 RT(Response Time)升高了多少?

    • 业务的 CPU/MEM 消耗升高了多少?

  2. Agent 自身的处理性能如何?
    • 特定压力下 Agent 的 CPU/MEM 消耗如何?

    • Agent 采集应用数据的 RPS 能力如何?

    • Agent 采集网络数据的 CPS/BPS/PPS 能力如何?

02 | 测试方法和目的

首先,为了评估 Agent 对业务性能的影响,我们希望设计典型的业务场景来进行评估,并希望能覆盖到 Agent 处理流程中的所有重要环节。我们一共设计了三个场景,如下图所示:

通过典型业务场景评估 Agent 对业务性能的影响

通过典型业务场景评估 Agent 对业务性能的影响

场景 A - 典型云原生微服务:DeepFlow 面向云原生场景,我们首先找到了 Istio Bookinfo Demo[2]。Istio 是一种流行的服务网格解决方案,在 GitHub 上拥有 32.9K Star。这个 Demo 的应用拓扑见上图,我们可以看到它由 Python、Java、Ruby、Node.JS 实现的四个微服务组成,每个微服务所在的 Pod 中运行着 Envoy 代理。这个 Demo 中的一个事务对应着访问 4 个微服务的 4 个调用。特别值得我们关注的是,由于 Envoy 的存在实际调用链深度会被拉长两倍。

场景 B - 极高性能极简业务:除了常规业务微服务以外,DeepFlow 还能采集并追踪基础设施服务。因此我们计划测试一个极致高性能、极简业务逻辑的中间件服务。我们选择了 Nginx,我们知道它以性能强悍著称,它用 C 语言实现,而且我们在测试中让他只是简单的回复一个默认静态页。我们相信这个 Nginx Demo 自身的性能表现远超过任何一个实际的生产环境,我们希望使用这个 Demo 来说明 DeepFlow Agent 的采集对极端高性能的中间件影响如何。

场景 C - 用 uprobe 追踪 Golang:我们知道 eBPF 中 uprobe 的性能要明显低于 kprobe/tracepoint,Brendan Gregg 在他的著作《BPF 之巅 - 洞悉 Linux 系统和应用性能》[3]中给出的参考数据中 uprobe 开销大约为 tracepoint 的 14 倍(DeepFlow 没有使用 uretprobe,它的开销是 tracepoint 的 20 倍)。虽然 DeepFlow 中大量的 Probe 都在使用最高性能的 tracepoint,并辅以 kprobe,但我们还是希望能有一个场景专注于对 uprobe 性能影响的评估,看看实际业务场景下 DeepFlow Agent 的表现。因此我们编写了一个 Golang Service 的 Demo[4],它对外提供一个 API,并会在 API 实现逻辑中调用上游的 Redis、MySQL、Nginx 三个服务。DeepFlow Agent 使用 uprobe 来 Hook Golang 服务的 runtime.execute 函数调用,用以跟踪协程的创建,因此这个服务每次响应 API 的 4 个调用都会触发一次 Hook。

所有上述三个场景,我们均会分别测试停用 deepflow-agent(基线)、运行 deepflow-agent两种情况,通过对比得出 Agent 对业务性能的影响。另外,我们也会注入不同 TPS 的压力,直至达到业务极限处理能力,以评估不同压力下的影响是否存在差异。

另一方面,对于 Agent 自身处理性能的评估,我们会记录场景 A-C 中 deepflow-agent 进程的 CPU/MEM 开销。除此之外,我们也希望设计一些更极端的场景,用来评估 Agent 在资源受限情况下的 RPS/CPS/BPS/PPS 极限处理能力。

评估 Agent 自身的处理性能

评估 Agent 自身的处理性能

场景 B - 压测 RPS:我们复用了 Nginx Demo,向他注入极限 RPS 压力,并评估 Agent 在消耗一个逻辑核的场景下能处理多大 RPS 的 HTTP 流量。

场景 D - 压测 CPS:我们编写了一个高并发 Flow 的流量生成器[5]。这个生成器会在 tcp_clienttcp_server 两个进程之间构建超过 100K CPS 的活跃并发连接,用以压测 Agent 中的 flow_map 模块,构造高内存压力。

场景 E - 压测 BPS:我们使用 iperf3 构造超过 20Gbps 的大流量,用以评估 Agent 对大吞吐流量的处理性能。

场景 F - 压测 PPS:我们编写了一个 UDP Flood 流量生成器[6]。这个生成器会从 udp_flood 进程发出超过 1Mpps 的流量,用以压测 Agent 对高频 Packet Data 的处理性能。

上述四个场景,我们均会注入最高的压力,使得 Agent 的 CPU/MEM 之一达到 1C/1GB 的高水位。

接下来,我们将会对所有上述六个场景的测试方法和结果进行详细的阐述,测试过程中 DeepFlow Agent 全部使用默认配置,没有进行任何调优。自动化测试流程每天都会执行本文介绍的测试例,并将测试结果与 Git Branch、CommitID 关联,以帮助开发者评估新功能对 Agent 的性能影响。

03 | Agent 对业务的影响

不同业务场景下 Agent 采集的 Span 数量

不同业务场景下 Agent 采集的 Span 数量

场景 A - 典型云原生微服务 Istio Bookinfo:我们使用 wrk2[7] 来注入稳定的 TPS 负载,wrk2 会直接请求 Productpage 服务。所有的服务(包括 wrk2)部署在一个 8C16GB 的 K8s 节点上(CentOS 7、Kernel 4.19),我们在该节点上部署 deepflow-agent Daemonset 来对所有调用进行采集,测试过程中限制了 deepflow-agent 资源消耗为 1C768MB。

基线场景下我们为 Bookinfo 注入了消耗整机 52.61% CPU(相当于 4.2 个逻辑核)的高负载请求,可以看到这是一个非常繁忙的业务场景,为了达到该负载我们甚至特意增加了其中两个瓶颈服务的副本数:将 Productpage 调整为 4 副本、将 Details 调整为 2 副本。在这样的高负载下:

  • 运行 deepflow-agent 前后,TPS 没有任何影响,均为 300/s

  • 运行 deepflow-agent 前后,CPU 仅有 0.46% 的增长,几乎感知不到,可以认为是统计误差

  • P50/P90 RT 分别有约 10ms 的增长,增长比例约为 12%
    • 对于每个事务,DeepFlow 采集了 37 个 Span,因此 300 TPS 负载下的 Span 采集速率为 11,100/s

    • 每个事务包括 26 个 SYS Span(eBPF)和 11 个 NET Span(cBPF)

    • 每个事物共采集了 13 个 Server-side SYS Span,因此平均每个调用仅引入了 0.8ms 的 RT 增长

DeepFlow 通过 eBPF 采集这些 Span,基于创新的关联算法实现了零插桩的分布式追踪,Span 之间的关联信息也是由 Agent 实时计算得到的:

DeepFlow 零插桩分布式追踪 - Istio Bookinfo

DeepFlow 零插桩分布式追踪 - Istio Bookinfo

该场景下的部分测试数据如下(更多测试数据请查看 DeepFlow 在线 Demo 中的 Grafana Dashboard):

 TPSBookinfo CPUP50 RTP90 RT
无 Agent30052.61%69.76ms87.81ms
有 Agent30052.85%80.06ms98.56ms
变化幅度0+ 0.24%+ 10.30ms+ 10.75ms
变化比例0+ 0.46%+ 14.76%+ 12.24%

场景 B - 极高性能极简业务 Nginx:我们依然使用 wrk2 来注入稳定的 TPS 负载,wrk2 会直接请求 Nginx 提供的 Default Page 服务。为了减少其他业务的干扰,我们将 Nginx 和 wrk2 部署在两个单独的虚拟机上(8C16GB、CentOS 7、Kernel 4.19),将 Nginx 的 worker 数量固定为 1 个。我们在 Nginx 所在虚拟机上部署了 deepflow-agent,测试过程中限制了 deepflow-agent 资源消耗为 1C768MB。

基线场景下我们为 Nginx 注入了消耗单核 56.50% CPU 的高负载请求,可以看到这是一个非常繁忙的业务场景。在这样的极端高性能和高负载下:

  • 运行 deepflow-agent 前后,TPS 没有任何影响,均为 40,000/s

  • 运行 deepflow-agent 前后,Nginx CPU 增长了 26.60%,另外 deepflow-agent 消耗了 86.17% CPU
    • 每次调用,deepflow-agent 会采集两个 Span,即 Span 采集速率为 80,000/s

    • 这意味着 deepflow-agent 处理两倍调用消耗的 CPU = 86.17+26.6 = 112.77 = 2 倍 Nginx 基线状态下的 CPU 消耗

    • 也就是说,deepflow-agent 的处理性能与 Nginx 相同

  • P50/P90 RT 分别仅有 0.13ms/0.22ms 的增长,增长比例约为 12%

该场景下的部分测试数据如下(更多测试数据请查看 DeepFlow 在线 Demo 中的 Grafana Dashboard):

 TPSNginx CPUP50 RTP90 RTAgent CPU
无 Agent40,00056.50%1.02ms1.68ms--
有 Agent40,00083.10%1.15ms1.90ms86.17%
变化幅度0+ 26.60%+ 0.13ms+ 0.22ms--
变化比例0+ 47.08%+ 12.75%+ 13.10%--

上述测试结果是在 Agent 默认配置下测得的。实际上由于 Agent 在全栈路径上不同位置采集到的 Span 中 request_domain、request_resource、response_result 等字段不会有变化,因此我们可以开启 Agent 的浅层解析采集模式,针对 HTTP 流量仅解析首行判断协议类型和响应码。所有调用日志均开启浅层解析后,Agent 的性能表现为:

  • 对 Nginx P50/P90 RT 的影响降低到 7.27%

  • deepflow-agent 自身 CPU 消耗降低到 66.5%
    • 采集 80K RPS HTTP 数据消耗 66.5%,加上 Nginx CPU 增长的 25.50% CPU,总消耗为 92%

    • 即 DeepFlow Agent 浅层解析时的处理能力为 Nginx 基线能力的 1.2 倍

浅层解析时的部分测试数据如下:

 TPSNginx CPUP50 RTP90 RTAgent CPU
有 Agent(浅层解析)40,00082.00%1.10ms1.80ms66.5%
变化幅度0+ 25.50%+ 0.08ms+ 0.12ms--
变化比例0+ 45.13%+ 7.27%+ 7.27%--

场景 C - 用 uprobe 追踪 Golang:我们使用 wrk2 来注入稳定的 TPS 负载,wrk2 会直接请求 go-server 提供的服务。我们将 wrk2 和所有服务部署在一个虚拟机上(8C16GB、CentOS 7、Kernel 4.19),除了 wrk2 以外所有服务均使用 docker-compose 部署在 container 中。同样这台虚拟机上也部署了 deepflow-agent。测试过程中限制了 deepflow-agent 资源消耗为 1C768MB。

DeepFlow 中通过 eBPF uprobe 跟踪 Golang 协程的创建来实现零插桩的分布式追踪。为了无差异的让 uprobe 覆盖到每一个调用(HTTP、MySQL、Redis),本 Demo 中我们没有注入 HTTP2/HTTPS 流量。

基线场景下我们为 go-server 注入了消耗单核 35.22% CPU 的请求负载。在这样的负载下:

  • 运行 deepflow-agent 前后,TPS 没有任何影响,均为 140/s
    • 每个事务采集了 15 个 Span,因此 140 TPS 负载下的 Span 采集速率为 2100/s

  • 相比其他场景,运行 deepflow-agent 后 CPU 和 RT 增长略大
    • 运行 deepflow-agent 前后,业务 CPU 增长了 9.58%,增长比例为 27.20%

    • 运行 deepflow-agent 前后,业务 P50/P90 RT 分别有约 0.6ms 的增长,增长比例约为 17%

我们看到 uprobe 的性能开销确实要略高于前两个场景(仅使用了 tracepoint/kprobe/AF_PACKET)。实际上每一次 uprobe Hook 触发会引起两次用户态和内核态间的上下文切换,每个事务中我们 Hook 的 golang 函数触发了 4 次,因此会导致每个事务中增加 8 次上下文切换。

但值得提到的是,DeepFlow Hook 的 Golang 函数是经过深思熟虑的。我们使用 runtime.execute 来跟踪协程和线程之间的关系,这是我们目前能找到的性能影响最低的途经。作为对比 Pixie Hook 了 runtime.casgstatus 函数(未用于分布式追踪,仅用来获取协程 ID),它的调用频率会明显高于 runtime.execute。在对比测试中我们发现,如果使用 Pixie 的方案,在当前场景下将会造成业务服务高达 67% 的 CPU 增长、高达 45% 的 RT 增长,对业务的影响是 DeepFlow 的 2.5 倍

该场景下的部分测试数据如下(更多测试数据请查看 DeepFlow 在线 Demo 中的 Grafana Dashboard):

 TPSGo-server CPUP50 RTP90 RT
无 Agent14035.22%3.56ms3.95ms
有 Agent14044.80%4.18ms4.60ms
变化幅度0+ 9.58%+ 0.62ms+ 0.65ms
变化比例0+ 27.20%+ 17.42%+ 16.46%

测试过程中我们也对业务的内存开销进行了监控,但未观察到任何影响,考虑到 DeepFlow 从原理上来讲不会增加业务进程的内存开销,因此我们没有呈现内存消耗数据。

04 | Agent 自身处理性能

在前一个章节的场景 A-C 中,我们同时也记录了 Agent 在高负载下的资源消耗,见下表(其中场景 A 的 CPU 消耗为整机百分比,共 8 核,其他场景为单核百分比):

场景业务 TPS业务基线 CPUSpan 采集速率Agent CPUAgent 内存
A - Istio30052.61%11,1003.40%46.76 MB
B - Nginx40,00056.50%80,00086.17%15.36 MB
B - Nginx(浅层解析)40,00056.50%80,00066.50%15.36 MB
C - uprobe14035.22%2,10010.20%43.26 MB

从上表可以看到:

  • 场景 A:Agent 自身的 CPU 消耗仅为整机的 3.4%(相当于单核的 27%),相比业务消耗微乎其微

  • 场景 B:Agent 运行引发的额外 CPU 消耗与 Nginx 的基线 CPU 消耗相当(归一化至处理同样 RPS 数据之后),浅层解析下 Agent 的处理能力是 Nginx 的 1.2 倍

  • 场景 C:Agent 自身的 CPU 消耗仅为单核的 10.20%

  • 各个场景下的内存开销极低

除了评估 Agent 在业务高负载情况下的资源消耗,我们也通过如下四个场景来评估 Agent 在 1C1G 资源限制下的极限处理能力。

场景 B - 使用 Nginx 压测 RPS:我们通过 wrk2 注入了极端的 45K/s TPS 压力,此时 Nginx 的基线 CPU 消耗已高达 62.39%,可以看到这是一个非常极端的场景。在这样的场景下我们得到了 Agent 在 1C 限制下的 HTTP 流量极限采集能力 —— 90K/s

该场景下的部分测试数据如下(更多测试数据请查看 DeepFlow 在线 Demo 中的 Grafana Dashboard):

TPS采集 Span/sAgent CPUAgent CPU(浅层解析)Agent MEM
45,00090,00098%77%15MB
40,00080,00086%67%15MB
35,00070,00075%58%15MB
30,00060,00063%50%15MB

场景 D - 使用 TCP client/server 压测 CPS:我们使用 tcp_client 产生了 122K 个 IP-Port 五元组不同的、持续活跃的 Flow,新建连接速率约为 1K/s。此时 Agent 的内存消耗已达到 1GB,具体性能数据见下表。

Agent CPUAgent 内存流量 BPS流量 PPS流量 CPS
35.00%1079.77 MB23.43Mbps40.66Kpps121,771

场景 E - 使用 iperf3 压测 BPS:我们使用 iperf3 产生了 20Gbps 的流量。这个场景下我们并没有压到 Agent 的极限,20Gbps 已经是我们能在测试虚拟机中构造出的最大流量了。具体性能数据见下表。

Agent CPUAgent 内存流量 BPS流量 PPS
10.54%124.03M20.08Gbps114.51Kpps

场景 F - 使用 udp_flood 压测 PPS:我们使用 udp_flood 产生了 1.28Mpps 的流量。这个场景下将 Agent CPU 压到了单核的 95%。具体性能数据见下表。

Agent CPUAgent 内存流量 BPS流量 PPS
95.86%122.97M1.04Gbps1.28Mpps

05 | 总结

通过六个场景的全方位评测,我们对 Agent 的性能有了完整的了解,希望能够帮助大家尽快落地基于 eBPF 的可观测性。简要结论总结如下:

  • 对于一个典型的微服务架构的云原生业务,在注入整机 52% 高负载的压力下:
    • Agent 对业务 TPS 没有任何影响

    • Agent 使得业务 P50 RT 仅增加了 10.30ms(+14.76%),平均每个调用的 RT 仅增加 0.8ms

    • Agent 使得业务 CPU 仅增长 0.24%(+0.46%),几乎感知不到

    • Agent 自身仅消耗 3.4% CPU、47 MB 内存

  • 对于一个极致性能的极简业务(Nginx Default Page),在注入单核 56% 高负载的压力下:
    • Agent 对业务 TPS 没有任何影响

    • Agent 使得业务 P50 RT 仅增加了 0.13ms(+12.75%)
      • 浅层解析时,业务 P50 RT 仅增加 0.08ms (+7.27%)

    • Agent 的处理性能等于 Nginx,处理等量 HTTP 调用的资源开销与 Nginx 相同
      • 浅层解析时,Agent 处理性能可高达 Nginx 的 1.2 倍

  • 对于需要使用 eBPF uprobe 的业务场景(Golang 协程跟踪),在注入单核 35% 负载的压力下:
    • Agent 对业务 TPS 没有任何影响

    • Agent 使得业务 P50 RT 仅增加了 0.62ms(+17.42%)

    • Agent 使得业务 CPU 增长了 9.58%(+27.20%),主要由 uprobe 的内核态-用户态上下文切换引入

    • 对比:此场景下 DeepFlow 的性能为 Pixie 方案的 2.5 倍

  • 在资源受限为 1核 CPU 1GB 内存的情况下,Agent 的极限处理性能如下
    • RPS:HTTP 采集性能 90K RPS,此时 Agent 消耗单核的 98% CPU

    • CPS:采集 并发 122K CPS、新建 1K CPS 的 TCP Flood 流量,Agent 消耗 1GB 内存

    • BPS:采集 20Gbps TCP Flood 流量,Agent 仅消耗单核的 10% CPU,仅消耗 124MB 内存

    • PPS:采集 1.28Mpps UDP Flood 流量,Agent 消耗单核的 96% CPU

最后,本文所有测试数据(除浅层解析外)均是在 Agent 默认配置下测得的。实际业务环境中对于每一个调用,Agent 通常会在进程、Pod 网卡、Node 网卡三个位置采集到三个 Span,你可以按需关闭某些位置的数据采集,以获得更好的性能表现。理论上当仅采集其中一份数据时,你可以获得 3 倍于本文的性能表现。我们也期待社区小伙伴的更多评测。Enjoy DeepFlow!Enjoy Zero Code Observability!

06 | 什么是 DeepFlow

DeepFlow[8] 开源项目旨在为复杂的云原生应用提供深度可观测性。DeepFlow 基于 eBPF 实现了零插桩(Zero Code)、全覆盖(Full Stack)的指标、追踪、日志采集,并通过智能标签技术实现了所有观测数据的全关联(Universal Tagging)和高效存取。使用 DeepFlow,可以让云原生应用自动具有深度可观测性,从而消除开发者不断插桩的沉重负担,并为 DevOps/SRE 团队提供从代码到基础设施的监控及诊断能力。

GitHub 地址:https://github.com/deepflowio/deepflow

访问 DeepFlow Demo[9],体验零插桩、全覆盖、全关联的可观测性。

参考资料

[1] 线上 Demo 页面中: https://ce-demo.deepflow.yunshan.net/d/Agent_Performance_Analysis/agent-performance-analysis

[2] Istio Bookinfo Demo: https://istio.io/latest/docs/examples/bookinfo/#deploying-the-application

[3] Brendan Gregg 在他的著作《BPF 之巅 - 洞悉 Linux 系统和应用性能》: https://www.brendangregg.com/bpf-performance-tools-book.html

[4] Golang Service 的 Demo: https://github.com/nrjatyunshan/go-server-sample

[5] 高并发 Flow 的流量生成器: https://github.com/deepflowio/traffic-generators/tree/main/1-high-connection

[6] UDP Flood 流量生成器: https://github.com/deepflowio/traffic-generators/tree/main/2-udp-flood

[7] wrk2: https://github.com/giltene/wrk2

[8] DeepFlow: https://github.com/deepflowio/deepflow

[9] DeepFlow Demo: https://deepflow.io/docs/zh/install/overview/

 

展开阅读全文
  • 0
    感动
  • 0
    路过
  • 0
    高兴
  • 0
    难过
  • 0
    搞笑
  • 0
    无聊
  • 0
    愤怒
  • 0
    同情
热度排行
友情链接