更多技术交流、求职机会、试用福利,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群
字节跳动数据平台开发套件数据开发团队自研了基于依赖关系的全链路智能监控报警——基线监控,目前已在字节跳动内部得到广泛使用,覆盖抖音、电商、广告等100+个项目,SLA任务的基线监控覆盖率超过80%。
随着字节跳动业务的快速发展,大数据开发场景下需要运维管理的任务越来越多,然而普通的监控系统只支持配置相应任务的监控规则,已经不能完全满足当前需求,在日常运维中开发者经常会面临以下几个问题:
任务多,依赖关系复杂:很难查找到重要任务的所有上游任务并进行监控。如果监控所有任务,又会产生很多无用报警,导致有用报警被忽略;
配置运维成本高:每个任务的运行情况不一样,承诺完成时间不一样,如果单独对每个任务设置监控,分析及人工对齐任务SLA成本非常高;
报警形式多样性:对于小时级的任务,不同时段的报警及时性要求不同,普通监控无法很好得满足不同时段多样的报警需求。
为了有效运维日常任务,保障数据质量,字节跳动数据平台开发套件数据开发团队自研了基于依赖关系的全链路智能监控报警——基线监控,能根据任务运行情况,智能决策是否报警、何时报警、如何报警以及给谁报警,保障任务的整体产出链路。
基线监控已在字节跳动内部得到广泛使用,覆盖抖音、电商、广告等100+个项目,SLA任务的基线监控覆盖率超过80%。
实际案例
本节将从一个实际案例出发,介绍基线监控相较于普通监控的核心优势。
用户小明有一个对外承诺了的SLA任务,10点前必须要产出。其上下游关系如下图所示,其中SLA任务和任务4、5属于项目B,其他项目属于项目A。小明仅具有项目B的运维权限。
在没有基线监控前,为了保障SLA任务产出符合预期,小明会在SLA任务及其相同项目B内的上游任务上配置一系列告警规则,来预防上游任务延迟导致的SLA破线。比如在SLA任务和任务4、5上都配置了3条基础告警,以保证SLA任务延迟的风险及时感知和暴露,如下图所示。
但这种方式的问题也是显而易见的:
利用基础监控规则,至少需要配置9条规则,才能基本完成对SLA任务的监控;监控规则的配置方式大多来自于专家经验,但仍有遗漏的风险;基础监控规则只能监控到有运维权限的项目,不属于本项目的上游任务是无法监控到,因此小明也就无法提前感知到延迟风险。
有了基线监控,小明就只需要将SLA任务作为“保障任务”加入到基线监控中,保障任务的所有上游节点默认会被基线监控覆盖,小明再也不用配置多条基础告警规则,极大降低了告警规则配置的难度;一旦基线监控配置好之后,任意上游任务延迟,对小明来说都可以快速感知到,可有效保障SLA任务按时产出。
通过上面的实际案例,你应该对基线有了一个大概的理解。下面,就让我们一起了解下基线监控的相关概念和系统架构,并详细了解下基线监控的核心实现逻辑吧。
概念解读
基线监控
根据监控规则和任务运行情况,基线监控能够决策是否报警、何时报警、如何报警以及给谁报警。基线监控保障的是任务整体产出链路。基线监控的核心目标包括:
覆盖链路中的所有任务;
降低任务监控配置成本;
避免无效报警。
保障任务
一般选择有SLA要求的任务作为保障任务加入基线,基线通过保障任务的依赖拓扑图自动监控上游任务,形成需要监控的任务链路。
时间定义
承诺时间:最晚完成时间,即SLA。
预警余量:基线SLA Buffer,消耗预警余量即触发基线预警。
预警时间:任务承诺时间 - 预警余量,即任务预期最晚完成时间。
预测运行时长:基于任务历史的执行情况预测当前任务执行的运行时长。
承诺最晚开始时间:承诺时间-任务预测运行时长。
预警最晚开始时间:预警时间-任务预测运行时长。
监控范围
基线默认监控的范围包括:基线保障任务及保障任务上游的所有任务。
如下图所示,保障任务D,E及它们所有的上游节点都会纳入基线监控范围,而任务C,F不受基线监控。值得说明的是,基线监控允许用户配置基线监控只覆盖“指定项目”下的任务,此时基线监控的范围就只包含了保障任务及这些项目下的上游任务。
基线实例
和任务类似,基线也有业务时间的概念。对任务来说,一个业务时间会生成一个任务实例;而对基线来说,一个业务时间会生成一个基线实例,负责监控同一业务时间下保障任务的实例及其依赖的所有上游任务实例的运行状态。天基线和小时基线每天生成实例的规则如下:
天基线:每天生成一个基线实例,其业务时间与该基线保障任务的业务时间相同;
小时基线:承诺时间有两种设置方式:统一承诺和分时承诺。如果是统一承诺,则生成基线实例的个数为24个,每个基线实例的承诺时间一致;如果是分时承诺,则每天生成N个基线实例,其中N为用户配置的监控业务时间范围包含的业务时间数量,N的范围是[1,24]。
基线实例状态
- 安全:任务在预警时间之前完成。
- 预警:任务在预警时间未开始运行,但还未达到承诺时间。
- 破线:任务在承诺时间仍未运行完成。
- 其他:基线实例关闭或者基线没有关联任务时,基线实例所处的状态。
报警类型
基线监控支持非常丰富的报警类型:
- 基线预警:基线监控的链路上,首个没有在基线预警时间节点开始运行的节点。
- 基线破线:“基线破线”报警需满足以下两个条件:
任务节点的上游(包含直接和间接上游)没有出现过破线;
该任务没有在破线时间节点开始运行。 - 破线加剧:“执行变慢导致破线加剧”报警触发需满足以下两个条件:
任务所在链路已发送首次“基线破线”报警
任务运行耗时相较于预测运行耗时有所增加,具体来说:
a. 当任务实际开始时间早于基线破线开始时间时,将“破线开始时间 + (预测运行耗时 * (1 + N%))”作为检测时间点,如果任务到达检测时间点时还没有运行完成,则触发告警;
b. 当任务实际开始时间晚于破线开始时间时,将“任务实际开始时间 + (预测运行耗时 * (1 + N%))”作为检测时间点,如果任务到达检测时间点时还没有运行完成,则触发告警。 - 保障任务预警时间未完成:基线预警时间到达(承诺时间-预警余量),检查基线所有保障任务是否完成运行,若有保障任务未运行完成,且基线之前无预警、破线报警,则触发报警。
- 保障任务承诺时间未完成:基线承诺时间到达,检查基线所有保障任务是否完成运行,若有保障任务未运行完成,则触发报警。
- 任务失败事件:基线监控链路上,任意任务,重试结束仍失败,则触发失败事件。监控链路上的任务,发生失败事件则触发该报警。
基线事件
基线监控任务(保障任务及其上游任务),在执行过程中,若出现失败、变慢等情况,将被当作基线的异常事件,进行记录。
- 变慢事件: 识别基线监控任务(保障任务及其上游)运行变慢的情况。识别条件为:任务运行时长较该任务的预测运行时长上涨了X%,则视为一个变慢事件。
- 失败事件: 识别基线监控任务(保障任务及其上游)运行失败的情况,识别条件为:任务运行过程中出现过失败,则视为一个失败事件。
基线事件的状态包含“新发现”和“已恢复”两种。当基线监控的任务产生变慢或者失败事件时,基线事件状态更新为“新发现”;但如果任务最终完成了的话,基线事件的状态会被更新为“已恢复”。
系统实现
整体架构
- 基线管理模块:负责基线创建、更新、删除等操作,管理基线元信息,包括保障任务,承诺时间,余量及报警配置等;
- 基线实例生成:系统每天定时触发生成基线实例,生成实例的同时根据保障任务,由下而上逐层遍历 (BFS)所有上游任务并生成基线监控埋点。
生成基线监控埋点的过程中,会计算每个任务节点的预测运行时长,承诺时间,预警时间,预警最晚开始时间,承诺最晚开始时间。
此外,系统会给基线监控任务添加基线出错/变慢报警规则,当任务执行触发规则后,通过基础报警服务发送基线报警事件; - 监控埋点校验:系统维护一个延迟队列,根据校验时间点(预警最晚开始时间,承诺最晚开始时间以及破线加剧时间校验点),定时触发监控埋点校验任务实例运行状态,如果在时间点实例未运行成功,产生基线预警/破线报警事件,发送给基础报警服务发送报警。
由于基线实例生成和基线埋点检测是基线监控的核心模块,因此本文只着重介绍下这两个模块。
基线实例生成
每天固定时间点(如22:00),根据基线类型及业务日期生成对应的基线实例。
针对每一个基线实例,系统根据该基线实例对应的监控链路(任务DAG),由保障任务为起点,自下而上逐层(BFS)计算各任务对应的监控埋点实例的校验时间节点,包括预测运行时长、 预警时间、承诺时间、预警最晚开始时间,承诺最晚开始时间。
如下图所示,上游任务(B)的预警时间为其子任务实例埋点的预警最晚开始时间,
任务节点中的数字表示任务的预测运行时长,如节点A(1.5h),表示A的预测运行时长是1.5小时。
如图所示,基线保障任务为A,承诺时间为9:00,用户设置的预警余量为0.5h,结合系统推算出该任务本次的预测运行时长为1.5h。因此,任务A监控埋点的预警时间为8:30(9:00-0.5h),预警最晚开始时间为7:00(8:30-1.5h),承诺最晚开始时间为7:30(7:00+0.5h)。
上下游任务之间监控埋点的各时间节点方法如上图所示,满足:上游任务的承诺(预警)时间 = 下游任务的承诺(预警)最晚开始时间。
上图示例只是理想情况,但实际上任务链路会非常复杂,如跨层依赖、循环依赖非常常见。此外,任务链路也是有可能动态变化的,上游依赖新增或者减少也是个普遍现象。
因此,基线实例生成时,需要针对上述情况进行处理,以保证基线监控的有效性和合理性。下面,我们针对每种场景介绍基线监控算法的解决办法。
基线监控的任务链变化了怎么办?
目前,基线监控算法是通过基线实例生成时刻该基线监控的任务链路“快照”来生成监控埋点实例的,暂未针对监控埋点生成结束后,基线覆盖的任务链路发生变化的情况进行处理。即,用户操作任务并不改变已经生成的基线监控埋点实例的信息(计算得来的各种时间及任务与基线的映射关系等),而是等到下一次生成基线实例的时候重新去计算。具体实现时,系统会将任务DAG进行缓存(1h),以提高埋点实例生成的效率。
基线覆盖的任务链路存在跨层依赖怎么办?
由于在计算监控埋点实例的时候是由下至上逐层计算的,可以理解为是个局部计算,无法获取整个任务链路的全貌。因此,如果基线覆盖的任务链路中存在跨层依赖,那么同一业务任务实例上的监控埋点的时间点需要不断更新至最早值。
如下图所示,任务A依赖任务C和E,任务C依赖于任务E,而任务A又直接依赖于任务E。保障任务A的埋点实例计算结束之后,可以计算任务B、C、D、E的埋点实例信息;而当计算任务C的埋点实例信息时,任务E的埋点实例需要根据任务C的埋点实例信息进行更新。这样才能保证整个任务链路监控的合理性。
基线覆盖的任务链路存在循环依赖怎么办?
基线覆盖的任务链路中存在循环依赖,一般是由于某两个任务之间在业务时间存在offset导致的。如下图所示,比如任务B 业务时间23:00的实例依赖任务C 业务时间23:00的实例,而任务C 业务时间23:00的实例又依赖于任务A 业务时间22:00的实例。
对于这种情况,处理原则为:只保留任务最新业务时间(latest_task_time)对应的埋点实例,早于latest_task_time的业务时间对应的埋点实例直接丢弃。这是考虑到对更早时间点的实例进行监控的意义不大,因为前一天的基线监控已经发现出问题并触发告警了。
基线埋点校验
基线实例生成结束后,将生成的监控埋点实例维护到一个延迟队列BaselineTimeQueue里,Delay时间节点、监控埋点实例校验阶段及对应阶段触发的报警类型三者之间的关系如下图所示:
- 基线监控埋点实例的初始阶段为基线预警校验阶段(CHECK_START_WARNING_TIME),其Delay时间点为预警最晚开始时间(earliest_start_time_for_warning)。当到达earliest_start_time_for_warning时间节点时,监控埋点对应的任务仍未开始运行,且该任务是该基线监控链路上的首个满足条件的任务,则基线实例的状态由安全更新为基线预警,并发送基线预警报警。无论是否触发报警,监控埋点实例的状态都会从CHECK_START_WARNING_TIME流转至基线破线校验阶段(CHECK_START_COMMIT_TIME),并且重新放至延迟队列中,等待基线破线的校验。
- 当到达承诺最晚开始时间(earliest_start_time_for_commit)时间节点时,监控埋点对应的任务仍未开始执行,且该任务是该基线监控链路上的首个满足条件的任务,则基线实例的状态由安全/基线预警更新为基线破线,并发送基线破线报警。
- 在基线破线校验结束之后,需要判断是否需要进入基线破线加剧校验阶段:
如果当前任务或其上游存在破线任务,且当前任务已经开始执行,则基线实例状态更新为基线破线加剧检查阶段(CHECK_OVERTIME_INTENSIFY),Delay时间为基线破线加剧校验时间节点(overtime_intensify_time),即任务实际开始时间 + (预测运行耗时 * (1 + N%));
如果当前任务尚未开始执行,则基线实例状态更新为等待基线破线加剧检查阶段(CHECK_WAIT_OVERTIME_INTENSIFY),此时Delay时间为等待基线破线校验时间节点(wait_overtime_intensify_time),即破线开始时间 + (预测运行耗时 * (1 + N%))。
当到达wait_overtime_intensify_time时间节点进行校验时,若任务仍未开始执行,则检查阶段保持不变,wait_overtime_intensify_time增加 30 秒,重新入队等待下次检查。
当到达overtime_intensify_time时间节点进行校验时,若任务仍未运行成功,则触发基线破线加剧报警,并将基线实例的状态更新为FINISH_WITH_UNSAFE,埋点监控结束;若任务已运行成功,则不触发报警,并将基线实例的状态更新为FINISH_WITH_SAFE,监控结束。
未来
目前,基线监控这一能力也已经通过火山引擎大数据研发治理套件DataLeap向企业开放。企业可以通过火山引擎DataLeap基线监控,有效降低监控配置成本、避免无效报警及报警泛滥。
未来,我们将继续针对基线监控进行优化,如基线关键路径分析、基线实例生成效率优化等,不断提高基线监控算法性能,完善基线链路分析能力,不断提升用户体验,致力于向火山引擎DataLeap用户提供更强大的全链路监控运维能力。
立即跳转火山引擎大数据研发治理套件DataLeap官网了解详情!