|
| 1 | +# 作业帮基于 DolphinScheduler 的数据开发平台实践 |
| 2 | + |
| 3 | +## 0 前言 |
| 4 | + |
| 5 | +随任务数量、任务类型需求不断增长,对数据开发平台提出了更高的要求。本文主要分享我们将调度引擎升级到 Apache DolphinScheduler 的实践经验,以及对数据开发平台的一些思考。 |
| 6 | + |
| 7 | +## 1 背景 |
| 8 | + |
| 9 | +公司大数据平台架构: |
| 10 | + |
| 11 | + |
| 12 | + |
| 13 | +数据计算层承接了全公司的数据开发需求,负责运行各类指标计算任务。 |
| 14 | + |
| 15 | +批计算任务运行在 UDA 数据开发平台,支持任务全链路的开发场景:开发、调试、环境隔离、运维、监控。这些功能的支持、任务的稳定运行,强依赖底层的调度系统。 |
| 16 | + |
| 17 | +原有调度系统自研,随着任务类型新增、任务数量增多,暴露诸多问题: |
| 18 | + |
| 19 | + |
| 20 | + |
| 21 | +稳定性:频繁出现 MySQL 连接不释放、锁超时等问题;数据库压力进一步导致调度性能瓶颈,任务无法及时调度 |
| 22 | + |
| 23 | +可维护性:核心调度器通过 php 开发,代码古老又经历多次交接,外围模块实现时采用了 go java python 多种语言;再加上功能上也存在单点,维护成本很高 |
| 24 | + |
| 25 | +扩展性:业务高速发展,不同任务类型需求越来越多,但是调度作为底层服务在支撑上一直力不从心 |
| 26 | + |
| 27 | +可观测性:由于是定时 nohup 启动任务进程的方式,经常出现任务跑飞了的情况,系统暴露出来的可观测指标几乎为 0 |
| 28 | + |
| 29 | +对调度系统的核心诉求,分为功能和系统两部分: |
| 30 | + |
| 31 | + |
| 32 | + |
| 33 | +功能上,调度系统的核心能力是解决数仓构建的依赖调度问题,因此需要支持多种依赖形式;支持丰富的任务类型,同时可扩展自定义新的任务类型。以及上线管控、历史版本回滚、任务血缘等提高易用性的能力。 |
| 34 | + |
| 35 | +系统上,稳定性是第一位的,因此需要具备高可用的能力。同时支持租户隔离、线性扩展、可观测,以方便的对系统进行开发、维护和预警。 |
| 36 | + |
| 37 | +历史上我们调研过 Airflow、DolphinScheduler 等多种选型,在过去大概一年的时间里,我们将大部分任务从自研调度系统迁移到了 DolphinScheduler。 |
| 38 | + |
| 39 | +### 当前调度系统概况 |
| 40 | + |
| 41 | +- 任务类型:HiveSQL、SparkSQL、DorisSQL、PrestoSQL、部分 shell 任务,均通过 DolphinScheduler 调度;遗留部分 shell 任务在原调度系统 |
| 42 | +- 任务数量:DolphinScheduler 天级别调度数万工作流实例,数十万任务实例,高峰时期同时运行 4K+ 工作流实例。迁移完成后,预计工作流实例实例数翻倍 |
| 43 | + |
| 44 | +## 2. 数据开发平台实践 |
| 45 | + |
| 46 | +### 2.1. 基于 DolphinScheduler 的改造 |
| 47 | + |
| 48 | +改造围绕稳定性和易用性展开,对于原有调度系统设计良好的功能,需要兼容以降低任务迁移成本。基于 DolphinScheduler 做升级: |
| 49 | + |
| 50 | + |
| 51 | + |
| 52 | +由于 DolphinScheduler 架构设计较好,优化基本可围绕单点或者复用现有能力展开,无需大刀阔斧。 |
| 53 | + |
| 54 | +SQL任务都是多个 SQL 组成,但原生的 SQL 任务只能提交单个。为确保系统简洁,没引入各类 client(hive-client、spark-client 等),而是通过 SQL 解析、连接池管理方式重构等方式,通过 JDBC 协议支持了单任务多 SQL 的提交。 |
| 55 | + |
| 56 | +同时充分复用 DolphinScheduler 对数据源的设计,赋予数据源更多的属性,如连接不同的 HiveServer2、Kyubbi、Presto Coordinator 等,对于计算运行在 Yarn 上的任务,单个数据源也只允许使用单个队列。对数据源增加权限控制,这样不同任务就只能使用有权限的集群资源。 |
| 57 | + |
| 58 | +将资源文件、DQL 运行的结果数据,统一上传到对象存储,以确保做到 Worker 真正的无状态。(*注:日志上传进行中*) |
| 59 | + |
| 60 | +### 2.2. 平滑的大规模迁移 |
| 61 | + |
| 62 | +尽管两个调度系统,在功能及架构巨大差异,但要平滑迁移,因为: |
| 63 | + |
| 64 | +1. 原调度系统服务多年,用户对于功能设计、系统专有字段名词等都已经养成习惯 |
| 65 | +2. 2W+工作流的迁移预计耗时较久,涵盖公司众多重要数据流,问题影响程度高 |
| 66 | +3. 用户覆盖了公司众多业务线(平台、直播课、硬件、图书),问题影响面广 |
| 67 | + |
| 68 | +如此大规模迁移做到对用户几乎无感知,主要依赖新旧调度系统的打通和 DIFF。 |
| 69 | + |
| 70 | +#### 2.2.1. 新旧调度系统打通 |
| 71 | + |
| 72 | +任务迁移阶段: |
| 73 | + |
| 74 | +- 一部分任务运行在新的调度系统 |
| 75 | +- 一部分运行在原调度系统 |
| 76 | + |
| 77 | +就要解决两个问题: |
| 78 | + |
| 79 | +- 用户能够查看所有任务实例的运行情况,包括一些内部已经习惯的调度名词(run_index、result_ftp、log_ftp、csv_result_path 等),这部分信息在 DolphinScheduler 调度里显然没有 |
| 80 | +- 任务和任务之间有依赖关系,两个系统间调度任务时,也需要查询对方系统调度的任务实例状态,用于判断当前任务依赖是否就绪 |
| 81 | + |
| 82 | +因此,我们在迁移阶段架构: |
| 83 | + |
| 84 | + |
| 85 | + |
| 86 | +#### 核心设计 |
| 87 | + |
| 88 | +任务实例状态统一到原调度系统数据库,对平台而言: |
| 89 | + |
| 90 | +- 查询方式、字段、API 跟之前一致 |
| 91 | +- 任务更新时,如该任务已迁移到新调度系统,则同时更新 DolphinScheduler 里的工作流定义 |
| 92 | + |
| 93 | +因此平台在使用上,对用户无感知。 |
| 94 | + |
| 95 | +其次修改 DolphinScheduler DependentTaskProcessor 的代码,支持查询 DolphinScheduler 及原有调度系统的任务实例状态。这样 DolphinScheduler 调度的任务,就可以自由依赖两个调度系统的任务实例了。因此在调度能力上,也做到了对用户没有感知。 |
| 96 | + |
| 97 | +上述架构,未来在迁移完成后,就可以仅通过 UDA-API + DolphinScheduler 提供完整的调度能力。 |
| 98 | + |
| 99 | +在配置依赖易用性也优化,支持多种依赖方式:文件依赖、任务依赖、hql 依赖、prestosql 依赖等。后两者都需要用户手动配置查询对应表,都优化为表依赖。平台解析用户的 sql,针对读取的表,自动添加对应的依赖: |
| 100 | + |
| 101 | +- 既提高易用性 |
| 102 | +- 也对用户屏蔽了底层具体表存储类型(Hive/Presto/Iceberg/...)的细节 |
| 103 | + |
| 104 | + |
| 105 | + |
| 106 | +对任务依赖,也支持了全局搜索、偏移量、偏移单位以进一步提高易用性。 |
| 107 | + |
| 108 | +#### 2.2.2. 新旧调度系统 DIFF |
| 109 | + |
| 110 | +其次是新旧调度系统的 DIFF。作为基础平台,服务的业务线众多,再加上 YARN 资源极其紧张,因此我们对调度系统的稳定性要求很高。为了确保迁移顺利,专门基于 DolphinScheduler DryRun 的能力定制: |
| 111 | + |
| 112 | + |
| 113 | + |
| 114 | +所谓镜像任务,是指我们在迁移新调度之前,会先在 DolphinScheduler 镜像一份完全相同的任务,任务同样经过变量替换等操作,只是该任务标记了不真正执行。 |
| 115 | + |
| 116 | +这样就可比较两个系统间DIFF: |
| 117 | + |
| 118 | +- 调度时间是否基本一致:用于验证依赖配置、定时设置等的兼容性 |
| 119 | +- SQL 是否完全一致:验证变量替换、SQL 屏蔽、队列配置后,真正提交的 SQL 是否完全相同 |
| 120 | + |
| 121 | +经上述空跑观察一段时间,确保无 diff 后,线上任务就真正迁移到新的调度引擎。 |
| 122 | + |
| 123 | +#### 2.2.3. 系统的可观测性 |
| 124 | + |
| 125 | +DolphinScheduler 对外提供了 Prometheus 格式基础指标。增加一些高优指标,同时转化为 Falcon 格式对接到公司内部的监控系统。 |
| 126 | + |
| 127 | +通过监控大盘来查看调度系统的健康状况,并针对不同级别的指标和阈值,配置电话/钉钉报警: |
| 128 | + |
| 129 | + |
| 130 | + |
| 131 | +可观测性提高后,分析问题的人力成本也得到控制,例如对于这种曲线: |
| 132 | + |
| 133 | + |
| 134 | + |
| 135 | +容易观察到在非工作时间曲线值基本为 0,因此就能判断指标异常(=1)很可能是用户修改后触发的,相比之前出现问题只能靠猜和逐台机器登录分析日志的方式,通过 metrics 分析能够更早发现和预警问题。 |
| 136 | + |
| 137 | +在迁移启动后,对于 misfire、worker 线程池饱和度、连接池饱和度、io-util、overload 等指标,都重点关注和评估,以确保迁移顺利。 |
| 138 | + |
| 139 | +### 2.3. 迁移收益 |
| 140 | + |
| 141 | +#### 2.3.1 数据库 |
| 142 | + |
| 143 | +a. QPS: 10000+ -> 500 |
| 144 | + |
| 145 | +b. 负载:4.0 -> 1.0 |
| 146 | + |
| 147 | +#### 2.3.2 资源使用降低 65% |
| 148 | + |
| 149 | +迁移过程中,通过 DolphinScheduler 以极低的开发成本支持了 SparkSQL、DorisSQL,以及高版本 PrestoSQL 这类业务新的调度需求。 |
| 150 | + |
| 151 | +#### 2.3.3 功能 |
| 152 | + |
| 153 | +| | 原调度系统 | DolphinScheduler | |
| 154 | +| -------- | ---------------------------------------------- | ------------------------------------------------------------ | |
| 155 | +| 任务类型 | 支持 Hive、Shell、低版本 Presto | 支持 SQL(Hive、Spark、Presto、Doris)、Shell支持 SeaTunnel 等共30+种任务类型 | |
| 156 | +| 多租户 | 不支持 | 支持 | |
| 157 | +| 调度性能 | 数据库压力存在瓶颈达到瓶颈后大量任务需手动修复 | 线性扩展 | |
| 158 | +| 数据回溯 | 支持 | 支持未来可支持下游任务补数 | |
| 159 | +| 开发成本 | 高多种语言及古老框架 | 低社区活跃 | |
| 160 | +| DAG | 不支持 | 页面拖拽、API均支持 | |
| 161 | +| 可观测 | 不支持 | 支持 | |
| 162 | + |
| 163 | +## 3 规划 |
| 164 | + |
| 165 | +- 例行任务、调试能力全部迁移 DolphinScheduler,沉淀线上操作 SOP |
| 166 | +- 结合社区的容器化进度,实现模块 K8S 部署。当前 API 模块已经在生产环境使用,Worker、Master 进行中 |
| 167 | +- 全链路的一键数据回溯能力 |
| 168 | +- 离线、实时平台打通 |
0 commit comments