【AICon】探索RAG 技术在实际应用中遇到的挑战及应对策略!AICon精华内容已上线73%>>> 了解详情
写点什么

谈阿里核心业务监控平台 SunFire 的技术架构

  • 2017-01-18
  • 本文字数:5888 字

    阅读完需:约 19 分钟

在 2016 年双 11 全球购物狂欢节中,天猫全天交易额 1207 亿元,前 30 分钟每秒交易峰值 17.5 万笔,每秒支付峰值 12 万笔。承载这些秒级数据背后的监控产品是如何实现的呢?接下来本文将从阿里监控体系、监控产品、监控技术架构及实现分别进行详细讲述。

阿里有众多监控产品,且各产品分工明确,百花齐放。整个阿里监控体系如下图:

集团层面的监控,以平台为主,全部为阿里自主研发(除引入了第三方基调、博睿等外部检测系统,用于各地 CDN 用户体验监控),这些监控平台覆盖了阿里集团 80% 的监控需求。

此外,每个事业群均根据自身特性自主研发了多套监控系统,以满足自身特定业务场景的监控需求,如广告的 GoldenEye、菜鸟的棱镜、阿里云的天基、蚂蚁的金融云(基于 XFlush)、中间件的 EagleEye 等,这些监控系统均有各自的使用场景。

阿里的监控规模早已达到了千万量级的监控项,PB 级的监控数据,亿级的报警通知,基于数据挖掘、机器学习等技术的智能化监控将会越来越重要。

阿里全球运行指挥中心(GOC)基于历史监控数据,通过异常检测、异常标注、挖掘训练、机器学习、故障模拟等方式,进行业务故障的自动化定位,并赋能监控中心 7*24 小时专业监控值班人员,使阿里集团具备第一时间发现业务指标异常,并快速进行应急响应、故障恢复的能力,将故障对线上业务的影响降到最低。

接下来将详细讲述本文的主角,承载阿里核心业务监控的 SunFire 监控平台。

SunFire 简介

SunFire 是一整套海量日志实时分析解决方案,以日志、REST 接口、Shell 脚本等作为数据采集来源,提供设备、应用、业务等各种视角的监控能力,从而帮您快速发现问题、定位问题、分析问题、解决问题,为线上系统可用率提供有效保障。

SunFire 利用文件传输、流式计算、分布式文件存储、数据可视化、数据建模等技术,提供实时、智能、可定制、多视角、全方位的监控体系。其主要优势有:

  • 全方位实时监控:提供设备、应用、业务等各种视角的监控能力,关键指标秒级、普通指标分钟级,高可靠、高时效、低延迟。
  • 灵活的报警规则:可根据业务特征、时间段、重要程度等维度设置报警规则,实现不误报、不漏报。
  • 管理简单:分钟级万台设备的监控部署能力,故障自动恢复,集群可伸缩
  • 自定义便捷配置:丰富的自定义产品配置功能,便捷、高效的完成产品配置、报警配置。
  • 可视化:丰富的可视化 Dashboard,帮助您定制个性化的监控大盘。
  • 低资源占用:在完成大量监控数据可靠传输的同时,保证对宿主机的 CPU、内存等资源极低占用率。

Sunfire 技术架构如下:

(点击放大图像)

主要模块实现及功能

针对架构图中的各个组件,其中最关键的为采集(Agent)、计算(Map、Reduce)组件,接下来将对这两个组件进行详细介绍。

1. 采集

Agent 负责所有监控数据的原始采集,它以 Agent 形式部署在应用系统上,负责原始日志的采集、系统命令的执行。日志原始数据的采集,按周期查询日志的服务,且日志查询要低耗、智能。Agent 上不执行计算逻辑。主要具有以下特点:

低耗

采集日志,不可避免要考虑日志压缩的问题,通常做日志压缩则意味着它必须做两件事情:一是磁盘日志文件的内容要读到应用程序态;二是要执行压缩算法。

这两个过程就是 CPU 消耗的来源。但是它必须做压缩,因为日志需要从多个机房传输到集中的机房。跨机房传输占用的带宽不容小觑,必须压缩才能维持运转。所以低耗的第一个要素,就是避免跨机房传输。SunFire 达到此目标的方式是运行时组件自包含在机房内部,需要全量数据时才从各机房查询合并。

网上搜索 zero-copy,会知道文件传输其实是可以不经过用户态的,可以在 Linux 的核心态用类似 DMA 的思想,实现极低 CPU 占用的文件传输。SunFire 的 Agent 当然不能放过这个利好,对它的充分利用是 Agent 低耗的根本原因。以前这部分传输代码是用 c 语言编写的 sendfile 逻辑,集成到 Java 工程里,后来被直接改造为了 Java 实现。

最后,在下方的计算平台中会提到,要求 Agent 的日志查询服务具备“按周期查询日志”的能力。这是目前 Agent 工程里最大的难题,我们都用过 RAF(RandomAccessFile),你给它一个游标,指定 offset,再给它一个长度,指定读取的文件 size,它可以很低耗的扒出文件里的这部分内容。然而问题在于:周期≠offset。从周期转换为 offset 是一个痛苦的过程。

在流式计算里一般不会遇到这个问题,因为在流式架构里,Agent 是水龙头,主动权掌握在 Agent 手里,它可以从 0 开始 push 文件内容,push 到哪里就做一个标记,下次从标记继续往后 push,不断重复。这个标记就是 offset,所以流式不会有任何问题。

而计算平台周期任务驱动架构里,pull 的方式就无法提供 offset,只能提供 Term(周期,比如 2015-11-11 00:00 分)。Agent 解决此问题的方式算是简单粗暴,那就是二分查找法。而且日志还有一个天然优势,它是连续性的。所以按照对二分查找法稍加优化,就能达到“越猜越准”的效果(因为区间在缩小,区间越小,它里面的日志分布就越平均)。

于是,Agent 代码里的 LogFinder 组件撑起了这个职责,利用上述两个利好,实现了一个把 CPU 控制在 5% 以下的算法,目前能够维持运转。其中 CPU 的消耗不用多说,肯定是来自于猜的过程,因为每一次猜测,都意味着要从日志的某个 offset 拉出一小段内容来核实,会导致文件内容进入用户态并解析。这部分算法依然有很大的提升空间。

日志滚动

做过 Agent 的同学肯定都被日志滚动困扰过,各种各样的滚动姿势都需要支持。SunFire 的 pull 方式当然也会遇到这个问题,于是我们简单粗暴的穷举出了某次 pull 可能会遇到的所有场景,比如:

  • 正常猜到了 offset
  • 整个日志都猜不到 offset
  • 上次猜到了 offset,但是下次再来的时候发现不对劲(比如滚动了)

这段逻辑代码穷举的分支之多,在一开始谁都没有想到。不过仔细分析了很多次,发现每个分支都必不可少。

查询接口

Agent 提供的查询服务分为 first query 和 ordinary query 两种。做这个区分的原因是:一个周期的查询请求只有第一次需要猜 offset,之后只需要顺序下移即可。而且计算组件里有大量的和 Agent 查询接口互相配合的逻辑,比如一个周期拉到什么位置上算是确定结束?一次 ordinary query 得到的日志里如果末尾是截断的(只有一半)该如何处理…… 这些逻辑虽然缜密,但十分繁琐,甚至让人望而却步。但现状如此,这些实现逻辑保障了 SunFire 的高一致性,不用担心数据不全、报警不准,随便怎么重启计算组件,随便怎么重启 Agent。但这些优势的背后,是值得深思的代码复杂度。

路径扫描

为了让用户配置简单便捷,SunFire 提供给用户选择日志的方式不是手写,而是像 windows 的文件夹一样可以浏览线上系统的日志目录和文件,让他双击一个心仪的文件来完成配置。但这种便捷带来的问题就是路径里若有变量就会出问题。所以 Agent 做了一个简单的 dir 扫描功能。Agent 能从应用目录往下扫描,找到同深度文件夹下“合适”的目标日志。

2. 计算

由 Map、Reduce 组成计算平台,负责所有采集内容的加工计算,具备故障自动恢复能力及弹性伸缩能力。计算平台一直以来都是发展最快、改造最多的领域,因为它是很多需求的直接生产者,也是性能压力的直接承担者。因此,在经过多年的反思后,最终走向了一条插件化、周期驱动、自协调、异步化的道路。主要具有以下几个特点:

纯异步

原来的 SunFire 计算系统里,线程池繁复,从一个线程池处理完还会丢到下一个线程池里;为了避免并发 bug,加锁也很多。这其中最大的问题有两个:CPU 密集型的逻辑和 I/O 密集型混合。

对于第一点,只要发生混合,无论你怎么调整线程池参数,都会导致各式各样的问题。线程调的多,会导致某些时刻多线程抢占 CPU,load 飙高;线程调的少,会导致某些时刻所有线程都进入阻塞等待,堆积如山的活儿没人干。

对于第二点,最典型的例子就是日志包合并。比如一台 Map 上的一个日志计算任务,它要收集 10 个 Agent 的日志,那肯定是并发去收集的,10 个日志包陆续(同时)到达,到达之后各自解析,解析完了 data 要进行 merge。这个 merge 过程如果涉及到互斥区(比如嵌套 Map 的填充),就必须加锁,否则 bug 满天飞。

但其实我们重新编排一下任务就能杜绝所有的锁。比如上面的例子,我们能否让这个日志计算任务的 10 个 Agent 的子任务,全部在同一个线程里做?这当然是可行的,只要回答两个问题就行:

  • 如果串行,那 10 个 I/O 动作(拉日志包)怎么办?串行不就浪费 cpu 浪费时间吗?
  • 把它们都放到一个线程里,那我怎么发挥多核机器的性能?

第一个问题,答案就是异步 I/O。只要肯花时间,所有的 I/O 都可以用 NIO 来实现,无锁,事件监听,不会涉及阻塞等待。即使串行也不会浪费 CPU。第二个问题,就是一个大局观问题了。现实中我们面临的场景往往是用户配置了 100 个产品,每个产品都会拆解为子任务发送到每台 Map,而一台 Map 只有 4 个核。所以,你让一个核负责 25 个任务已经足够榨干机器性能了,没必要追求更细粒度子任务并发。因此,计算平台付出了很大的精力,做了协程框架。

我们用 Akka 作为协程框架,有了协程框架后再也不用关注线程池调度等问题了,于是我们可以轻松的设计协程角色,实现 CPU 密集型和 I/O 密集型的分离、或者为了无锁而做任务编排。接下来,尽量用 NIO 覆盖所有的 I/O 场景,杜绝 I/O 密集型逻辑,让所有的协程都是纯跑 CPU。按照这种方式,计算平台已经基本能够榨干机器的性能。

周期驱动

所谓周期驱动型任务调度,说白了就是 Map/Reduce。Brain 被选举出来之后,定时捞出用户的配置,转换为计算作业模型,生成一个周期(比如某分钟的)的任务, 我们称之为拓扑 (Topology), 拓扑也很形象的表现出 Map/Reduce 多层计算结构的特征。所有任务所需的信息,都保存在 topology 对象中,包括计算插件、输入输出插件逻辑、Map 有几个、每个 Map 负责哪些输入等等。这些信息虽然很多,但其实来源可以简单理解为两个:一是用户的配置;二是运维元数据。拓扑被安装到一台 Reduce 机器(A)上。

A 上的 Reduce 任务判断当前集群里有多少台 Map 机器,就产生多少个任务(每个任务被平均分配一批 Agent),这些任务被安装到每台机器上 Map。被安装的 Map 任务其实就是一个协程,它负责了一批 Agent,于是它就将每个 Agent 的拉取任务再安装为一个协程。至此,安装过程结束。Agent 拉取任务协程(也称之为 input 输入协程,因为它是数据输入源)在周期到点后,开始执行,拉取日志,解析日志,将结果交予 Map 协程;Map 协程在得到了所有 Agent 的输入结果并 merge 完成后,将 merge 结果回报到 Reduce 协程(这是一个远程协程消息,跨机器);Reduce 协程得到了所有 Map 协程的汇报结果后,数据到齐,写入到 Hbase 存储,结束。

上述过程非常简单,不高不大也不上,但经过多年大促的考验,其实非常的务实。能解决问题的架构,就是好的架构,能简单,为何要把事情做得复杂呢?

这种架构里,有一个非常重要的特性:任务是按周期隔离的。也就是说,同一个配置,它的 2015-11-11 00:00 分的任务和 2015-11-11 00:01 分的任务,是两个任务,没有任何关系,各自驱动,各自执行。理想情况下,我们可以做出结论:一旦某个周期的任务结束了,它得到的数据必然是准确的(只要每个 Agent 都正常响应了)。所以采用了这种架构之后,SunFire 很少再遇到数据不准的问题,当出现业务故障的时候我们都可以断定监控数据是准确的,甚至秒级都可以断定是准确的,因为秒级也就是 5 秒为周期的任务,和分钟级没有本质区别,只是周期范围不同而已。能获得这个能力当然也要归功于 Agent 的“按周期查询日志”的能力。

任务重试

在上节描述的 Brain->Reduce->Map 的任务安装流程里,我们对每一个上游赋予一个职责:监督下游。当机器发生重启或宕机,会丢失一大批协程角色。每一种角色的丢失,都需要重试恢复。监督主要通过监听 Terminated 事件实现,Terminated 事件会在下游挂掉 (不论是该协程挂掉还是所在的机器挂掉或是断网等) 的时候发送给上游。由于拓扑是提前生成好且具备完备的描述信息,因此每个角色都可以根据拓扑的信息来重新生成下游任务完成重试。

  • 若 Brain 丢失,则 Brain 会再次选主, Brain 读取最后生成到的任务周期, 再继续生成任务。
  • 若 Reduce 丢失,每个任务在 Brain 上都有一个 TopologySupervisor 角色, 来监听 Reduce 协程的 Terminated 事件来执行重试动作。
  • 若 Map 丢失,Reduce 本身也监听了所有 Map 的 Terminated 事件来执行重试动作。
  • 为确保万无一失,若 Reduce 没有在规定时间内返回完成事件给 Brain,Brain 同样会根据一定规则重试这个任务。

过程依然非常简单,而且从理论上是可证的,无论怎么重启宕机,都可以确保数据不丢,只不过可能会稍有延迟(因为部分任务要重新做)。

输入共享:在用户实际使用 SunFire 的过程中,常常会有这样的情况:用户配了几个不同的配置,其计算逻辑可能是不同的,比如有的是单纯计算行数,有的计算平均值,有的需要正则匹配出日志中的内容,但这几个配置可能都用的同一份日志,那么一定希望几个配置共享同一份拉取的日志内容。否则重复拉取日志会造成极大的资源消耗。那么我们就必须实现输入共享,输入共享的实现比较复杂,主要依赖两点:

  • 其一是依赖安装流,因为拓扑是提前安装的,因此在安装到真正开始拉取日志的这段时间内,我们希望能够通过拓扑信息判断出需要共享的输入源,构建出输入源和对应 Map 的映射关系。
  • 其二是依赖 Map 节点和 Agent 之间的一致性哈希,保证 Brain 在生成任务时,同一个机器上的日志,永远是分配相同的一个 Map 节点去拉取的(除非它对应的 Map 挂了)。

站在 Map 节点的视角来看:在各个任务的 Reduce 拿着拓扑来注册的时候,我拿出输入源(对日志监控来说通常可以用一个 IP 地址和一个日志路径来描述)和 Map 之间的关系,暂存下来,每当一个新的 Reduce 来注册 Map,我都判断这个 Map 所需的输入源是否存在了,如果有,那就给这个输入源增加一个上游,等到这个输入源的周期到了,那就触发拉取,不再共享了。

其他组件

存储:负责所有计算结果的持久化存储,可以无限伸缩,且查询历史数据保持和查询实时数据相同的低延迟。Sunfire 原始数据存储使用的是阿里集团的 Hbase 产品(HBase :Hadoop Database,是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统),用户配置存储使用的是 MongoDb。

展示:负责提供用户交互,让用户通过简洁的建模过程来打造个性化的监控产品。基于插件化、组件化的构建方式,用户可以快速增加新类型的监控产品。

自我管控:即 OPS-Agent、Ops-web 组件,负责海量 Agent 的自动化安装监测,并且承担了整个监控平台各个角色的状态检测、一键安装、故障恢复、容量监测等职责。

2017-01-18 16:2211433

评论

发布
暂无评论
发现更多内容

这一年,我和 TiDB 的故事

TiDB 社区干货传送门

TIDB 6.0新特性漫谈之Clinic

TiDB 社区干货传送门

新版本/特性发布 6.x 实践

TiDB v6.0.0 DMR 源码阅读——缓存表

TiDB 社区干货传送门

TiDB 源码解读 新版本/特性解读 6.x 实践

TiDB中如何查看database级别的QPS

TiDB 社区干货传送门

监控

TiDB 6.1 单机环境 On openEular 2003 SP3

TiDB 社区干货传送门

实践案例 版本测评 应用适配 6.x 实践

TiDB 6.1 新特性解读 | TiDB 6.1 MPP 实现窗口函数框架

TiDB 社区干货传送门

新版本/特性解读 6.x 实践

利用odbc连接oracle与tidb

TiDB 社区干货传送门

迁移 实践案例 数据库架构选型 应用适配 数据库连接

TiDB 查询优化及调优系列(四)查询执行计划的调整及优化原理

TiDB 社区干货传送门

你踩过这些坑吗?谨慎在时间类型列上创建索引

TiDB 社区干货传送门

性能调优 TiDB 底层架构 OLTP 场景实践

TIDB监控升级解决panic的漫漫探索之路

TiDB 社区干货传送门

监控 实践案例 集群管理 故障排查/诊断 扩/缩容

我和 TiDB 的故事 - 2020~2022

TiDB 社区干货传送门

TiSpark v2.5 开发入门实践及 TiSpark v3.0.0 新功能解读

TiDB 社区干货传送门

6.x 实践

TiDB 性能优化概述

TiDB 社区干货传送门

性能调优

TiDB 性能分析和优化

TiDB 社区干货传送门

性能调优

避坑指南 生产环境TiKV的IO-Util趋近100%问题定位

TiDB 社区干货传送门

集群管理 管理与运维 TiKV 底层架构

TiDB多活方案

TiDB 社区干货传送门

实践案例 集群管理 数据库架构选型 数据库架构设计

生产环境TiDB集群缩容TiKV操作步骤

TiDB 社区干货传送门

扩/缩容

TiCDC canal_json的实际应用

TiDB 社区干货传送门

迁移 管理与运维 新版本/特性解读 OLTP 场景实践

TiDB VS MySQL

TiDB 社区干货传送门

OLTP 负载性能优化实践

TiDB 社区干货传送门

性能调优 OLTP 场景实践

分布式数据库 TiDB 6.0 集群保姆级安装手册

TiDB 社区干货传送门

6.x 实践

TiCDC 6.0 原理之 Sorter 演进

TiDB 社区干货传送门

TiDB 源码解读 6.x 实践

离线安装 TiSpark v2.5.1

TiDB 社区干货传送门

6.x 实践

文盘Rust -- 子命令提示,提高用户体验

TiDB 社区干货传送门

开发语言

使用 Vagrant + VirtualBox 虚拟机搭建TiDB v5.4 实验环境

TiDB 社区干货传送门

安装 & 部署

基于 TiDB 场景式技术架构过程 - 理论篇

TiDB 社区干货传送门

数据库架构选型 数据库架构设计

文件数据导入到TiDB的实践

TiDB 社区干货传送门

TiFlash 源码阅读(三) DeltaTree 存储引擎设计及实现分析 - Part 1

TiDB 社区干货传送门

Performance Overview 面板重要监控指标详解

TiDB 社区干货传送门

监控

TiSpark 3.0.0 新特性实践

TiDB 社区干货传送门

实践案例 新版本/特性发布 HTAP 场景实践 大数据场景实践

TiSpark v2.4.x 升级到 TiSpark v2.5.x

TiDB 社区干货传送门

实践案例 6.x 实践

谈阿里核心业务监控平台SunFire的技术架构_语言 & 开发_郁松_InfoQ精选文章