控制理论中的可观测性是指:系统可以由其外部输出确定其内部状态的程度。在复杂 IT 系统中,具备可观测性是为了让系统能达到某个预定的稳定性、错误率目标。随着微服务数量的急速膨胀和云原生基础设施的快速演进,建设可观测性已经成为了保障业务稳定性的必要条件。
然而,传统的 APM 无法实现真正的可观测性:一方面插桩行为已经修改了原程序,逻辑上已无法实现原程序的可观测性;另一方面云原生基础设施组件越来越多,基础服务难以插桩导致观测盲点越来越多。实际上,插桩的方式在金融、电信等重要行业的核心业务系统中几乎无法落地。eBPF 由于其零侵扰的优势,避免了 APM 插桩的缺点,是云原生时代实现可观测性的关键技术。
本文依次论述 APM 无法实现真正可观测性的原因,分析为什么 eBPF 是可观测性的关键技术,介绍 DeepFlow 基于 eBPF 的三大核心功能,并进一步阐述如何向 eBPF 的观测数据中注入业务语义。在此之后,本文分享了 DeepFlow 用户的九大类真实使用案例,总结了用户在采用 eBPF 技术前的常见疑问。最后,本文进一步分析了 eBPF 对新技术迭代的重大意义。
APM 希望通过代码插桩(Instrumentation)的方式来实现应用程序的可观测性。利用插桩,应用程序可以暴露非常丰富的观测信号,包括指标、追踪、日志、函数性能剖析等。然而插桩的行为实际上改变了原始程序的内部状态,从逻辑上并不符合可观测性「从外部数据确定内部状态」的要求。在金融、电信等重要行业的核心业务系统中,APM Agent 落地非常困难。进入到云原生时代,这个传统方法也面临着更加严峻的挑战。总的来讲,APM 的问题主要体现在两个方面:Agent 的侵扰性导致难以落地,观测盲点导致无法定界。
第一,探针侵扰性导致难以落地。插桩的过程需要对应用程序的源代码进行修改,重新发布上线。即使例如 Java Agent 这类字节码增强技术,也需要修改应用程序的启动参数并重新发版。然而,对应用代码的改造还只是第一道关卡,通常落地过程中还会碰到很多其他方面的问题:
实际上,这也是为什么侵扰性的插桩方案少见于成功的商业产品,更多见于活跃的开源社区。OpenTelemetry、SkyWalking 等社区的活跃正是佐证。而在部门分工明确的大型企业中,克服协作上的困难是一个技术方案能够成功落地永远也绕不开的坎。特别是在金融、电信、电力等承载国计民生的关键行业中,部门之间的职责区分和利益冲突往往会使得落地插桩式的解决方案成为「不可能」。即使是在开放协作的互联网企业中,也少不了开发人员对插桩的不情愿、运维人员在出现性能故障时的背锅等问题。在经历了长久的努力之后人们已经发现,侵入性的解决方案仅仅适合于每个业务开发团队自己主动引入、自己维护各类 Agent 和 SDK 的版本、自己对性能隐患和运行故障的风险负责。当然,我们也看到了一些得益于基建高度统一而取得成功的大型互联网公司案例,例如 Google 就在 2010 年的 Dapper 论文中坦言:
True application-level transparency, possibly our most challenging design goal, was achieved by restricting Dapper’s core tracing instrumentation to a small corpus of ubiquitous threading, control flow, and RPC library code.
再例如字节跳动在 2022 年的对外分享《分布式链路追踪在字节跳动的实践》中也表示:
得益于长期的统一基建工作,字节全公司范围内的所有微服务使用的底层技术方案统一度较高。绝大部分微服务都部署在公司统一的容器平台上,采用统一的公司微服务框架和网格方案,使用公司统一提供的存储组件及相应 SDK。高度的一致性对于基础架构团队建设公司级别的统一链路追踪系统提供了有利的基础。
第二,观测盲点导致无法定界。即使 APM 已经在企业内落地,我们还是会发现排障边界依然难以界定,特别是在云原生基础设施中。这是因为开发和运维往往使用不同的语言在对话,例如当调用时延过高时开发会怀疑网络慢、网关慢、数据库慢、服务端慢,但由于全栈可观测性的缺乏,网络、网关、数据库给出的应答通常是网卡没丢包、进程 CPU 不高、DB 没有慢日志、服务端时延很低等一大堆毫无关联的指标,仍然解决不了问题。定界是整个故障处理流程中最关键的一环,它的效率至关重要。

定界在故障处理流程中的核心作用
这里我们想澄清两个概念:排障边界和职责边界。虽然开发的职责边界是应用程序本身,但排障边界却需要延展到网络传输上。举个例子:微服务在请求 RDS 云服务时偶现高达 200ms 的时延,如果开发以此为依据向云服务商提交工单,得到的应答大概率会是「RDS 没有观察到慢日志,请自查」。我们在很多客户处碰到了大量此类案例,根因有的是 RDS 前的 SLB 导致、有的是 K8s Node 的 SNAT 导致,背后的原因千奇百怪,但若不能在第一时间完成故障定界,都会导致租户(开发)和云服务商(基础设施)之间长达数天乃至数周的工单拉锯战。从排障边界的角度来讲,若开发能给出「网卡发送请求到收到响应之间的时延高达 200ms」,就能快速完成定界,推动云服务商排查。找对了正确的人,之后的问题解决一般都非常快。我们在后文也会分享几个真实案例。

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