DeepFlow 在小米的部署eBPF模式

云杉 世纪

2024年2月21日

产品资讯

接下来讲一下我们大致在小米中的部署框架,大家使用 DeepFlow 产品的时候知道, DeepFlow 的 Agent 采集器部署是分两种方式去部署的,一个是云原生部署,一个是传统主机部署,就是云主机或者物理主机的方式。小米内部我们前面也说了,有两套平台,一个是主机应用平台,一个是容器应用平台。所以说我们部署方式也分了两套。
首先是传统主机的部署架构,我们做了一个比较巧妙的设计,我们之前也说到有个 Falcon,即 SREOps 的产品,我们的采集器其实已经覆盖集团所有主机了。这个时候我们要推 DeepFlow,如何把它也向全集团去推呢?其实我们可以搭我们的 Falcon Agent 的顺风车,我们把 Falcon 的 Agent 进行了改造,把 DeepFlow 的功能融合进去了。这边可以看到绿色的方框内是我们采集对象的主机,上面有 DeepFlow Agent,它对接的是 cBPF 和 eBPF 的探针,然后 Falcon Agent 采集的是主机的基础指标,包括 CPU、内存。最下面我们还有个插件,就是之前 DeepFlow 采集的 Flow Metrics 和 Flow Log 这些信息,它是没有应用信息的,我们通过这个插件跟我们的应用发布系统联动,用户发应用的时候会在主机上留应用元信息,包括应用的细节,比如进程也即 PID 的映射,然后这个插件就是把这个映射同步给 DeepFlow 的集群,这样就可以给 DeepFlow 的流打上我们的应用信息,也满足了 Hera 的以应用为核心的需求,最下面有一个 Super Agent,其实就是把三个 Agent 进行融合,进行统一化的部署。然后右边做一个简单的管控面,这个管控面是我们内部用的,我们可以看到有多少个机器覆盖了 DeepFlow 的 Agent,有多少个开启采集配置,采集配置下发分两部分,一个是静态配置的下发,需要重启 Agent,还有一个是 DeepFlow 本身的动态配置下发,比如说它采集规则还有其他比较灵活的配置,也集成到配置下发这个功能模块里面了。
最后是我们的 Agent 编译部署平台,我们有一个全量更新的过程,通过发布工单,比如发布到全集团的机器,然后一个一个地去更新,比如 DeepFlow 有功能要更新或者有 bug 要解决,我们就通过工单系统一次性把它全量升级。另外还有自动化的发布脚本,小米集团所有新采购机器预装的时候,我们会执行这个脚本,它从 FDS 里面拉 Super Agent 的二进制包,然后把 Super Agent 部署到新机器上面去。
最下面(红色箭头)是一个数据链路,这个数据面相当于 DeepFlow 通过 Super Agent 传到 DeepFlow 集群里面去,这是传统主机部署。后面有容器平台与部署,这个就是开箱即用了,就是社区的 Helm 部署,我们直接执行一下 10 分钟就可以搞定。
这个服务端的架构中间绿色部分就是开源社区提供的能力,我们提供了几个用户的终端,也就是 Hera 的界面。首先就是我们刚才说的 L7 协议的,比如 Dubbo 或者 HTTP 的一个 R.E.D. 的黄金指标,我们是通过 Grafana, 再通过 DeepFlow 的插件,直接以看板的形式暴露给用户的。
然后下面是链路,其实我们前面也说到了,首先我们的链路是基于 OTel 探针的,不是替换,我们是加强 OTel 探针采集的 Span,OTel 探针会通过 MQ 把我们的 Span 数据传到 ES 里面去,同时我们会有个服务,在给用户链路火焰图的时候,会同时从 ES 里面去查 Span,包括业务自己上报的 Span 和 DeepFlow 生成的网络 Span 和系统 Span,我们会把它融合在一起形成融合视图,最后展现给用户。
然后这边有一个比较有意思的功能,有一个 DeepFlow 同步模块,因为 DeepFlow 的数据都存在 Clickhouse 里,它会定期同步应用到应用的拓扑关系,并导到队列的一个 T+1 作业里面去,会生成一个静态拓扑图。
静态拓扑图这个功能,大致用于观察应用到应用之间实时的状态,它的主要作用是解答一下应用在系统架构层面上有什么样的拓扑关系。这个时候我们会导到 T+1 的作业,导入到 Doris 里面去暴露给用户,这样用户就可以看到自己应用上下连着哪些应用,或者是整个集团下面有哪些应用,以一个全局的视角。这个前面可能也还有个 Redis 缓存,对我们经常查的一些应用进行加速,然后下面有一个应用队列,我们之前也说了 Hera 是以应用为核心,所以它会定期从 Clickhouse 里把我们打标应用的元信息同步到 ES 里面去,这样在我们 Hera 平台里面搜索的时候,用户就可以看到,即使没有接入探针的应用,它也可以在 Hera 平台里面搜索出来。当然我们可以通过过滤器把它过滤掉。
后说说我们遇到的挑战和解法。首先这个是我们核心的目标,尽可能在不插码的前提下,让更多的用户体验到 Hera 的完整功能。可能不是 100%,但是 80% 到 90% 是一个目标,这个目标的实现过程中我们遇到很多挑战,DeepFlow 社区的专业团队的支持很多,都高效解决了,这边主要是两个挑战,一个是小米重度依赖 cBPF 能力,另一个就是拓扑图隐藏 LVS。
首先就是依赖于 cBPF 能力,因为小米的内核版本比较老,很多机器装不了 eBPF 的探针,所以我们使用了 cBPF 采集,但 cPPF 采集在社区最开始的一个版本里不支持进程粒度的拓扑关系。我举个例子,左边和右边各一个主机A、B,小米内部的应用是要混部的,因为要提高主机的资源利用率,我们在左边主机 A 里面混部 A 和 B 两个应用,右边主机 B 混部 C 和 D 两个应用,这时候我们通过 DeepFlow 的 cBPF 能力检测到 HTTP 5XX 的 status code ,那应用肯定是有问题的,但这个时候我们定位不到是哪个应用到哪个应用的问题,比如实际上是应用 A 到应用 D 有异常,应用 B 到应用 C 没有异常。业务去排查问题就会一脸懵,到底是哪个有问题?这也不能满足我们以应用为中心的诉求,这些问题在 eBPF 里面没有遇到,但在 cBPF 里面遇到过。
我们做了一个解法,当时跟社区提了一个 FR,这个应该是社区的第一个 FR,跟 DeepFlow 共同制定了一个方案,我们会定期同步 Socket 与 Process 的关联关系,DeepFlow 给的数据是一个 Flow,Flow 其实是个五元组,包含目的端的 Port IP 和我们的源端的 Port IP,我们通过流的五元组定义一个 Socket,然后 Socket 在 Linux 下可以跟 Process 进行关联,把流和应用 ID 关联在一起,后面我们根据流量的五元组信息锁定 Socket,从而锁定应用进程。
最后是我们前面提到的 DeepFlow 插件,我们会从发布系统中获取进程与应用关联,这样我们就可以把五元组,也就是 Flow 信息跟我们的应用进行关联,从而推算出比较有用的应用信息。表格里的是我们后来通过 FR 加入的功能,左边是我们的源应用,右边是我们的目的应用,取代了之前 host IP 到 host IP 这样的一个拓扑关系图,这样就可以回答用户应用到应用的关联了。
然后其他的优化做了哪些?首先是 Process 过滤,在 Linux 里面进程还是比较多的,有一些无效的噪音(编者按:非业务应用进程),比如说脚本或者内核进程,这些就是无效的 Process。我们通过正则对一些无效的噪音进行过滤,降低了我们的上报频率,减少开销。后面是子进程打标签,之前说过我们是通过流关联到进程,从而关联到应用。但很多架构,比如说 python 多进程架构,或者像 nginx,它不是单进程架构,它是个多进程架构,有父进程还有子进程,所以有时候关联的是子进程,但并不是真正要关联的应用。因为应用的标签是给父进程打元数据,记录的是应用元数据到父进程的 PID,这个时候流的子进程信息是没有用的。所以我们也做了树状分析,我们通过父进程不断 fork 子进程,然后我们给子进程打上和父进程同样的应用标签,这样就不会让用户产生疑问。
后面还有一个问题,经常创建短连接的应用,Socket 的创建数量会比较多,会导致我们在内存中的 Process 到 Socket 的映射表基数膨胀,最后导致了 OOM,这里面我们做了一些优化,通过过滤小于 10 秒的短连接,我们把基数进行控制,因为大部分在我们集团内部的一些框架,比如说 GRPC ,或者是 Dubbo 也好,或者是 HTTP 也好,它和 MySQL 和 Redis 都是基于连接池的,所以一般来说大部分都是长连接,只有极少数是短连接。所以过滤掉短连接其实对黄金指标的监控影响也不是特别大。
最后一个是比较难理解的,在 DeepFlow 的 Flow 中,有客户端到服务端的概念,到底哪个 IP 是客户端,或者哪个应用是客户端?这个方向比较难判,之前出现过乱序的现象,因为在网络层其实没有客户端、服务端这个概念,DeepFlow 解法是通过抓 SYN 报文,判断是谁先发起握手的,先发起握手的就更像是客户端。但是在我们部署 Agent 的时候,可能这个连接它已经建立很久,它可能是个长连接,所以我们会捕获不到 SYN 报文。这个时候我们通过一些动态的算法,分析 TCP 里面流量的一些行为,然后不断地给这个方向进行投票,如果它得分比较低的时候,我们就把这个错误的方向给过滤掉了,我们只过滤那种得分比较高的,比如满分是 255 分,我们只过滤 200 分以上的流量。这样的话它在概率上就是接近 100% 的正确率。
挑战二是这个是拓扑图如何隐藏 LVS 。我们还是从应用到应用的监控来分析,不同的应用之间可能存在 LB,就是 LVS,然后 LVS 大家也知道它有个特点,它有个 VIP。我们这边看的就是 Client 连接中间 LVS 的时候,连接的是个VIP,然后 LVS 出来的时候又有一个 VIP’,甚至可能还有多个不同的 VIP’,因为它可能有多网卡,最后到我们的 Server 端。如果是正常的情况下,我们用户是其实是想知道 Client 到 Server,也就是应用 A 到应用 B 的调用关系的,但是因为有这个 LVS 的情况,导致我们整个的链路中断了,形成这样的效应,拓扑图上面全都是我们的客户端,然后下面全都是 VIP,我们的服务端就看不到了,因为中间链路断开了。因为是从客户端为视角开始做遍历的,怎么看不到客户端 —— 服务端的拓扑图了,这个对用户造成非常大的干扰,生成大量无意义的无效的拓扑信息。
而解法的话,我们通过两个方法解决,首先我们通过 TOA 获取真实的客户端 IP,中间这种服务端通过流量分析,抓的就是 VIP’,也就是 LVS 出网卡的 IP 到 RS 的流信息。但是我们如何穿透 LVS?其实我们可以通过这个技术,在我们集团内部的 LVS 模块,当然也不仅是我们集团内部,很多公有云也是一样的,它会在发送 SYN package 的时候,或者第一次推 PSH 包的时候,把客户端的真实的 IP 和 Port 放到 TCP Option 里面去,可以从 TCP Option 里面解析它,来获取真实的客户端 IP 和真实客户端的 Port,这个就解了如何穿透 LVS 获取真实客户端 IP 的技术难题。还有一个技术难题,就是我们知道客户端的真实 IP,但是我们存在混部的现象,得到的是主机的 IP,不知道到底是哪个应用。
所以下面我们还做了另一个优化,我们通过 Server 获取 PID,同时通过这个方法推导出客户端上的 PID。我们知道客户端上的 PID 后,就自然知道客户端上的应用了。具体我们可以看最上面一条链路,我们如何锁定一个PID?比如在我们的服务端安装了一个 DeepFlow Agent,抓了一个流,这个流的 PID 其实是可以知道的,因为有五元组信息,我们可以锁定 Socket,然后同时锁定到 PID。但是我们顺着链路往回溯我们客户端的 PID 的时候,中间断开了,因为我们不知道 VIP 到 VIP’ 的映射关系,在主机 A 有两个 Socket,所以我们不知道是哪个 Socket 发起这个连接,所以也不知道是哪个 PID。这个时候我们跟 DeepFlow 合作,做 LVS 平台注入的功能,把 LVS 中间拓扑信息注入到 DeepFlow 中间去,这样整个过程就可以串联在一起, DeepFlow 会自动地把这两个完全不相干的 Socket 通过拓扑关系把它关联在一起。
我们遇到很多挑战,通过技术的方式已经攻破了,但中间还遇到一些其他的挑战,目前还在解决的过程中。首先是 Dubbo 的线程池监控,我们做的 cBPF 是不侵入用户的进程的,它没办法通过 Uprobe 侵入到用户的进程中去。所以我们无法知道 Dubbo 线程池的监控,而 Dubbo 有一些线程池相关的都是应用级别的,所以我们不能到应用中去获取这些信息。后面第二个就是 OTel 探针, Span 的程序运行上下文缺失,OTel 探针在异常的时候,会把调用堆栈给打印出来,这个东西是要基于进程内部的方式,你得通过 eBPF 侵入到进程中去做,我们 cBPF 没有这个能力。
然后第三个就是业务日志,业务日志打印的东西都是高度自定义化的,这也需要我们侵入到业务进程中去做的,目前我们也没有办法去做。还有就是只对长连接进行标注,Socket 太多无法全部上报,我们前面也说了 Socket 如果全上报的话,那就太大了,内存就会爆掉,我们就只对长连接进行标注,这个也导致一部分的连接无法在系统中监控到,这个问题不大。我刚才也说了,大部分就是 95% 的应该都是长连接。最后还存在一个问题,对于部分大数据业务场景吞吐高的,会达到 Agent 的抓包上限,因为本身 cBPF 探针的原理其实和抓包原理是一样的,有个 buffer ,如果吞吐量比较高的话,抓包达到上限它就会丢包,这样就会导致结果不是特别准。
最后说一下当前的一个落地场景,我们第一个落地的产品是 Hera 的静态拓扑图,前面是我们说的是系统架构层面上,不是实时的,而是一个 T+1 的延时作业。我们主要是干什么用的?首先是回答各个部门之间有多少个应用。比如一级部门下面有二级部门,二级部门下面可能还有不同的业务线,业务线各个应用有多少个?我们要提供一个全景的视图,第二个是回答部门和部门之间存在多少个调用关系,这个调用关系的话,看部门之间,比如说不同业务线之间是不是有耦合现象。第三个就是回答应用之间的相互依赖关系,这个就是精确到之前说的部门维度,这时候就到服务应用的维度了,服务之间是不是存在依赖关系?然后第四个就是回答各个应用之间是否存在流量?这个是服务下线用的,经常会有人问我们这个服务能不能下了,我们把这个静态拓扑给他一看,说这个已经有三天没有流量了,然后他们就可以把这个服务给下掉了。
这边分两个(视图),一个是部门视图,一个是应用视图。如果你不断放大 scope,就是你想看的应用遍历层级,会发现应用越来越多,所以我们把它聚合成一个部门视图,这中间这几个圆,可以点击它展开成我们的应用视图,上面都是我们的二级部门,然后是我们不同的业务线,业务线就是最上面的蓝色和红色之间是有调用关系的,中间有一个是未接入 Hera 的应用,这个就是我们通过 ePPF 要通过 DeepFlow 探针自动采集到的,这时候我们就知道有多少个应用没有接入到 Hera ,可以推我们的业务方去把这些东西接探针。
右边是我们应用视角的链路拓扑,这个本身其实没有接 DeepFlow,之前我们是看了两个服务之间调用关系,接入 DeepFlow 后就我们会发现这几个服务他们还不是这么简单。它下面还会依赖 Redis,依赖 nacos,还有 ES 的能力,这样相当于把那些没有接入探针的应用,我们都把它给扫描出来了,就形成了一个全景图。
最后我们深知在这个 DeepFlow 的能力耕耘上面还是做得不够,我们还是希望它能做更多的事,这是我们大致的规划。首先是平台层面上,我们的目标目前是 80%(的可观测覆盖率),这个目标其实已经快达成了。然后容器平台是我们下个季度要去推的,这个应该推得很快,估计下个季度就可以把它全部覆盖了。然后三个核心维度的话我们主要去加强使用链路的功能,前提是一定要先接入 OTel 探针,然后我们通过加强网络 Span 的形式,把这个功能加强。
然后指标看板其实是可以完全覆盖的,用户在不接入 OTel 探针情况下,我们会给他一个体验版,最初的一个版本原型,可以看到大致的监控大盘,服务的接口的 SLA,都可以通过 Grafana 暴露出来。然后中间日志的话我们就先不去 touch 了,暂时搞不了。左边是我们的暴露给用户的核心能力,上面就是应用状态。
然后在这边有一个调用异常,也是我们加强的能力,这个不是覆盖,也是加强的,之前可以看到应用的调用异常,现在我们可以看到网络的调用异常。慢查询是可以完全覆盖的,因为 DeepFlow 天然支持 MySQL 的协议解析,你不需要接探针就可以看到慢查询。后面是一个服务大盘,我们说的 SLA 的一些黄金指标,就是 HTTP Dubbo 接口的 R.E.D.指标,我们也可以完全地展示给我们用户,然后报警这一块我们目前在调研,因为之前 DeepFlow 没有 Prometheus 的能力,我们整个报警是基于 Prometheus 去做的。所以我们在调研,可能这一块也可以做了。整个目标就是 Hera 不接入探针和接入探针,能覆盖到 90%。通过 DeepFlow 的 Agent,然后加 OTel 探针的能力覆盖到 90%,这是我们最终的目标。

Related Posts

根因分析假 running 真故障 记一次电力行业的 SRE 实践

云杉 世纪

2024年3月8日

产品资讯

用户:某省级电网企业 挑战 定界困难:当发生故障,业务部门和网络部门互相推诿,而不是解决问题; 监控颗粒度不足 […]

Read More

云杉网络 DeepFlow 联合 OpenCloudOS 完成技术兼容互认证

云杉 世纪

2024年3月6日

产品资讯

北京云杉世纪网络科技有限公司(以下简称:云杉网络)的云原生可观测性产品 DeepFlow 与 OpenClou […]

Read More