本文为 DeepFlow 在首届中国 eBPF 大会上的在线演讲实录。
大家好!我是来自云杉网络的向阳,负责公司的云原生可观测性产品 DeepFlow。今天很高兴能给大家分享我们基于 eBPF 的一些创新实践,我们的产品已经有六年历史,并且开源,希望能为云原生应用开发者实现可观测性带来高度自动化的全新体验。
欢迎大家预约近期“云原生可观测性分享会”,分享 DeepFlow 使用 eBPF 为分布式追踪带来的革命性创新。借助 eBPF 的零侵扰机制,开发者无需修改代码、无需重新发布、无需重启服务,即可实现全景、全栈的分布式追踪能力,覆盖各类语言的微服务及基础设施。
01|历史和现状
最近可观测性是一个很火的名词,这里我也想稍微介绍一下它的历史。追溯历史我们会发现,实际上可观测性是作为保障复杂系统可控的必要条件。61 年前,美国提出载人登月的豪言壮语,但届时如何将高度复杂的载人飞船平稳地送到月球还只是人类的一个梦想。8 年之后,受益于 Rudolf Kalman 教授开创的可观测性理论,人类终于完成了登月壮举。Kalman 教授指出,一切系统具有可观测性当且仅当:针对所有的状态向量及控制向量,都可以在有限时间内,只根据输出信号来识别当前的状态。注意这里面的关键:外部输出、内部状态、有限时间。
可观测性的历史
时间回到 2022 年,现在的云原生应用正是一个复杂度急剧增长的 IT 系统。随着微服务的拆分,单个服务变得越来越简单,服务发布越来越快速,业务中的通用逻辑逐渐卸载至服务网格等基础设施,开发语言和框架越来越自由。这些趋势使得一方面服务的数据急剧增长,但更棘手的问题是连接服务的基础设施路径变得越来越长,路径数量越来越多。开发者们发现,用于采集指标、追踪、日志三大支柱信号的已有方案几乎都聚焦在业务代码和框架代码层面,而对于 N^2 复杂度的进程、Sidecar、节点、宿主机、网关、数据库、消息队列就像黑盒一样,虽然能暴露一些有限的指标和日志,但是和应用是割裂的,应用开发者越来越难以观测 IT 系统的运行状况,其中面临的挑战也正是可观测性理论所指出的:1)微服务的动态性对实时性提出的高要求;2)路径的复杂性对多维度提出的高要求;3)服务实现的复杂性对零侵扰的提出的高要求。
云原生应用是一个复杂系统
DeepFlow 就是希望来解决云原生应用可观测性问题的,希望能让开发者轻松实现全栈可观测性。下图左上角是一个非常“简单”的复杂云原生业务系统,它是 OpenTelemetry 项目的官方 Demo,是一个用数十种语言实现的高速迭代的电商应用。DeepFlow 使用 OpenTelemetry、Prometheus 等标准化的采集方式,实现应用代码层面的可观测性,同时使用 eBPF 技术,创新的实现了云原生环境中高度自动化的全栈可观测性,得益于 eBPF 的零侵扰特性,不需要开发人员插码、重启进程、修改配置。今天我的分享主要聚焦在 eBPF 的部分,主要介绍 DeepFlow 三个方面的能力:
-
从 cBPF 到 eBPF 的 AutoMetrics 能力,实现面向微服务的动态应用拓扑;
-
从 InProcess 到 Distributed 的 AutoTracing 能力,实现面向用户请求的零侵扰分布式追踪;
-
从 kprobe 到 uprobe 的 AutoLogging 能力,实现面向事件的细粒度回溯日志。
02|AutoMetrics
首先我们来看一下 DeepFlow 是如何使用 eBPF 的。下面这张图描述了云原生应用的运行环境,应用进程运行在 Pod 内,可能伴随着例如 Envoy 之类的 Service Mesh Sidecar,通过 iptables、ipvs 等组件接入到虚拟机 Overlay 网络中,继续通过 OvS、Linux Bridge 等组件接入到宿主机 Underlay 网络中,还可能会由一系列四七层网关为其服务。DeepFlow 同时用到了 cBPF 和 eBPF 的能力,使得能覆盖逐跳路径实现全栈可观测性。第一步是采集,我们会从 cBPF 采集网卡流量、eBPF kprobe/tracepoint 采集 Syscall 数据、eBPF uprobe/USDT 采集 Function 数据。拿到这些数据后,第二步是聚合,从 Raw Data 中聚合得到网络流和应用会话,以降低数据量。第三步继续从 Flow 和 Session 聚合生成全栈性能指标。第四步会从网络流和应用会话中提取结构化日志,包括 FlowLog 和 RequestLog,其中后者可认为就是我们常说的 Span。最后第五步,利用一系列算法将 Span 关联生成分布式追踪,这一步也是 DeepFlow 中一项颠覆性的创新。
DeepFlow 如何使用 eBPF
那么下面我来介绍 DeepFlow 的第一部分能力:从 cBPF 到 eBPF 的 AutoMetrics 能力,实现面向微服务的动态应用拓扑。也就是上图中的第三步。
我们来看一下效果,AutoMetrics 能力能做什么呢?我们用户大量反馈的第一个价值是自动绘制微服务动态应用拓扑,知晓任意微服务的上下游访问关系。云原生环境下业务混部共享一个服务器、容器 SNAT 等是普遍现象,以往我们需要靠业务插码解决的事情,现在通过 DeepFlow 可以很快速完成。比如我们一个互联网用户,使用 DeepFlow 从数万个 Pod 中快速定位了对它的 RDS 造成最大访问量的 Pod、服务乃至开发团队。就算在应用代码中插码,有些问题也难以回答,比如我们的金融用户信用卡核心业务上云受阻,使用 DeepFlow 快速发现了两个服务之间 API 网关是性能瓶颈,而这个“透明”的服务通常是被开发者所忽略的。
我在访问谁、谁在访问我
除了拓扑本身之外,DeepFlow 对拓扑中每一条路径的全栈观测能力是另一个亮点。下图左下角是两个 Pod 之间最普通的访问路径,这里面的进程、Pod、Node、KVM 宿主机都可能引发应用性能问题。DeepFlow 由于全栈覆盖了服务之间的全路径,能够提供沿途每一跳的丰富性能指标。例如应用性能的请求/时延/异常,即我们常说的 RED 三大黄金指标;系统性能的新建/活跃/建连异常;网络性能的建连时延/系统时延/数据时延/吞吐/零窗/重传/载荷长度等。我们支持对主流的应用协议进行识别,包括 HTTP/HTTPS、Dubbo/gRPC、MySQL/PostgreSQL/Redis、Kafka/MQTT、DNS 等。使用这个能力,DeepFlow 的用户能快速定位应用请求在哪里出问题了。例如我们一个互联网用户,快速定位了两个服务之间 K8s 网络性能瓶颈造成的应用时延;一个金融用户,快速定位了 ARP 异常导致的 Pod 概率性长时间无法 Ready;一个系统软件用户,快速定位了客户端没有及时收包导致的 gRPC 超时。得益于 eBPF 的零侵扰特性,这些定位过程不需要对线上应用做任何改变。
DeepFlow 中的全栈访问路径
刚才还只是最简单的场景,而在专有云网络中,访问路径会更加复杂,DeepFlow 的能力也更加丰富。DeepFlow 与阿里专有云、腾讯 TCE、华为 HCS 已经合作超过三年,进行了深度的适配。云网络中最复杂的就是 NFV 区域,可能存在十余种云网关、四五种种隧道封装协议、十余种通信端点,组合起来形成上百种穿越 NFV 区的路径。以往大家在排查应用问题时只能逐个抓包确认,过程非常痛苦,技能养成靠着老师傅传帮带。基于网关区 cBPF 获取到的流量,DeepFlow 将应用访问穿越网关区的整个路径智能推导出来,无论中间经历过多少次 SNAT、DNAT、FULLNAT,都能关联至上一页我讲到的云原生应用访问路径中。这里的路径追踪方法,等会在第二部分的分布式追踪中会做一个介绍。下图右下角是我们基于 NAT 追踪能力绘制的流量拓扑和链路拓扑。利用这个能力我们也有很多用户案例,我们一个金融用户,使用 DeepFlow 解决了困扰云厂商工程师多日的路由环路,它造成了某个服务上云下云时延周期性飙升;另一个金融用户快速定位了一个 NFV 网关实例对一组服务流量丢包,导致的客户端频繁重试。
追踪专有云中 NFV 网关区域的访问路径
03|AutoTracing
刚才介绍的是指标方面的能力,下面来介绍一下从 InProcess 到 Distributed 的 AutoTracing 能力,实现面向用户请求的零侵扰分布式追踪。这是 DeepFlow 中一项颠覆性的创新。
我们以一个 Demo 为例,左下角是 Istio 官方的 Bookinfo Demo,包含五种语言实现的微服务和 Sidecar,服务存在多个版本体现了迭代速度之快,所有服务使用 Service Mesh 连接。以往为了实现这样一个简单应用的分布式追踪,我们不得不在各种语言和框架中进行打桩和插码,利用 OpenTelemetry、SkyWalking 技术绘制分布式追踪火焰图。而现在大家看到的这张火焰图是 DeepFlow 完全基于 eBPF 的 AutoTracing 能力得到的,覆盖了一次用户请求的整个生命周期,以及每一个微服务调用从客户端进程到服务端进程的全路径,而所有这些结果不依赖对应用做任何插码、重启、配置。相信云原生开发者看到这个结果时是非常兴奋的。
对 Istio Bookinfo Demo 的零侵扰分布式追踪
基于这个火焰图,我们来详细的展示一下 DeepFlow 的 AutoTracing 能力。首先 AutoTracing 是零插码的,它不需要修改应用代码,不需要向 HTTP Header 中注入 TraceID、SpanID。这张火焰图共由 38 个 Span 组成,包括 24 个 eBPF Span 和 14 个 cBPF Span,覆盖了一次应用请求的全链路。火焰图中覆盖了 Java、Python、Ruby、Node.js、C、C++ 六种语言实现的微服务和网关,全部都能自动呈现在火焰图中。对于我们一直提到的全栈能力,这里也放大两个区域看看。我们看到一个调用从客户端进程,经过 Pod 网卡、Node 网卡,甚至是 KVM、NFVGW 等到达服务端进程的全过程,无论中间路径如何复杂,添加过几层隧道、做过几次 NAT,都能清晰的展现在火焰图中,快速判断服务之间的性能问题是由哪个环节导致,并且结合之前介绍的 DeepFlow 指标体系,火焰图可以快速关联到应用、系统、网络各个层面的指标。再来看看另一个区域,我们看到一个调用从进入 Pod 被 Envoy 劫持,到被微服务接收,到微服务继续请求下游并被 Envoy 劫持,在一个 Pod 内的全栈路径也能非常清晰的体现出来。除此之外,我们也能发现以往应用代码插桩难以覆盖的一些基础服务,例如微服务对 DNS 解析、磁盘 IO 等过程在这个火焰图中也体现了出来。基于这样的全栈分布式追踪能力我们也有很多用户案例,例如一个互联网用户快速定位客户端慢而服务端不慢的经典扯皮问题。
DeepFlow AutoTracing
AutoTracing 是 DeepFlow 中一个开创性的能力,这个能力基于 eBPF,它与大家所熟知的一个进程内部的 Trace 不同,解决的是微服务分布式调用的追踪问题。这里我尝试简单的介绍一下我们的方法。实际上我们只需要解决两个子问题:
-
如何追踪一个服务前后的入出请求;
-
如何追踪两个服务之间的网络路径。
对于第一个问题,又分为线程内追踪、跨线程追踪、跨协程追踪。DeepFlow 利用 ThreadID ,以及为每个 Request 生成的 SyscallTraceID 关联一个线程内的入向和出向请求,完美的解决了同线程的追踪问题;跨线程有些复杂,基于为每个会话生成的 SessionID 和包头中提取的 X-Request-ID,我们目前解决了一部分场景,相信未来能搞定越来越多的异步调用;对于 Golang/Erlang 等协程语言同样也非常困难,我们追踪了 CoroutineID 之间的关系,这部分代码也正在合入中,预计在我们的 v6.2.0 版本中和大家见面。
那么接下来如何追踪两个服务之间的网络路径呢,简单来讲使用了 TCP Seq 序列号,并结合时间窗口等信息。虽然原理简单,但其中有很多细节的工程问题需要处理,以实现用户函数调用、系统函数调用、网络流量的关联。时间关系没法展开,如果大家感兴趣欢迎到 DeepFlow 社区群中交流。
DeepFlow 如何实现分布式追踪
04|AutoLogging
最后我们来介绍一下 DeepFlow 中的日志能力,从 kprobe 到 uprobe 的 AutoLogging 能力,实现面向事件的细粒度回溯日志。
由于我们已经从 cBPF 和 eBPF 中拿到了所有微服务沿途的全栈数据,通过 Flow 和 Session 聚合,我们能提取出标准化、结构化的回溯日志。例如对应用调用日志我们能提取请求域名、Endpoint、请求 ID、响应码、响应结果,也能提取 HTTP Header 中常见的客户端真实 IP、UserAgent、Referer 等字段。对于已经在应用内插码追踪的场景,也能从 cBPF 和 eBPF 数据中提取出 TraceID、SpanID 等字段以关联应用 Span。协议解析和字段提取的能力我们也正在开发通过 WASM 和 LUA 提供插件式的扩展能力,使得用户可以解析和提取私有协议字段。除了调用日志以外,我们也采集了网络流日志。在企业版中我们还支持以 1/10 的存储代价压缩存储所有 TCP 包头,以及压缩存储全包 PCAP 文件。
这方面也有很多我们的用户案例,例如政府用户使用 DeepFlow 流日志替代全包存储,回溯查询的速度和存储时长都实现了数量级的提升;金融用户使用 TCP 包头时序图快速发现了云网关转发 SYN 报文时延大导致的应用性能衰减。
DeepFlow AutoLogging
下图是 DeepFlow 当前的所有 eBPF hook 点,GitHub 上直接搜索 DeepFlow 即可找到我们的仓库,这里也列出了几条命令帮助大家查看 DeepFlow 目前使用到的主要 uprobe、kprobe、tracepoint hook 点。
DeepFlow eBPF Hook 点
05|数据的标准化
好了,讲完了指标、追踪、日志,最后来给大家介绍一下 DeepFlow 中的标签注入能力。大家可能也关注到了前面我所讲的内容中,DeepFlow 能以非常丰富的维度展现数据,我们通过 AutoTagging 机制为所有的可观测性信号注入丰富的标签,包括资源池、云资源、网络资源、容器服务、应用等相关的标准标签,和 K8s Label、Annotation、ENV 等开发者自定义的标签。依靠这些标签我们能快速的在不同类型的可观测性信号之间切换,无论是 eBPF 采集到的数据,还是通过 Prometheus、OpenTelemetry 采集到的数据,完全消除不同数据源之间的孤岛。数据的标准化也有助于进一步的消费,DeepFlow 的用户消费这些数据时,不再需要考虑复杂的标准化过程,我相信这也是实现智能化的前提。
DeepFlow AutoTagging 及 SmartEncoding
大家可能会比较好奇,插入这么多标签是否会造成大量的资源消耗,这里就要介绍 DeepFlow 的 SmartEncoding 机制了,简单来讲通过标签和数据的分离采集、标签的数值编码、以及自定义标签的读时关联,我们能将标签写入的资源消耗降低 10~50 倍。这里有一些数据可以看一下,DeepFlow 默认使用 ClickHouse 存储数据,在 SmartEncoding 的加持下,标注 Tag 的 CPU 和磁盘消耗相比 LowCard 存储或直接存储有一个数量级的优化,而由于自定义 Tag 不会随数据写入,在通常的场景下整体写入资源消耗可降低 50 倍。因此实际上 DeepFlow 实现了 Tag without Limit,可以让开发者尽情的注入丰富的标签,充分挖掘数据的价值。
最后,简单介绍一下 DeepFlow 的软件架构。DeepFlow 希望让云原生开发者能够轻松实现全栈可观测性,这个产品我们做了六年,今年七月份正式开源,目前已经是 CNCF Cloud Native Landscape Project 和 eBPF Landscape Project。从下图中可以看到 DeepFlow 社区版的架构非常精炼,主要包括 Agent 和 Server 两个进程,前者是 Rust 实现的,后者是 Golang 实现的。Agent 使用 eBPF 的能力实现 AutoMetrics、AutoTracing、AutoLogging 能力,并基于 WASM 提供可编程性。Server 使用 AutoTagging 和 SmartEncoding,基于 Agent 采集到的元数据,向所有可观测性信号中高性能的注入标准化的标签。除了 eBPF 之外,DeepFlow 还使用 Prometheus、OpenTelemetry、SkyWalking 等开放 Agent 采集数据,并且和 eBPF 的数据进行关联。向上我们提供了易于使用的 SQL API、Grafana Datasource 和 Query Editor,未来也会支持 PromQL 等流行的查询语言。
DeepFlow 社区版软件架构
如果大家对 DeepFlow 感兴趣,可以扫码加入我们的开源社区微信群,希望能给你带来全新的可观测性体验。谢谢大家!
06|什么是 DeepFlow
DeepFlow[1] 是一款开源的高度自动化的可观测性平台,是为云原生应用开发者建设可观测性能力而量身打造的全栈、全链路、高性能数据引擎。DeepFlow 使用 eBPF、WASM、OpenTelemetry 等新技术,创新的实现了 AutoTracing、AutoMetrics、AutoTagging、SmartEncoding 等核心机制,帮助开发者提升埋点插码的自动化水平,降低可观测性平台的运维复杂度。利用 DeepFlow 的可编程能力和开放接口,开发者可以快速将其融入到自己的可观测性技术栈中。
GitHub 地址:https://github.com/deepflowys/deepflow
访问 DeepFlow Demo[2],体验高度自动化的可观测性新时代。
参考资料
[1] DeepFlow: https://github.com/deepflowys/deepflow
[2] DeepFlow Demo: https://deepflow.yunshan.net/docs/zh/install/overview/