DeepFlow 简介

# 1. 什么是 DeepFlow

DeepFlow 是云杉网络 (opens new window)开发的一款可观测性产品,旨在为复杂的云基础设施及云原生应用提供深度可观测性。DeepFlow 基于 eBPF 实现了应用性能指标、分布式追踪、持续性能剖析等观测信号的零侵扰Zero Code)采集,并结合智能标签SmartEncoding)技术实现了所有观测信号的全栈Full Stack)关联和高效存取。使用 DeepFlow,可以让云原生应用自动具有深度可观测性,从而消除开发者不断插桩的沉重负担,并为 DevOps/SRE 团队提供从代码到基础设施的监控及诊断能力。

为了鼓励全球可观测性领域的开发者和研究者能做出更多的创新和贡献,DeepFlow 核心模块已使用 Apache 2.0 License 开源 (opens new window),并在网络通信领域国际顶级会议 ACM SIGCOMM 2023 上正式发表了学术论文 (opens new window)《Network-Centric Distributed Tracing with DeepFlow: Troubleshooting Your Microservices in Zero Code》。

# 2. 核心特性

  • 任意 Service 的全景图:基于领先的 AutoMetrics 机制,利用 eBPF 技术零侵扰绘制生产环境的服务全景图,包括任意语言开发的服务、未知代码的第三方服务、所有的云原生基础设施服务。内置大量应用协议解析能力,并提供 Wasm 插件机制扩展解析任意私有协议。零侵扰计算每一次调用在应用程序和基础设施中的全栈黄金指标,快速定界性能瓶颈。
  • 任意 Request 的分布式追踪:基于领先的 AutoTracing 机制,利用 eBPF 和 Wasm 技术零侵扰实现分布式追踪,支持任意语言的应用程序,并完整覆盖网关、服务网格、数据库、消息队列、DNS、网卡等各类基础设施,不留下任何追踪盲点。全栈,自动采集每个 Span 关联的网络性能指标和文件读写事件。从此,分布式追踪进入零插桩的新时代。
  • 任意 Function 的持续性能剖析:基于领先的 AutoProfiling 机制,利用 eBPF 技术以低于 1% 的开销零侵扰采集生产环境进程的性能剖析数据,绘制函数粒度的 OnCPU、OffCPU 火焰图,快速定位应用函数、库函数、内核函数的全栈性能瓶颈,并自动关联至分布式追踪数据。即使在 2.6+ 内核版本下,仍然可提供网络性能剖析能力,洞察代码性能瓶颈。
  • 无缝集成流行的可观测性技术栈:可作为 Prometheus、OpenTelemetry、SkyWalking、Pyroscope 的存储后端,亦可提供 SQL、PromQL、OTLP 等数据接口作为流行技术栈的数据源。基于领先的 AutoTagging 机制,自动为所有观测信号注入统一标签,包括云资源、K8s 容器资源、K8s Label/Annotation、CMDB 中的业务属性等,消除数据孤岛。
  • 存储性能 10x ClickHouse:基于领先的 SmartEncoding 机制,向所有观测信号注入标准化的、预编码的元标签,相比 ClickHouse 的 String 或 LowCard 方案均可将存储开销降低 10x。自定义标签与观测数据分离存储,从此你可安心注入近乎无限维度和基数的标签,且可获得像 BigTable 一样的轻松查询体验。

# 3. 解决两大痛点

传统方案中,APM 希望通过代码插桩(Instrumentation)的方式来实现应用程序的可观测性。利用插桩,应用程序可以暴露非常丰富的观测信号,包括指标、追踪、日志、函数性能剖析等。然而插桩的行为实际上改变了原始程序的内部状态,从逻辑上并不符合可观测性「从外部数据确定内部状态」的要求。在金融、电信等重要行业的核心业务系统中,APM Agent 落地非常困难。进入到云原生时代,这个传统方法也面临着更加严峻的挑战。总的来讲,APM 的问题主要体现在两个方面:Agent 的侵扰性导致难以落地,观测盲点导致无法定界。

第一,探针侵扰性导致难以落地。插桩的过程需要对应用程序的源代码进行修改,重新发布上线。即使例如 Java Agent 这类字节码增强技术,也需要修改应用程序的启动参数并重新发版。然而,对应用代码的改造还只是第一道关卡,通常落地过程中还会碰到很多其他方面的问题:

  1. 代码冲突:当你为了分布式追踪、性能剖析、日志甚至服务网格等目的注入了多个 Java Agent 时,是否经常遇到不同 Agent 之间产生的运行时冲突?当你引入一个可观测性的 SDK 时,是否遇到过依赖库版本冲突导致无法编译成功?业务团队数量越多时,这类兼容性问题的爆发会越为明显。
  2. 维护困难:如果你负责维护公司的 Java Agent 或 SDK,你的更新频率能有多高?就在此时,你们公司的生产环境中有多少个版本的探针程序?让他们更新到同一个版本需要花多长时间?你需要同时维护多少种语言的探针程序?当企业的微服务框架、RPC 框架无法统一时,这类维护问题还将会更加严重。
  3. 边界模糊:所有的插桩代码严丝合缝的进入了业务代码的运行逻辑中,不分你我、不受控制。这导致当出现性能衰减或运行错误时,插桩代码往往难辞其咎。即使探针已经经过了长时间的实战打磨,遇到问题时也免不了要求排除嫌疑。

实际上,这也是为什么侵扰性的插桩方案少见于成功的商业产品,更多见于活跃的开源社区。OpenTelemetry、SkyWalking 等社区的活跃正是佐证。而在部门分工明确的大型企业中,克服协作上的困难是一个技术方案能够成功落地永远也绕不开的坎。特别是在金融、电信、电力等承载国计民生的关键行业中,部门之间的职责区分和利益冲突往往会使得落地插桩式的解决方案成为「不可能」。即使是在开放协作的互联网企业中,也少不了开发人员对插桩的不情愿、运维人员在出现性能故障时的背锅等问题。在经历了长久的努力之后人们已经发现,侵入性的解决方案仅仅适合于每个业务开发团队自己主动引入、自己维护各类 Agent 和 SDK 的版本、自己对性能隐患和运行故障的风险负责。

第二,观测盲点导致无法定界。即使 APM 已经在企业内落地,我们还是会发现排障边界依然难以界定,特别是在云原生基础设施中。这是因为开发和运维往往使用不同的语言在对话,例如当调用时延过高时开发会怀疑网络慢、网关慢、数据库慢、服务端慢,但由于全栈可观测性的缺乏,网络、网关、数据库给出的应答通常是网卡没丢包、进程 CPU 不高、DB 没有慢日志、服务端时延很低等一大堆毫无关联的指标,仍然解决不了问题。定界是整个故障处理流程中最关键的一环,它的效率至关重要。

如果你是一个业务开发工程师,除了业务本身以外,还应该关心系统调用和网络传输过程;如果你是一个 Serverless 租户,你可能还需要关注服务网格边车及其网络传输;如果你直接使用虚拟机或自建 K8s 集群,那么容器网络是需要重点关注的问题点,特别还需注意 K8s 中的 CoreDNS、Ingress Gateway 等基础服务;如果你是私有云的计算服务管理员,应该关心 KVM 宿主机上的网络性能;如果你是私有云的网关、存储、安全团队,也需要关注服务节点上的系统调用和网络传输性能。实际上更为重要的是,用于故障定界的数据应该使用类似的语言进行陈述:一次应用调用在整个全栈路径中,每一跳到底消耗了多长时间。我们发现,开发者通过插桩提供的观测数据,可能只占了整个全栈路径的 1/4。在云原生时代,单纯依靠 APM 来解决故障定界,本身就是妄念

# 4. 使用 eBPF 技术

假设你对 eBPF 有了基础的了解,它是一项安全、高效的通过在沙箱中运行程序以实现内核功能扩展的技术,是对传统的修改内核源代码和编写内核模块方式的革命性创新。eBPF 程序是事件驱动的,当内核或用户程序经过一个 eBPF Hook 时,对应 Hook 点上加载的 eBPF 程序就会被执行。Linux 内核中预定义了一系列常用的 Hook 点,你也可以利用 kprobe 和 uprobe 技术动态增加内核和应用程序的自定义 Hook 点。得益于 Just-in-Time (JIT) 技术,eBPF 代码的运行效率可媲美内核原生代码和内核模块。得益于 Verification 机制,eBPF 代码将会安全的运行,不会导致内核崩溃或进入死循环。

https://ebpf.io/what-is-ebpf/#hook-overview

https://ebpf.io/what-is-ebpf/#hook-overview

沙箱机制是 eBPF 有别于 APM 插桩机制的核心所在,「沙箱」在 eBPF 代码和应用程序的代码之间划上了一道清晰的界限,使得我们能在不对应用程序做任何修改的前提下,通过获取外部数据就能确定其内部状态。我们来分析下为何 eBPF 是解决 APM 代码插桩缺陷的绝佳解决方案:

第一,零侵扰解决落地难的问题。由于 eBPF 程序无需修改应用程序代码,因此不会有类似 Java Agent 的运行时冲突和 SDK 的编译时冲突,解决了代码冲突问题;由于运行 eBPF 程序无需改变和重启应用进程,不需要应用程序重新发版,不会有 Java Agent 和 SDK 的版本维护痛苦,解决了维护困难问题;由于 eBPF 在 JIT 技术和 Verification 机制的保障下高效安全的运行,因此不用担心会引发应用进程预期之外的性能衰减或运行时错误,解决了边界模糊问题。另外从管理层面,由于只需要在每个主机上运行一个独立的 eBPF Agent 进程,使得我们可以对它的 CPU 等资源消耗进行单独的、精确的控制。

第二,全栈能力解决故障定界难的问题。eBPF 的能力覆盖了从内核到用户程序的每一个层面,因此我们得以跟踪一个请求从应用程序出发,经过系统调用、网络传输、网关服务、安全服务,到达数据库服务或对端微服务的全栈路径,提供充足的中立观测数据,快速完成故障的定界

关于这方面更详细的分析,请参考我们的文章《eBPF 是实现可观测性的关键技术》 (opens new window)

需要强调的是,这并不意味着 DeepFlow 只使用 eBPF 技术。相反,DeepFlow 支持无缝集成流行的可观测性技术栈,例如它可作为 Prometheus、OpenTelemetry、SkyWalking、Pyroscope 等观测信号的的存储后端。

# 5. 使命和愿景

  • 使命:让观测更简单。
  • 愿景:成为云原生应用实现可观测性的首选。