其实怎么部署 airflow 又哪些特性,然后功能又是如何全面都可以在 Reference 的文章里面找到,都不是重点这里就不赘述了。
这里重点谈一下我在部署完成仔细阅读文档之后觉得可以总结的一些东西,或者踩到的一些坑。
首选明确 airflow 中最重要的几个概念:
DAG
DAG 意为有向无循环图,在 Airflow 中则定义了整个完整的作业。同一个 DAG 中的所有 Task 拥有相同的调度时间。
Task
Task 为 DAG 中具体的作业任务,它必须存在于某一个 DAG 之中。Task 在 DAG 中配置依赖关系,跨 DAG 的依赖是可行的,但是并不推荐。跨 DAG 依赖会导致 DAG 图的直观性降低,并给依赖管理带来麻烦。
DAG Run
当一个 DAG 满足它的调度时间,或者被外部触发时,就会产生一个 DAG Run。可以理解为由 DAG 实例化的实例。
Task Instance
当一个 Task 被调度启动时,就会产生一个 Task Instance。可以理解为由 Task 实例化的实例。
在使用过程中,需要谨记这些概念不然很容易就被绕晕。
TimeZone:
时区问题可以说是 airflow 中根本绕不开的一个问题,官方文档也拿整整一章来阐述时区给 airflow 带来的影响。
官方默认使用 UTC+0 时间来作为 airflow 的时间,并且在 airflow 提供的 webserver 上,这个时区直到现在 1.10.1 版本上依旧是无法被配置的。所以我们在 server 上看到的时间默认都是 UTC+0 的时间。但是系统启动的时间,以及写入数据库元数据的时间确是可以配置时区的。他的格式遵循 IANA 格式。可以通过配置 airflow 的配置文件 airflow.cfg 改变这个时区。例如我在启动的时候就配置了中国时间:
# Default timezone in case supplied date times are naive # can be utc (default), system, or any IANA timezone string (e.g. Europe/Amsterdam) #default_timezone = utc default_timezone = Asia/Chongqing
当配置了这个生效之后,我们给任务配置的启动时间,以及 runtime 之类的时间会遵循这个时间(虽然在 ui 界面上看还是 UTC+0 的非常分裂。。。)
例如我们来看一个执行任务:
可以看到 Last Run 字段指明了该字段是 2018-12-09 16:00 被执行了。按照我们刚才的配置 UTC+8 应该是 10号的 0点被执行了。我们来看下数据库里是否真的是这样呢?
可以通过下图发现完全按照我们预期:
mysql> select * from task_instance where task_id = 'sensors'\G; *************************** 1. row *************************** task_id: sensors dag_id: sensors_dag execution_date: 2018-12-10 00:00:00.000000 start_date: 2018-12-11 00:00:04.547631 end_date: 2018-12-11 00:04:50.686937 duration: 286.139 state: success try_number: 1 hostname: zed-2 unixname: apache-airflow job_id: 26975 pool: NULL queue: default priority_weight: 1 operator: BashOperator queued_dttm: 2018-12-11 00:00:03.302928 pid: 19962 max_tries: 3 executor_config: }q .
所以说,证明了只要我们设置了刚才的参数之后,只有 ui 界面依然继续展示 UTC+0 的时间,其实我们真正的执行操作的时区已经被修改过来了。
另外还需要注意的一个地方在于任务的申明那里,在申明一个 dag 的时候同样需要指明自己的时区,否则一脸懵逼为啥到了时间为什么没有正常调度起来。
import pendulum import datetime local_tz = pendulum.timezone("Asia/Chongqing") # start 接收一个 %Y-%m-%d %H:%M:%S 的字符串时间 def init_default_args(owner, start_time, retry=3, email=None): if not email: email = [] if not isinstance(start_time, datetime.datetime): raise TypeError('start_date must be datetime.') d = start_time.replace(tzinfo=local_tz) return { 'owner': owner, 'depends_on_past': False, 'start_date': d, 'email': email, 'email_on_failure': False, 'email_on_retry': False, 'retries': retry, 'retry_delay': datetime.timedelta(minutes=5), }
这里我在申明默认 default_args 参数的时候就显示的指明了时区并且赋值给 start_date 让他开始的时间复合我们的预期。
Backfill:
感觉这个也非常让人懵逼值得拿出来多多少少谈一下。
光从字面意思来理解作为一个中国人我真的是一下没有 get 到他的点,但是后来去查了一下文档结合他的行为看了一下可以把它简单的理解成当你错过了某一次执行时间之后,往回去补充执行的行为。我们可以使用手动方法来执行这个行为
airflow backfill sensors -s 2015-06-01 -e 2015-06-07
他会回补这个时间段开始的 和 -e 后面时间段结束期间所有的任务执行。回补的意思就是把没有执行的操作都执行一遍。
这个特性想法很好,但是自自动触发的时候不注意就会产生非常不可预期的问题。
比如刚才在上面我们谈到的在给 dag 配置的时候指定的 default_args 上面有一个参数 start_date。如果我们不给 dag 指定不回补,那么 airflow 会默认回补从系统当前时间到我们指定的 start_date 期间的任务。如果这个参数设置得不恰当会打来恐怖的回补,所以一般我都会禁用回补。
sensors_dag = DAG( 'sensors_dag', default_args=default_args, schedule_interval=u'0 0 * * *', catchup=False)
指定 catchup=False 。让他从最新的任务时间点开始执行。这个在官方文档 Scheduling & Triggers 一章有详细提到。
Operator:
现在这一章我只有一个地方觉得有点坑。可能是我还没有深度使用吧
由于 airflow 支持模版 jinja 的模版功能,所以说在使用 BashOperator 的时候要注意自己的写法,官方文档对此有描述
Troubleshooting Jinja template not found Add a space after the script name when directly calling a Bash script with the bash_command argument. This is because Airflow tries to apply a Jinja template to it, which will fail. t2 = BashOperator( task_id='bash_example', # This fails with `Jinja template not found` error # bash_command="/home/batcher/test.sh", # This works (has a space after) bash_command="/home/batcher/test.sh ", dag=dag)
我觉得这个。。。还蛮坑的- -
这一章其实应该有蛮多的内容可以延伸,包括社区也给 airflow 贡献了非常多的 Operator。等我深度使用之后再来 backfill 吧哈哈哈哈。
Reference:
https://airflow.apache.org airflow 官方文档
https://zhuanlan.zhihu.com/p/44768244 如何部署一个健壮的 apache-airflow 调度系统
http://www.shuhegroup.com/newsmedias/%E6%B5%85%E8%B0%88%E8%B0%83%E5%BA%A6%E5%B7%A5%E5%85%B7airflow/ 浅谈调度工具——Airflow