桑强

2018 年 11 月 5 日

目录

  1. 背景 2

  2. 设计目标 2

    2.1 用户交互的产品功能 2

    2.2 后台调度功能 3

    2.3 任务执行器功能 4

    2.4 任务运维功能 5

    2.5 平台对外功能 6

  3. 平台价值 7

  4. 平台建设 7

    4.1 用户功能实现说明 8

    4.2 调度周期设计说明 12

    4.3 调度策略设计说明 13

    4.4 调度流控设计说明 15

    4.5 系统高可用设计说明 16

    4.6 任务失败策略 17

    4.7 任务运行分析设计说明 18

  5. 现状和未来 20

  6. 后续 21

1. 背景

在数据仓库的建立过程中,核心技术是抽取、转换、装载(ETL),它为数据仓库提供及时、高质而准确的数据。由于 ETL 包括众多的处理任务,且这些任务之间有一定的约束关系,如何高效的调度和管理这些任务是数据仓库 ETL 实施中非常重要的工作,也是提高数据仓库开发效率和资源利用率的关键。

在大数据平台,随着业务发展,每天承载着成千上万的 ETL 任务调度,这些任务的形态各种各样。怎么样让大量的 ETL 任务准确的完成调度而不出现问题,甚至在任务调度执行中出现错误的情况下,任务能够完成自我恢复甚至执行错误告警与完整的日志查询。IDE 大数据离线任务调度系统就是在这种背景下衍生的一款分布式调度系统。

在阐述 IDE 平台之前,首先讨论一下作业调度系统和资源调度系统的区别,因为往往有同学把这两者混为一谈。资源调度系统的典型代表比如:Yarn/Mesos/Omega/Borg,还有阿里的伏羲,腾讯的盖娅(Gaia),百度的诺曼底 (Normandy) 等等。我们今天的内容主要讲述的是作业调度系统。不过,IDE 除了完成了大数据的任务各种复杂调度外,还包含了任务开发、依赖组织、状态维护、任务监控、任务治理、服务监控、动态扩缩容等诸多内容。

2. 设计目标

2.1 用户交互的产品功能

  • 从用户交互的产品功能上看,主要包括以下几点:

    提供可视化的操作页面,任务的开发、运维、监控等操作图形化页面化

  • 对拥有权限的作业/任务进行管理,包括设计调度周期和时间、添加、修改、删除任务节点、组织依赖关系、查询等

  • 对任务运行状态进行查看、监控,进行杀死、重跑、补数据、查看运行日志、查看操作记录等运维操作

  • 支持作业失败自动重试,可以设置自动重试次数,重试间隔等

  • 支持任务失败报警,超时报警,到达指定时间未执行报警等异常情况的报警监控

  • 支持动态按应用/业务/优先级等维度调整作业执行的并发度调度时间和数据时间的分离

  • 支持历史任务独立重刷或按照依赖关系重刷后续整条作业链路允许设置作业生命周期,可以临时禁止或启用一个周期作业

  • 提供任务链路依赖分析,便于分析任务的上下游影响,调度执行延迟的溯源分析

  • 提供任务的异常自助诊断分析和优化建议,便于用户及时合理的整改任务

  • 提供任务的导入导出、复制等功能,便于用户在多个运行环境和数据中心快速开发任务,提高开发效率

  • 支持多用户的并发访问控制,便于多用户的协同开发而不出问题

  • 提供任务的发布管控和权限管控的配置功能,规范任务开发流程,降低生产风险

详细的用户产品功能如图 1 所示:

(图 1:用户产品功能清单)

2.2 后台调度功能

从后台调度系统自身逻辑看,主要包括以下几点:

  • 准实时调度,支持仅执行一次和周期性任务(分钟、小时、天、周、月),作业计划的变更,即时生效
  • 灵活的调度策略,触发方式需要支持:时间触发,依赖触发或者混合触发,支持多种依赖关系等
  • 做好多租户隔离,执行器资源隔离、内建流控,负载均衡和作业优先级等机制
  • 系统高可用,组件模块化,核心组件无状态化
  • 支持任务失败转移、执行机器资源发现和健康度评估
  • 提供调度内存对象的跟踪捕捉,便于复杂场景下的任务调度异常原因分析
  • 开放系统接口,对外提供 REST API,便于对接周边系统
  • 提供任务状态的发布订阅,便于对接外部系统根据任务状态的变化完成其他业务逻辑

详细的调度模块功能如图 2 所示:

(图 2:调度模块的功能清单)

2.3 任务执行器功能

从任务执行器方面看,主要包括以下几点:

  • 支持多种类型大数据任务执行,包括:数据交换、Hive、Python、Java、MapReduce、Spark、SparkSQL、Sqoop、机器学习任务等
  • 支持任务进程之间的隔离,防止任务之间的互相影响
  • 支持自动健康检查和状态汇报,不健康时及时汇报调度系统,不再继续领取执行任务
  • 部署无状态,支持动态扩缩容
  • 能够支持异常任务跟踪,高耗 CPU 和内存的任务的自助发现和记录
  • 支持固定、动态、智能分配任务执行资源,合理利用服务器资源
  • 能够进行很好的容错处理

详细的任务执行器功能如图 3 所示:

(图 3:任务执行器功能清单)

2.4 任务运维功能

从任务运维角度看,主要包括以下几点:

  • 提供任务状态的监控告警功能,任务异常时及时告警和干预,避免造成任务堆积和业务异常
  • 提供自动化部署,包括调度模块和执行器模块,支持动态扩缩容
  • 任务执行进度和完成时间预测
  • 提供任务运行分析和自助诊断功能
  • 任务日志分析,自动识别错误原因和类型
  • 提供 PC 和移动端运维功能,便于随时随地发现和解决问题
  • 提供知识库建立和应急解决方案、系统降级方案

平台运维能力如图 4 所示:

(图 4:平台运维能力)

2.5 平台对外功能

从平台对外的能力输出方面,主要包括以下几点

  • 提供任务创建、状态查询和杀死、重跑、补数据等运维接口,便于复杂业务场景根据业务逻辑动态操作任务
  • 接口授权和安全性校验,打通与外部系统的对接,扩展平台和业务系统的大数据开发能力

平台的对外服务能力如图 5 所示:

(图 5:平台对外服务能力)

3. 平台价值

目前苏宁八大产业齐聚并发,数据爆发增长,依托大数据平台打通各产业数据,服务智慧零售。在整个智慧零售中和大数据开发战略中,ETL 是 BI(商业智能)的基础,数据调度是 ETL 的灵魂。

使用该平台能够对企业中批量运行作业进行集中管理,并通过强大的调度引擎功能实现作业的逻辑运行,并提供直观详细的监控手段,辅助运维工作。平台为系统的开发与运维提供以下价值:

  • 解放人力,提高工作效率。

    通过使用平台对大数据作业进行自动化管理和监视。当系统出现异常的情况时,以邮件、短信等多种方式通知相应的管理员进行人工参与,大大减轻了现有管理员的工作压力,提高了 IT 系统的管理效率。

  • 灵活的配置及告警机制。

    利用平台提供的灵活配置功能和完善的告警处理机制,大大避免了因为故障处理不及时而带来的损失。

  • 强大的权限管理。

    将各种运行操作权限合理的分配给作业及操作人员,进行权限限定的批量作业设置、运行和监视,使核心权限得到有效保护。

  • 全面的作业运行状况分析。

    通过自动采集作业运行状况、时间,分析故障分布、作业运行时长等业务运行指标,整体把握系统运行健康度。

4. 平台建设

IDE 平台旨在为用户提供从数据源申请、任务创建、任务调度编排、任务运维、任务告警、运行分析等一站式大数据开发平台,帮助企业快速完全数据中台搭建。

IDE 大数据开发平台在架构上分为任务管理平台、任务调度、任务执行、任务监控、和 API 服务五部分。平台采用了先进的 JavaEE 技术架构,具有很强的跨平台性。部署简便,维护简单,容易使用。支持分步式的多机集群,能承载大规模数据的高负荷运行,具有良好的稳定性。平台采用了多层架构,结构清晰,具有良好的扩展性、稳定性和容错性。

平台整体架构如图 6 所示。

(图 6:平台架构设计)

接下来,我们重点阐述部分设计实现细节和相关实践经验。

4.1 用户功能实现说明

用户交互的产品功能,注重图形化可视化,易操作易维护

让用户能够尽可能的自助服务,同时降低操作代价,减少犯错的可能性。支持当日任务计划和执行历史的查询,支持周期作业信息的检索,包括作业概况,历史运行流水,运行日志,变更记录,依赖关系、任务运行明细查询等。这部分功能的目的,是为了让系统更加透明,让业务更加可控,让排查和分析问题更加容易。尽可能的让一切作业任务信息和变更记录都有源可查,做到任务操作都可以追踪和溯源。

  • 主要操作全部图形化可视化,降低用户使用成本,提高开发效率。

    任务流和任务的开发设计页面如图 7 所示

(图 7:任务开发主页面)

  • 任务流的开发提供画布操作,任务节点拖拽方便,编排简单,如图 8 和图 9 所示。

(图 8:任务流设计画布)

(图 9:任务节点依赖关系编排)

  • 任务配置可视化,简单化,降低犯错率,如图 10 所示。

(图 10:任务可视化配置页面)

  • 任务运行状态进行查看、监控,进行杀死、重跑、补数据、查看运行日志、查看操作记录等运维操作。如图 11 和图 12 所示

(图 11:任务运维管理页面)

(图 12:任务流画布上的运维操作)

  • 提供丰富的告警,便于及时跟踪任务执行状态,降低事故率。如图 13 和 14 所示

(图 13:任务告警类型)

(图 14:任务告警配置页面)

  • 提供任务链路依赖分析,便于分析任务的上下游影响,调度执行延迟的溯源分析。

    如图 15 所示。

(图 15:任务执行依赖分析)

4.2 调度周期设计说明

准实时调度,支持周期性任务(分钟、小时、天、周、月),作业计划的变更

所谓准时实调度,首先指的是平台会按照各个上线的任务流的调度时间生成调度执行计划,当触发时间到了,平台会按照调度执行计划精确的生成任务流实例和任务实例。但是在任务执行上,并不保证准实时的分配机器执行。实际上平台以整体资源使用情况为最高原则,并按照一定的限流策略控制任务的执行,比如:任务优先级、任务组并发度、平台任务并发数、任务特定执行时间等因素。在保证平台资源允许的情况下,尽量按时执行任务。为了保障任务的实时性,必须保障任务资源的可用性和计划可控性。

作业计划的变更允许用户调整自己上线的任务流执行计划。比如天任务调整为小时任务,调度开始时间从 1 点开始调整为 2 点开始。如果对上线已经生成任务实例的任务流进行调整,必须下线后再调整,此时会提示用户杀死当前的任务,然后完成计划调整。如果上线的任务流还未到触发时间,没有生成任务实例,用户可以及时修正,并对任务执行无影响。

图 16 是平台的任务流频率支持的类型。

(图 16:任务流调度频率类型)

4.3 调度策略设计说明

灵活的调度策略,触发方式需要支持:时间触发,依赖触发或者混合触发,支持多种依赖关系等

首先,在一些复杂的业务场景里,存在跨流任务依赖调度,不同的任务流的频率和时间周期不一致,比如天依赖小时、周依赖天等

其次,针对不同优先级的任务,在资源利用高峰和任务执行高峰时段,可以适当调整中低优先级的任务的执行时间窗口,避免低优先级的任务和高优先级的任务进行资源抢占,错峰执行,提高资源利用率和均衡性。

在解决跨流依赖方面,平台目前仅支持 大频率依赖小频率,比如:天依赖小时、周依赖天、月依赖天,以及同频依赖,比如:小时依赖小时、天依赖天、周依赖周、月依赖月。平台在设计之初限制了一个任务只能属于一个任务流,不允许任务跨流存在,如何解决跨流依赖的问题呢?我们建议用户对任务创建任务事件,可以理解成任务的一个执行副本。这个事件是允许跨流存在的。如果一个任务流需要依赖另外一个任务流的的某个任务,只要依赖这个任务的事件即可,通过事件我们将任务流进行串联起来。如图 17 所示。

(图 17:任务事件依赖)

在解决跨系统之间的依赖问题,我们提供了 FTP 事件,即在 FTP 服务器上建立标识文件,一个事件对应一个标识文件地址,当 FTP 服务器上的标识文件生成的时候,我们认为业务系统已经完成作业,需要触发平台任务执行。

因为 FTP 事件存在实时性和稳定性的问题,即调度服务是不停的轮询各个 FTP 事件服务器,当 FTP 服务器负荷较重,往往会连接超时,导致扫描文件标识延迟,进而导致下游任务延迟,我们对用户系统了 API 事件,即业务系统作业完成后可以调用平台的 API 接口,触发下游任务执行,解决了时效性和稳定性的问题。

(图 18:事件列表)

4.4 调度流控设计说明

做好多租户隔离,执行器资源隔离、内建流控,负载均衡和作业优先级等机制

大多数的工作流调度系统,多租户的隔离比较简单,业务上租户之间完全独立,租户之间的业务很难相互关联。同一租户的工作流方面也不能互相依赖和关联。更多的考虑是物理资源层面的隔离,这个,多半通过独立集群或者虚拟化的方案来解决,同一租户内部,做得好的可能再考虑一下业务队列管理。

我司的业务环境则不适合采取类似的方案,首先从业务的角度来说,不同的业务组(亦即租户),虽然管理的作业会有不同,但是往往不同租户之间的作业,相互依赖关系复杂,犬牙交错,变化也频繁,基本不可能在物理集群或机器的层面进行隔离,业务组之间的人员流动,业务变更也比较频繁。

其次在同一租户业务内部,不同优先级的任务,不同类型的任务,不同应用来源的任务,包括周期任务,一次性任务,失败重试任务,历史重刷任务等各种情况,也有不同的资源和流控管控需求。

目前本平台在开发实践中,主要从以下几方面进行限流控制:

  • 任务优先级,任务分为高中低三个优先级,分别对应底层 yarn 的资源调度不同的权重,高优先级的优先分配执行机器和计算资源
  • 系统调用和用户补数据操作分开,平台绝大部分的任务执行都是依赖自身的调度周期执行,无需用户干涉,优先保证这部分的任务的执行资源;在某些业务场景下,需要对历史数据进行弥补,通过指定运行的数据时间强制任务执行,这类任务往往不是很紧急,可以将优先级降低
  • 失败重试和用户重跑优先级高于系统调用的优先级。当任务出现异常需要进行重跑或者重试,需要人工进行干预的情况下,一般属于紧急情况,需要优先保证这部分的任务的计算资源
  • 部分任务可以设置固定时间,错峰执行,避免高峰时段的资源压力
  • 对于同一任务流内同一层级内部的多个任务,可以设置任务组,通过设置任务组的并发度来限制任务的并行个数,降低对业务系统的访问压力。这个往往在对分库数据库或者同一数据库进行多任务访问操作的时候,减轻业务数据库的访问压力,可以限制任务的并行执行个数
  • 按照任务类型进行限流。平台支持的任务类型很多,当底层对应的计算或者存储资源压力过大,可以限制某些类型的任务的并发个数,降低底层的计算压力。这个限流可以做到平台全局层面,也可以做到系统账号层面。
  • 可以为某些类型任务或者某个系统账号下的任务指定固定的机器资源执行,这样可以降低大资源消耗任务对其他任务的资源抢占,保障特殊任务的资源需求,也可以进行部分账号的任务的灰度发布,降低生产事故率
  • Worker 节点的被动负载反馈(负载高的情况拒绝接收任务)和主节点的主动负载均衡(轮询和 Worker 节点并发数控制等策略)。在分配任务和领取任务方面都进行了任务队列深度控制,防止过度分配和过度领取而导致分配不均而引起的任务堆积

4.5 系统高可用设计说明

系统高可用,组件模块化,核心组件无状态化

从系统架构的角度出发,模块化的设计有利于功能隔离,降低组件耦合度和单个组件的复杂度,提升系统的可拓展性,一定程度上有利于提升系统稳定性,但带来的问题是开发调试会更加困难,从这个角度来说又不利于稳定性的改进。所以各个功能模块拆不拆,怎么拆往往是需要权衡考虑的。

平台采用常见的主从式架构,按照功能模块划分清晰,职责单一而不紧耦合,避免繁重复杂的业务耦合设计。Master 节点负责作业计划的管理和任务的调度分配,worker 节点负责具体任务的执行。用户通过 Web 控制后台管理作业,而 Web 控制后台与 Master 服务器之间的交互透过 Rest 服务来执行,Rest 服务也可以给 Web 控制后台以外的其它系统提供服务(用于支持外部系统和调度系统的对接)。平台部署架构图如图 19 所示。

(图 19:部署架构图)

Master 调度除了引入 Quartz 的负责时间调度外,核心的就是任务实例在执行过程中的状态变化以及变化后触发的操作逻辑。任务实例状态的变化切换目前仿照 yarn 的状态机实现原理,重新进行了改造封装。这块的核心组件受限研发时间和技术问题没有做到无状态。

这里所说的无状态化,更多的强调的是各个调度组件运行时状态的持久化,在组件崩溃重启后,所有的运行时状态都应该能够通过外部持久化的数据中快速的恢复重建。

为了保证状态的一致统一,平台的所有作业和任务的信息变更,无论是用户发起的作业配置修改,还是执行器反馈的作业状态变更,都会提交给 Master 节点同步写入到数据库。

在 HA 方面,按照准实时的设计目标,平台并没有打算做到秒级别的崩溃恢复速度,系统崩溃时,只要能在分钟级别范围内,重建系统状态,就基本能满足系统的设计目标需求。

所以其实高可用性设计的重点,关键在于重建的过程中,系统的状态能否准确的恢复。比如,主节点崩溃或维护期间,发生状态变更的任务在主节点恢复以后,能否正确更新状态等等。而双机热备份无缝切换,目前来看实现难度较大(太多流程需要考虑原子操作,数据同步和避免竞争冲突),实际需求也不强烈,通过监控,自动重启和双机冷备的方式来加快系统重建速度,基本也就足够了。

另外,为了提高系统局部维护/升级期间的系统可用性,平台支持 Worker 节点的动态上下线,可以对 worker 节点进行滚动维护,当 Master 节点下线时,Worker 节点和 ZK 节点也会缓存任务的状态变更信息,等到 Master 节点重新上线后再次汇报结果。所以一定程度上也能减少和规避系统不可用的时间。

4.6 任务失败策略

支持任务失败转移、执行机器资源发现和健康度评估

作业失败的原因很多,有上游表数据不对、脚本配置错误等主要原因,也有一些临时性的原因,比如网络原因、外部 DB 负载原因,可能重试了就好了。所以,为了降低运维代价,我们需要可以支持相关重试策略的配置。当然,更加理想的情况是系统可以智能的根据失败的原因自动采取不同的策略。

(图 20:任务失败重试配置)

(图 21:任务失败重试运行记录)

4.7 任务运行分析设计说明

任务运行自助分析,对任务进行诊断分析,便于用户发现和调整问题。

针对失败的任务提供任务运行日志,方便查看出错问题,识别常见的错误模式,明确的告诉用户错误类型,如果可能,告知解决方案。

(图 22:任务执行日志)

针对成功的任务提供诊断信息,比如某个任务跑得慢,是因为 GC,还是数据量变化,是因为集群资源不够,还是自身业务在某个环节被限流?各种情况下,用户该如何应对解决,能否自动给出建议?此外,是否能够定期自动诊断,及时提醒用户,敦促用户自主优化?避免问题积压到影响业务正常运行的时候才被关注。

目前我们自研了“华佗”平台,可以针对在 hadoop 上执行的 hive、mr、spark 任务进行运行诊断,对于出现 GC、数据倾斜、资源消耗等方面做出综合的诊断。

(图 23:任务运行诊断)

(图 24:任务运行诊断详情)

5. 现状和未来

整体来说,本文前面所描述的设计目标,平台基本上都已经实现了。经过 3 年的开发和持续改进过程,当前调度系统日常大概日均承载约 20 万个周期调度作业,接入集团绝大部分的离线计算相关的开发任务。平台目前趋于稳定,由于系统自身原因造成的大规模系统故障已经非常罕见。

但是,由于前期平台过分追求满足用户的离线作业需求,未考虑任务配置的合理性、优先级和业务的关联性以及任务之间的血缘关系等,在后期任务量快速上升的情况下,经常出现资源竞争激烈、任务小文件碎片化严重、任务变更影响和追踪定位链路过长等等问题。另外在极端环境下,尤其是在高负载大流�