浅谈工作流调度系统
http://blog.csdn.net/bdchome/article/details/52438540
每个公司都有自己的一套或者多套调度系统,从简单到复杂,满足各种定制化的需求。
Crontab任务调度
在没有工作流调度系统之前,公司里面的任务都是通过crontab来定义的,时间长了后会发现很多问题:
- 大量的crontab任务需要管理
- 任务没有按时执行,各种原因失败,需要重试
- 多服务器环境下,crontab分散在很多集群上,光是查看log就很花时间
于是,出现了一些管理crontab任务的调度系统。
CronHub
CronHub是暴风影音的马晨开源的时间调度系统。CronHub通过web界面来管理分散在不同机器上的crontab任务。详见:Cronhub 开源的时间调度系统。
CronWeb
CronWeb也是类似的系统
CronWeb的UI:
工作流调度系统
在很多公司,随着处理的数据量增长,需要管理的工作流也变得越来越大,里面充斥着大量的ETL任务:有些依赖某个时间点去执行,有些依赖数据或者外部的事件来执行。工作流中的有些任务会失败,需要有重试和报警功能,而不是等到整个工作流失败才发现问题。
总的来说,一个workflow scheduler system需要的主要功能有:
- DAG定义 (DAG的定义方式:表达式,脚本定义,通过WEB UI定义;还需要支持子DAG)
- 执行节点 (节点类型:unix cmd,shell,python,mapreduce, logging etc…)
- 节点控制(ignore, retry, suspend, run now, test mode)
- Metrics (需要对比一段时间内任务的运行时间)
- Monitor (失败策略,报警通知功能)
- CLI & Web UI (查询workflow执行情况,以及简单的控制)
Hadoop工作流引擎
OOZIE
Oozie是一个管理hadoop任务的工作流/协调系统。Oozie工作流中拥有多个Action,如Hadoop Map/Reuce job,Hadoop Pig job等,所有的Action以有向无环图(DAG Direct Acyclic Graph)的模式部署运行。详见:Hadoop工作流调度系统Oozie
Oozie的优点是与Hadoop生态圈结合紧密,比如:
- 有MapReduce的Action,定义一个mapreduce任务很方便,而且可以直接通过job id关联到hadoop history页面
- Oozie任务的资源文件都必须存放在HDFS上
- Action也方便扩展,比如添加自定义的任务类型或者报警方式
缺点是通过XML文件来定义DAG依赖,虽然支持的功能很多,比如分支,ok,failed节点,但是总感觉定义过于复杂,维护成本高。
Oozie的UI:
Oozie目前是Hadoop生态圈工作流调度事实上的标准,很多公司都在使用。其实,很难用,UI只能查看,不能进行任何操作。好在社区出现了HUE,可以弥补oozie的不足。
HUE是一个开源的Apache Hadoop UI系统,最早是由Cloudera Desktop演化而来,由Cloudera贡献给开源社区,它是基于Python Web框架Django实现的。通过使用Hue我们可以在浏览器端的Web控制台上与Hadoop集群进行交互来分析处理数据,例如操作HDFS上的数据,运行MapReduce Job等等。
HUE提供了一个Oozie编辑器,可以通过仪表板提交和监控Workflow、Coordinator和Bundle。
HUE的UI:
AZKABAN
Azkaban是由Linkedin开源的一个批量工作流任务调度器。用于在一个工作流内以一个特定的顺序运行一组工作和流程。Azkaban定义了一种KV文件格式来建立任务之间的依赖关系,并提供一个易于使用的web用户界面维护和跟踪你的工作流。详见:工作流调度器Azkaban
Azkaban和Oozie一样,也属于Hadoop生态圈。它的DAG定义方式比Oozie简单很多,在properties文件里面可以通过dependencies指定任务的上游依赖。Azkaban支持可插拔的扩展插件,方便扩展,比如支持pid,hive等。
Azkaban的特点是所有的任务资源文件都需要打成一个zip包上传。这个在资源文件较大的时候不是太方便,当前也可以进行扩展,比如存放在HDFS上,任务实际运行的时候才拉到本地。
Azkaban的UI:
ZEUS
Zeus是Alibaba开源的一个完整的Hadoop的作业平台,用于从Hadoop任务的调试运行到生产任务的周期调度。
宙斯支持任务的整个生命周期。从功能上来说,支持:
* Hadoop MapReduce任务的调试运行
* Hive任务的调试运行
* Shell任务的运行
* Hive元数据的可视化查询与数据预览
* Hadoop任务的自动调度
* 完整的文档管理
Zeus是针对Hadoop集群任务定制的,通用性不强。
基于Python的工作流引擎
基于Python的工作流引擎优点是:
The DAG definition is code
因此可维护性,版本管理,可测性和协作性更好。
dagobah
dagobah是一个Python写的基于DAG的任务调度系统。Dagobah可以使用Cron语法调度周期性任务,任务之间可以定义依赖关系。Dagobash可以支持重试某个失败的任务,并在任务结束或失败的后发送汇总邮件,跟踪任务的输出log,持久化到不同的后端。Dagobash是一个相当轻量级的调度系统。
dagobah可以通过Web UI通过拖拉的方式操作dag,也支持直接通过代码定义:
from dagobah import Dagobah from dagobah.backend.base import BaseBackend my_dagobah = Dagobah(BaseBackend()) my_dagobah.add_job('My Job') my_job = my_dagobah.get_job('My Job') my_job.add_task('python required_task.py', 'Required Task') my_job.add_task('python dependent_task.py', 'Dependent Task') my_job.add_dependency('Required Task', 'Dependent Task') my_job.schedule('0 10 * * *')
dogobah的UI:
Luigi
Luigi 是Spotify开源的,关注流程复杂的长时间运行的批次任务,例如Hadoop任务,推送数据,或者从数据库拉数据的任务,机器学习算法任务等。它能处理依赖关系,工作流关系,可视化等。它内置对Hadoop的支持。
一个hadoop上运行的wordcount任务定义如下:
import luigi import luigi.contrib.hadoop import luigi.contrib.hdfs class InputText(luigi.ExternalTask): """ This task is a :py:class:`luigi.task.ExternalTask` which means it doesn't generate the :py:meth:`~.InputText.output` target on its own instead relying on the execution something outside of Luigi to produce it. """ date = luigi.DateParameter() def output(self): """ Returns the target output for this task. In this case, it expects a file to be present in HDFS. :return: the target output for this task. :rtype: object (:py:class:`luigi.target.Target`) """ return luigi.contrib.hdfs.HdfsTarget(self.date.strftime('/tmp/text/%Y-%m-%d.txt')) class WordCount(luigi.contrib.hadoop.JobTask): """ This task runs a :py:class:`luigi.contrib.hadoop.JobTask` over the target data returned by :py:meth:`~/.InputText.output` and writes the result into its :py:meth:`~.WordCount.output` target. This class uses :py:meth:`luigi.contrib.hadoop.JobTask.run`. """ date_interval = luigi.DateIntervalParameter() def requires(self): """ This task's dependencies: * :py:class:`~.InputText` :return: list of object (:py:class:`luigi.task.Task`) """ return [InputText(date) for date in self.date_interval.dates()] def output(self): """ Returns the target output for this task. In this case, a successful execution of this task will create a file in HDFS. :return: the target output for this task. :rtype: object (:py:class:`luigi.target.Target`) """ return luigi.contrib.hdfs.HdfsTarget('/tmp/text-count/%s' % self.date_interval) def mapper(self, line): for word in line.strip().split(): yield word, 1 def reducer(self, key, values): yield key, sum(values) if __name__ == '__main__': luigi.run()
Luigi的UI:
Pinball
Pinball 是Pinterest开源的扩展性较好的工作流系统。Pinball的架构是一种master-worker模式,master节点负责管理任务调度,worker节点是无状态的。Pinball无缝集成了Hadoop/Hive/Spark等。
Pinball提供了自动重试,单任务邮件报警,运行时替换,任务优先级,过载策略等功能。
Pinball的UI:
Airflow
Airflow是Airbnb开源的DAG任务调度系统,用于管理,调度和监控工作流。它与Luigi,Pinball很像。后端是基于Flask,Celery,RabbitMQ/Redis。
Airflow工作流的定义:
""" Code that goes along with the Airflow located [here](http://pythonhosted.org/airflow/tutorial.html) """ from airflow import DAG from airflow.operators import BashOperator from datetime import datetime, timedelta seven_days_ago = datetime.combine(datetime.today() - timedelta(7), datetime.min.time()) default_args = { 'owner': 'airflow', 'depends_on_past': False, 'start_date': seven_days_ago, 'email': ['airflow@airflow.com'], 'email_on_failure': False, 'email_on_retry': False, 'retries': 1, 'retry_delay': timedelta(minutes=5), \# 'queue': 'bash_queue', \# 'pool': 'backfill', \# 'priority_weight': 10, \# 'schedule_interval': timedelta(1), \# 'end_date': datetime(2016, 1, 1), } dag = DAG('tutorial', default_args=default_args) \# t1, t2 and t3 are examples of tasks created by instatiating operators t1 = BashOperator( task_id='print_date', bash_command='date', dag=dag) t1.doc_md = """\ \#### Task Documentation You can document your task using the attributes `doc_md` (markdown), `doc` (plain text), `doc_rst`, `doc_json`, `doc_yaml` which gets rendered in the UI's Task Details page. ![img](http://montcs.bloomu.edu/~bobmon/Semesters/2012-01/491/import%20soul.png) """ dag.doc_md = __doc__ t2 = BashOperator( task_id='sleep', depends_on_past=False, bash_command='sleep 5', dag=dag) templated_command = """ {% for i in range(5) %} echo "{{ ds }}" echo "{{ macros.ds_add(ds, 7)}}" echo "{{ params.my_param }}" {% endfor %} """ t3 = BashOperator( task_id='templated', depends_on_past=False, bash_command=templated_command, params={'my_param': 'Parameter I passed in'}, dag=dag) t2.set_upstream(t1) t3.set_upstream(t1)
Airflow的功能很多,对于工作流可以支持Cron语法调度,失败重试策略,各种任务依赖调度策略等。
Airflow的一大亮点是backfill功能,对于数据仓库这种应用很有帮助,可以指定开始时间,将一个时间范围的任务重跑。Airflow还提供了丰富的命令行CLI和UI供操作。
Airflow通过Celery来实现分布式调度,架构的设计上很清晰,利于扩展。
Airflow的UI:
其他
工作流调度系统很多,有人做了统计: