58 大数据任务调度和智能运维实践
导读: 随着大数据的进一步发展,大数据运维变得非常重要。要做好大数据运维的工作,需要对集群进行技术上和业务上的合理规划,从技术上,充分利用和理解现有的资源调度系统。今天和大家分享下 58 同城自研的星河-数据开发平台。
今天的介绍会围绕下面三点展开:
- 调度系统的定位与挑战
- 架构设计与演进
- 智能运维最佳实践
分享嘉宾|张梦龙 58同城 资深开发工程师
编辑整理|王雨萌 中文在线
出品社区|DataFun
01 调度系统的定位与挑战
1. 调度系统的定位
任务调度在数据开发平台中起着重要作用。大数据的原始数据一般是从业务库,日志等获取,通过一系列的 加工处理才能提炼出有价值的信息 ,这些过程一般分为以下几步:
- 第一步,经过离线或实时采集进入大数据系统;
- 第二步,然后通过建模,层层加工进行数据分析与处理,提取出有价值的数据;
- 第三步,然后通过建模,层层加工进行数据分析与处理,提取出有价值的数据。
在这整个过程中,会有很多任务负责进行数据的采集、加工和提取。这些任务相互依赖,形成一个大的工作流。数据开发的核心就是需要对这个工作流进行调度。
调度系统负责将底层大数据组建与上层应用连接起来。调度系统保证了大数据开发任务按照上下游依赖关系,准时的、按条件的、依次执行。由此可见,调度系统在数据开发中起到至关重要的作用,说它是“数据中台的心脏”也不过分。
业界解决方案包括像 DolphinScheduler、Airflow、Azkaban 等等。
2. 任务调度系统——挑战
在 58 的业务场景下,任务调度系统的挑战主要包括三个部分:
- 稳定性
日均 25 万的任务,就有上百万的依赖关系,这样就给调度系统的性能和稳定性带来了很大的挑战。除此之外,调度系统还需要具有一定的故障恢复能力。
- 性能
随着任务的增长,还会导致调度性能的急剧下降。并且任务相互链路长,一旦上游报错,很有可能导致下游大面积任务收到影响。
- 扩展
作为数据中台的一个基础能力,调度系统可能会接入各类的业务场景。调度方式、任务种类也会非常多。那么对调度系统的扩展能力,也是一个巨大的挑战。
3. 58 调度系统的发展历程
58 大数据任务调度系统的发展历程,大致分为三个阶段:
- 第一个阶段:2016 年到 2019 年
在这期间,有一套自研的调度系统,主要是为了满足业务需求,但是后期随着业务量的不断增加,原有的调度系统的稳定性和性能已经无法满足要求。
- 第二个阶段:2020 年
到 2020 年的时候,为了解决这种老调度系统的性能问题,我们进行了架构的整体的升级。可以支撑百万级任务的调度,同时支持任务的上下文的参数传递、灰度发布、多维度限流等能力。
- 第三个阶段:2021 年至今
第三个阶段,解决了新架构下,Worker 资源隔离的问题同时我们也开始关注任务智能运维能力,提供了包括像基线监控等功能,降低任务运维成本,提升人效。
在过去的 3 年,58 的任务数每年成倍数增长,从几万增长到当前的 25w万左右,接入的平台数从 4 个增长到数十个,用户当前已覆盖公司几乎所有的业务方。
4. 星河调度系统核心能力
星河调度系统,整体分为五个部分:
- 可视化
星河系统提供了非常友好的可视化界面,方便使用,可以通过拖拉的方式使用,配置依赖关系。并且一些简单ETL操作,是不需要进行编码的。极大程度的降低了使用门槛。
- 丰富的任务类型
星河系统支持大数据领域内常见的包括 Hive、Shell、MR、SparkSQL、Python 等多种任务类型。同时还支持自定义组件,用户可以自己开发,嵌入进我们这个调度系统中来。
- 高可用
随着任务数量的增加,需要调度系统有很高的容错能力,包括节点的失败转移。还有,当任务一旦失败,重试是一个非常重要的能力。
- 依赖
随着业务复杂度变多,调度系统不仅要支持时间依赖,还需要支持事件依赖和自依赖。
- 智能监控
星河系统还可以提供智能监控服务,比如任务可以实时的查看运行日志。且具有丰富的告警的类型,包括后面会着重介绍的基线监控的能力。
02 架构设计与演进
1. 工作流的划分
从工作流定义方式来说,业界整体分为两大流派,一类是静态显示定义工作流,一类是动态隐士定义。
第一类就是静态显示 ,比如 DolphinScheduler、Airflow。属于静态显示定义,是先定义工作流,再创建任务。当出现工作流之间的依赖时,需要调度系统解决跨流依赖的问题,调度模型相对比较复杂。
而且调度定时属性是定义在工作流 Flow 上的,而不是 Job 上,同一个工作流内的任务具有相同的调度时间。
这种方式适合小规模,业务变更少的场景下使用。。
第二类是动态隐式 。在 58 这个体系内,星河调度系统其实是不需要定义工作流的,也就没有 DAG 这个概念,每个任务都是一个独立的个体,都可以去配置调度时间和配置表达式。在这种场景下业务调整、依赖变更更加灵活,且用户管理压力小。调动系统内部的所有任务是一个大图。调度模型以任务为单位进行调度,这种相对比较简单,而且易于用户理解。
2. 探索历程——最初架构
在 2019 年的时候,整体调度系统架构设计是一个主备 Master、Worker集群设计。随着业务增长,每天有 9 万多次的任务调度。这在原有架构下会有很多问题,比如凌晨高峰期延迟达到五分钟以上,延迟对业务的影响是非常大的。
由于是主备设计,所以 存在一个调度单点的问题, 并且吞吐能力有限,缺乏监控,运维能力也有限。因此在 2020 年的时候,我们进行重构。
3. 探索历程——当前架构
整体仍然是 Master/Worker,消息队列的架构。其中 Master 做了去中性化的设计。
首先,分布式 Quartz,定时生成任务实例。由 Master 内一个任务实例扫描的线程,从 DB 里把一批实例领取进master内存,再由 Master 内部的调度器去进行依赖检查、限流检查、资源检查等。一系列检查之后,分发到 Worker去运行。Worker 运行任务是单进程多线程的形式,Worker 内启动一个新的线程运行任务。运行完之后,会把这个任务的状态,通过 ZK 回调到消息队列里面去。这之间所有的任务状态的流转变更,都会由master发送至 Kafka,经过下游的监控组件(Courier),做告警、运行分析、状态同步等。
由于任务运行时实际使用的资源不可控,可能会耗尽 Worker 本机的资源,导致 Worker 服务的宕机。为了解决这一问题,我们使用 CGroup 对任务资源隔离,将 Worker 的服务和任务进程进行细粒度的隔离,从而保证了 Worker 服务的稳定性。
这种架构支持水平扩展。并且,在分发过程中,支持了按任务类型、时间段等条件自定义规则分发,这在一些极端场景如故障恢复的场景下会很有用。Worker 执行组件是插件式开发,支持热部署,每次任务运行时,动态加载任务执行组件。Master 支持灰度发布,Worker 支持蓝绿部署滚动升级。Worker 无 DB 依赖。支持多 Yarn 集群扩展。
4. 调度 Master 高可用
接下来,讲一讲调度master是如何实现高可用的。master在启动时注册zk。如图所示,第三台master节点突然宕机,zk会把这个节点下线通知给其他master。然后剩余master会去获取分布式锁,进行容错处理。首先处理zk回调事件,更新任务状态,重置未运行任务实例更新至db,存活的master会重新拉起实例。
5. 去中心化 Master
去中心化 Master,采用动态中心化设计,让管理者被动态选举出来,而不是通过事先的预置。这种方式是基于 Zookeeper 分布式锁,选举临时管理者来进行任务调度。保证集群中同一时刻只有一个 Master 在消费数据。
上面这张图,是一个整体的流程图 。首先,Master 去 Check 自己的 CPU 内存是否满足要求。满足要求之后,才获取锁,拿到锁的 Master 去 DB 进行查询,领取一批任务实例到内存中来,然后进行任务状态更新,提交状态和事物,处理完成之后释放锁就可以了。这一批任务实例都是交给这个 Master 去处理。
03 智能运维最佳实践
1. 任务运维面临的问题
在实际的数据开发场景中,会出现很多任务运维方面的问题,如下就是经常会遇到的一些问题:
- 任务多、运维繁琐
- 上下游依赖关系复杂
- 值班同学起夜频繁
- 任务诊断困难
2. 分级保障
面对上述问题,58 提供了分级保障的能力。对任务进行划分,划分之后,任务数和服务质量就形成了一个金字塔分布。
其中, P0 任务,就是我们最核心的任务,比如公司级的数据。 这种任务需要严格控制产出时间,比如影响收入的任务、依赖链顶层的任务。如果发生此类任务异常,平台都会第一时间跟进处理。
第二种, P1任务,是重要级别任务。 此类任务属于核心的业务数据。此类任务,也是严格控制产出时间的。比如,部门内部的核心业务系统相关的任务,还有跨部门合作的数据等等。这类任务异常,平台会在早晨6点前响应。
第三种, P2任务,就是次要任务,比如业务普通数据 。此类任务,保证当天产出即可,这种任务一般都是离线作业。
通过分级保障的方式,最终达到核心任务优先保证。平台同学可以在第一时间响应重要任务。
3. 基线监控
接下来介绍一个比较核心的能力,基线监控。
在海量数据开发场景中,因为业务繁多,跨团队,数量大,且任务间相互依赖,定位异常任务困难。
为了保证数据的准时产出,会遇到无法及时发现上游依赖任务的延迟,当发现延迟的时候,已经错过了最佳补救时间。
为了解决上述问题,我们引入了基线的概念。可以理解为,定义一组任务的最迟产出时间,将比较核心的任务挂到基线上。
基线具备两个指标,预警时间与破线时间 ,如果基线上的所有实例的最晚预计完成时间超过基线所设置的预警或破线时间,就会触发报警,将延迟信息告知到项目值班组的成员。借此可以使得业务团队可以提前感知预警信息,已达到提前解决的目的。
针对基线任务,我们提供了一个 关键路径的功能 ,通过关键路径,可以找到影响基线任务最长的一个产出链路。
4. 预期时间推算模型
接下来,介绍基线的核心计算模型,预期时间推算模型。
预期时间推算模型,本质上是通过任务的历史运行时间去推算它是否可能发生延迟。
首先,调度系统会由一个定时的服务,在指定时间,拉取基线任务全部上游,构建一个大的 DAG 图,并且进行一个链路分析。通过这个任务的历史平均时长和 7 天的一个平均时间,以及任务最晚完成时间去推算。
5. 基线监控实践
接下来,讲一个应用场景。
凌晨 12:30,值班同学收到了一个基线报警。内容是,8:30 基线余量不足,可能会破线。
十分钟之后,值班同学上线了,由基线监控定位发现延迟任务,经排查发现是有同学夜间提交了补数据任务占用队列资源,导致队列堆积,此时值班同学,去kill掉一些非核心补数据任务,同时加大任务的队列资源。让任务加快运行。在凌晨 1:20 的时候,任务追上来了。
这个时候,基线会发送一个 8:30 的基线预警恢复安全,任务已追上。通过这种方式,将一场延迟事故,扼杀在摇篮中。
6. 基线关键路径实践
基线关键路径的目标是为了优化链路,保障数据 SLA。我们可以通过关键路径分析,找到影响产出的最长的一个链路,寻找上游可能存在优化的点。
整体来说,有三个步骤:
- 第一步,优化任务逻辑,找到运行时间长的任务去优化。提升资源的利用率,缩短任务运行时长。
- 第二步,调整任务时间,在基线关键路径中,会提供一个余量指标,就是上游完成时间和下游运行时间差。可以根据这个时间调整任务调起时间,让任务提前运行、提前产出的效果。
- 第三步,增加资源。基线最上游核心任务的资源优先保证。
7. 值班组
值班组,是日常应用非常多的一个功能。星河调度系统,支持非常多的排班策略,配置灵活。值班组支持报警升级、以及恢复的功能。并且支持多通道的报警,比如短信、邮件、语音等。
8. 补数据场景
接下来,介绍一下补数据的应用场景。
比如,上游一个核心任务数据出错,但是并没有导致任务报错,只是单纯的数据错误。那么这种数据的错误将会扩散,对下游任务数据准确性产生影响,很可能影响到一些核心报表。
那么,这种场景下,数据怎么恢复呢?而且,下游有一些任务是禁止重跑的。比如图中置灰的任务。那么这种不需要重跑的任务是怎么处理的呢?
首先,补数据需要由业务方定位问题源头任务,并创建补数据。
然后,通过补数据里的展开全部下游任务,发起补数重跑,因为涉及到一些跨部门任务,所以需要审批。
并且,我们会对任务添加一些参数。保证任务可以在第一时间拿到资源。
接下来,开始运行,提交任务,这个时候补数据会直接清掉下游的任务。等待上游任务结束之后,未开始的任务恢复调度。
通过这种方式,用户可以快速完成恢复数据的动作。
9. 未来规划
最后介绍一下调度系统的未来规划,分为三大方面:
- 数据质量:比如前文中上游数据出错的场景,可以通过数据质量和任务调度的结合来解决,在产出数据时检查数据是否正确,干预任务运行状态。
- 智能运维:根据任务推算资源队列在时间段内的紧张程度,动态调整队列资源上限。
- 智能数仓:根据表周期及输入输出,自动把任务创建出来,配置依赖,进一步降低使用门槛。
本次分享就到这里,欢迎大家关注交流。
04 问答环节
Q1:任务量大量增长然后资源消耗有遇到什么挑战吗?例如任务测试跑不动。
A1:在第一版中,随着任务的增加,会出现依赖延迟。在新的调度系统中,为了避免大量的轮询数据库。使用事件机制,通知调度系统,及时更新正在轮询的任务状态。
Q2:基线任务的依赖关系是每天晚上十二点前计算的吗?如果凌晨有人改任务依赖关系,会实时更****新基线任务关系吗?
A2:更改依赖关系,在检查的时候,确实会发生这种任务已运行完,触发报警的情况。针对这种场景,后面会增加一些兜底策略来解决。
Q3:哪些任务会被定义为基线任务?
A3:取决于业务方,由业务方定义哪些任务关注产出时间。
Q4:什么是多维度限流?
A4:我们对任务、用户、补数据三个维度限制任务实例运行的并行度。