airflow分享

知识共享许可协议 版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons

组成部分

从一个使用者的角度来看,调度工作都有以下功能:

系统配置($AIRFLOW_HOME/airflow.cfg)
作业管理($AIRFLOW_HOME/dags/xxxx.py)
运行监控(webserver)
告警(邮件或短信)
日志查看(webserver 或 $AIRFLOW_HOME/logs/)
跑批耗时分析(webserver)
后台调度服务(scheduler)

除了短信需要自己实现,其他功能 airflow 都有,而且在 airflow 的 webserver 上我们可以直接配置数据库连接来写 sql 查询,做更加灵活的统计分析。

一个DAG demo

"""
Code that goes along with the Airflow tutorial located at:
https://github.com/apache/airflow/blob/master/airflow/example_dags/tutorial.py
"""
# 导入模块
# The DAG object; we'll need this to instantiate a DAG
from airflow import DAG
# Operators; we need this to operate!
from airflow.operators.bash_operator import BashOperator
from datetime import datetime, timedelta

# 默认参数
default_args = {
    'owner': 'airflow',
    'depends_on_past': False,
    'start_date': datetime(2015, 6, 1),
    'email': ['[email protected]'],
    'email_on_failure': False,
    'email_on_retry': False,
    'retries': 1,
    'retry_delay': timedelta(minutes=5),
    # 'queue': 'bash_queue',
    # 'pool': 'backfill',
    # 'priority_weight': 10,
    # 'end_date': datetime(2016, 1, 1),
}
# 实例化
dag = DAG('tutorial', default_args=default_args, schedule_interval=timedelta(days=1))

# 任务 task
# t1, t2 and t3 are examples of tasks created by instantiating operators
t1 = BashOperator(
    task_id='print_date',
    bash_command='date',
    dag=dag)

t2 = BashOperator(
    task_id='sleep',
    bash_command='sleep 5',
    retries=3,
    dag=dag)

# jinja模版 {{}}模版变量,来参数化bash_command参数
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',
    bash_command=templated_command,
    params={'my_param': 'Parameter I passed in'},
    dag=dag)

# 设置依赖关系
t2.set_upstream(t1)
t3.set_upstream(t1)
# 等价于
# t1.set_downstream(t2)
# t1 >> t2
# t2 << t1
# t1.set_downstream([t2, t3])
# t1 >> [t2, t3]

DAG 脚本的目的只是定义 DAG 的配置,并不包含任何的数据处理,在这里 operator 就是 task。

DAG 的实例化

一个 DAG 脚本是由 DAG object 的实例化和对应的 operator 组成的,除此之外我们还可以定义默认的参数提供给每个任务。
DAG 对象实例化可以根据我们的需要提供对应的初始化参数,实例化 DAG 对象需要提供唯一的 dag_id:

dag = DAG('tutorial', default_args=default_args, schedule_interval=timedelta(days=1))

task的实例化

t1 = BashOperator(
    task_id='print_date',
    bash_command='date',
    dag=dag)

task 对象的定义的就是 operator 的实例化,operator 有 task_id,用来区分任务,可以按照需要定制 bash_command,也可以传递参数等。
有个坑要注意:

# 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 ",

操作符-Operators

DAG 定义一个作业流,Operators 则定义了实际需要执行的作业。airflow 提供了许多 Operators 来指定我们需要执行的作业:

BashOperator - 执行 bash 命令或脚本。
SSHOperator - 执行远程 bash 命令或脚本(原理同 paramiko 模块)。
PythonOperator - 执行 Python 函数。
EmailOperator - 发送 Email。
HTTPOperator - 发送一个 HTTP 请求。
MySqlOperator, SqliteOperator, PostgresOperator, MsSqlOperator, OracleOperator, JdbcOperator, 等. - 执行 SQL 任务。
DockerOperator, HiveOperator, S3FileTransferOperator, PrestoToMysqlOperator, SlackOperator 你懂得。
除了以上这些 Operators 还可以方便的自定义 Operators 满足个性化的任务需求。

task 的依赖

task 之间是能相互建立依赖的

t2.set_upstream(t1)
t3.set_upstream(t1)
# 等价于
# t1.set_downstream(t2)
# t1 >> t2
# t2 << t1
# t1.set_downstream([t2, t3])
# t1 >> [t2, t3]

使用位位移来指定执行顺序很简单
Airflow 会自动检测环形依赖以防止 task 无法工作的情况出现

执行和测试

和 airflow.cfg 同级目录下建立 dag 目录,用来存放第一个 DAG 脚本,然后执行 python tutorial.py ,如果没有报错说明 tutorial 建立成功了

python ~/airflow/dags/tutorial.py

airflow的命令行

命令行元数据验证,查看 DAG 和 task

# print the list of active DAGs
airflow list_dags

# prints the list of tasks the "tutorial" dag_id
airflow list_tasks tutorial

# prints the hierarchy of tasks in the tutorial DAG
airflow list_tasks tutorial —tree

测试任务的执行

执行任务很简单,指定 DAG 并去指定 task 和执行的日期

# command layout: command subcommand dag_id task_id date

# testing print_date
airflow test tutorial print_date 2019-06-10

# testing sleep
airflow test tutorial sleep 2019-06-10

# testing templated
airflow test tutorial templated 2019-06-10

test 命令会执行任务并且输出到控制台,不会把任务的执行状态进行持久化

run

airflow run example_bash_operator runme_0 2018-01-11

如果运行正常,则可以启用该定时任务,启用任务有两种方式:

  1. 通过命令启动:
$ airflow unpause example_hello_world_dag
  1. 通过界面启动:
    在airflow的web管理界面,将左边的off按钮改为on

更多关于 DAG 和 operator

DAG 的 scope

Airflow 会默认加载任意它能导入到饿 DAG object,这就意味着只要是全局的 DAG object 都可以被导入,但是有时候为了让 DAG 不被导入,比如 SubDagOperator 就可以使用 local 的作用域。

dag_1 = DAG('this_dag_will_be_discovered')
 
def my_function()
    dag_2 = DAG('but_this_dag_will_not')
my_function()

DAG 可以指定默认的参数

DAG 的默认参数会应用到所有的 operator 中

# 默认参数
default_args = {
    'owner': 'airflow',
    'depends_on_past': False,
    'start_date': datetime(2015, 6, 1),
    'email': ['[email protected]'],
    'email_on_failure': False,
    'email_on_retry': False,
    'retries': 1,
    'retry_delay': timedelta(minutes=5),
    # 'queue': 'bash_queue',
    # 'pool': 'backfill',
    # 'priority_weight': 10,
    # 'end_date': datetime(2016, 1, 1),
}
# 实例化
dag = DAG('tutorial', default_args=default_args, schedule_interval=timedelta(days=1))

t1 = BashOperator(
    task_id='print_date',
    bash_command='date',
    dag=dag)

扩展性极强的 operator

Airflow operator 很容易扩展,这也是 airflow 几乎支持任何形式 task 重要原因。虽然 Airflow 支持不同的 task 可以传输数据,但是如果你的两个 task 之间确实需要共享数据,最好的办法是把他们写在一起。

时区-timezone

airflow 1.9 之前的版本使用本地时区来定义任务开始日期,scheduler_interval 中 crontab 表达式中的定时也是依据本地时区为准,但 airflow 1.9 及后续新版本将默认使用 UTC 时区来确保 airflow 调度的独立性,以避免不同机器使用不同时区导致运行错乱。如果调度的任务集中在一个时区上,或不同机器,但使用同一时区时,需要对任务的开始时间及 cron 表达式进行时区转换,或直接使用本地时区。目前 1.9 的稳定版本还不支持时区配置,后续版本会加入时区配置,以满足使用本地时区的需求。
我用的v1.10.3
可以在DAG文件中,以本地时间来设置,再把本地时间转为utc时间。

airflow 的守护进程

airflow 系统在运行时有许多守护进程,它们提供了 airflow 的全部功能。守护进程包括 Web服务器-webserver、调度程序-scheduler、执行单元-worker、消息队列监控工具-Flower等
注意的是 airflow 的守护进程彼此之间是独立的,他们并不相互依赖,也不相互感知。每个守护进程在运行时只处理分配到自己身上的任务,他们在一起运行时,提供了 airflow 的全部功能

web服务器-webserver

webserver 是 airflow 的界面展示,可显示 DAG 视图,控制作业的启停,清除作业状态重跑,数据统计,查看日志,管理用户及数据连接等。不运行 webserver 并不影响 airflow 作业的调度。
用户可能在 webserver 上来控制 DAG,比如手动触发一个 DAG 去执行。

调度器-schduler

调度器 schduler 负责读取 DAG 文件,计算其调度时间,当满足触发条件时则开启一个执行器的实例来运行相应的作业,必须持续运行,不运行则作业不会跑批。
调度器 scheduler 会间隔性的去轮询元数据库(Metastore)已注册的 DAG(有向无环图,可理解为作业流)是否需要被执行。如果一个具体的 DAG 根据其调度计划需要被执行,scheduler 守护进程就会先在元数据库创建一个 DagRun 的实例,并触发 DAG 内部的具体 task(任务,可以这样理解:DAG 包含一个或多个task),触发其实并不是真正的去执行任务,而是推送 task 消息至消息队列(即 broker)中,每一个 task 消息都包含此 task 的 DAG ID,task ID,及具体需要被执行的函数。如果 task 是要执行 bash 脚本,那么 task 消息还会包含 bash 脚本的代码。

schedule_interval

schedule_interval=timedelta(days=1) #执行周期,表示每天执行一次
schedule_interval="00, *, *, *, *"  # 执行周期,依次是分,时,天,月,年,此处表示每个整点执行schedule_interval=timedelta(minutes=1)  # 执行周期,表示每分钟执行一次

工作节点-worker

当执行器为 CeleryExecutor 时,需要开启一个 worker。
worker启动 1 个或多个 Celery 的任务队列,负责执行具体 的 DAG 任务。
当设置 airflow 的 executors 设置为 CeleryExecutor 时才需要开启 worker 守护进程。推荐你在生产环境使用 CeleryExecutor :(生产环境若CeleryExecutor配置不方便,也可使用LocalExecutor)

executor = CeleryExecutor

启动一个 worker守护进程,默认的队列名为 default:

airfow worker -D

worker 守护进程将会监听消息队列,如果有消息就从消息队列中取出消息,当取出任务消息时,它会更新元数据中的 DagRun 实例的状态为正在运行,并尝试执行 DAG 中的 task,如果 DAG 执行成功,则更新任 DagRun 实例的状态为成功,否则更新状态为失败。

flower

用于是监控 celery 消息队列

airflow flower -D

默认的端口为 5555,您可以在浏览器地址栏中输入 “http://hostip:5555” 来访问 flower ,对 celery 消息队列进行监控。

执行器-Executor

执行器有 SequentialExecutor, LocalExecutor, CeleryExecutor

SequentialExecutor 为顺序执行器,单进程顺序执行任务,默认执行器,通常只用于测试。默认使用 sqlite 作为知识库,由于 sqlite 数据库的原因,任务之间不支持并发执行,常用于测试环境,无需要额外配置。
LocalExecutor 为本执行器,多进程本地执行任务,不能使用 sqlite 作为知识库,可以使用mysql,postgress,db2,oracle 等各种主流数据库,任务之间支持并发执行,常用于生产环境,需要配置数据库连接 url。
CeleryExecutor 为 Celery 执行器,分布式调度,生产常用。需要安装 Celery ,Celery 是基于消息队列的分布式异步任务调度工具。需要额外启动工作节点-worker。使用 CeleryExecutor 可将作业运行在远程节点上。
DaskExecutor :动态任务调度,主要用于数据分析

执行失败时email通知

如果需要在任务执行失败(执行过程中有异常抛出)的时候邮件通知,除了在DAG文件中指定接收email列表外,还需要在配置文件中指定发送邮箱的信息,打开配置文件$AIRFLOW_HOME/airflow.cfg,修改以下配置项,修改完需要重启

smtp_host = smtp.163.com  # smtp邮箱地址
smtp_starttls = True  # 是否tls加密
smtp_mail_from = [email protected]  # 发件人邮箱地址,需开通smtp服务
smtp_ssl = False  # 是否ssl加密
smtp_port = 25  # smtp端口号

使用变量(Variables)

变量的value可以在UI界面的Admin > Variables里面进行增删改查
可以在代码中这样使用变量

from airflow.models import Variable
foo = Variable.get("foo", default_var='a')  # 设置当获取不到时使用的默认值
bar = Variable.get("bar", deserialize_json=True)  # 对json数据进行反序列化

部署方式

airflow 单节点部署
airflow 多节点(集群)部署(需安装Celery、RabbitMQ/redis,需要额外启动工作节点-worker,配置CeleryExecutor)
https://www.jianshu.com/p/2ecef979c606

backfill 回填

backfill命令是用来回填数据的,也就是说以之前的日期运行任务。
在特定情况下,修改DAG后,为了避免当前日期之前任务的运行,可以使用backfill填补特定时间段的任务

airflow backfill -s START -e END --mark_success DAG_ID

当任务是每天运行时只需要加上开始日期就可以了

# start your backfill on a date range
airflow backfill tutorial -s 2019-06-10 -e 2019-06-17
# start your backfill on a date range
airflow backfill tutorial -s 2019-06-10 -e 2019-06-17

但是当任务时多天运行一次时这样就不起作用了,会提示No run dates were found for the given dates and dag interval.
这是因为 airflow有一个窗口的概念
Airflow sets execution_date based on the left bound of the schedule period it is covering, not based on when it fires (which would be the right bound of the period)
stackoverflow上搜到比较合理的解释,意思就是说,airflow会在start_date开始后,符合schedule_interval定义的第一个时间点记为execution_date,但是会在下个时间点到达是才开始运行,也就是说由于这个窗口的原因,last run会滞后一个周期。
所以如何通过jinja来查看execution_date就会发现问题
基于 jinja 模板引擎很容易做到脚本命令参数化

log日志:/home/bigdata/airflow/logs/tutorial/print_date/2019-06-10T00:00:00+00:00/

catchup 追赶

具有start_date,可能有设置end_date和airflow DAG schedule_interval定义了一系列间隔,调度程序将变为单独的Dag运行并执行。Airflow的一个关键功能是这些DAG运行是原子的幂等项,默认情况下,调度程序将检查DAG的生命周期(从开始到结束/现在,一次一个间隔)并启动DAG运行对于尚未运行(或已被清除)的任何间隔。这个概念叫做Catchup。
如果您的DAG被编写为处理它自己的追赶(IE不限于间隔,而不是例如“现在”。),那么您将需要关闭追赶(在DAG本身上)或默认情况下配置文件级别。这样做的目的是指示调度程序仅为DAG间隔序列的最新实例创建DAG运行。dag.catchup = Falsecatchup_by_default = False

"""
Code that goes along with the Airflow tutorial located at:
https://github.com/apache/airflow/blob/master/airflow/example_dags/tutorial.py
"""
from airflow import DAG
from airflow.operators.bash_operator import BashOperator
from datetime import datetime, timedelta


default_args = {
    'owner': 'airflow',
    'depends_on_past': False,
    'start_date': datetime(2015, 12, 1),
    'email': ['[email protected]'],
    'email_on_failure': False,
    'email_on_retry': False,
    'retries': 1,
    'retry_delay': timedelta(minutes=5),
    'schedule_interval': '@daily',
}

dag = DAG('tutorial', catchup=False, default_args=default_args)

在上面的示例中,如果调度程序守护程序在2016-01-02上午6点(或从命令行)获取DAG,则将创建一个DAG Run,其中包含execution_date2016-01-01,下一个将在2016-01-03上午午夜后创建,执行日期为2016-01-02。

如果该dag.catchup值为True,则调度程序将为2015-12-01和2016-01-02之间的每个完成时间间隔创建一个DAG运行(但是2016-01-02还没有一个,因为该时间间隔没有已完成),调度程序将按顺序执行它们。对于可以轻松拆分为句点的原子数据集,此行为非常有用。如果您的DAG运行在内部执行回填,则关闭追赶是很好的。

常用命令

$ airflow webserver -D       守护进程运行webserver
$ airflow scheduler -D       守护进程运行调度器
$ airflow worker -D          守护进程运行调度器
$ airflow worker -c 1 -D     守护进程运行celery worker并指定任务并发数为1
$ airflow pause dag_id      暂停任务
$ airflow unpause dag_id     取消暂停,等同于在管理界面打开off按钮
$ airflow list_tasks dag_id  查看task列表
$ airflow clear dag_id       清空任务实例
$ airflow trigger_dag dag_id -r RUN_ID -e EXEC_DATE  运行整个dag文件
$ airflow run dag_id task_id execution_date       运行task

web ui

启动web管控界面需要执行airflow webserver -D命令,默认访问端口是8080
http://hadoop02.office.gdapi.net:8080/admin/

这个帖子对界面的解释比较好: https://www.jianshu.com/p/e878bbc9ead2

start_date & schedule_interval

看第一遍可能以为Airflow的调度是往后延一个周期的,比如一个每日执行的任务,会在1月2日0点执行1月1日的任务。而实际上,并非如此。
Airflow什么时候开始执行任务?实际上是从dag的start_date + schedule_interval开始的,如果schedule_interval设成@daily,那么会从start_date之后的一天开始。
注意我理解这里的start_date并非dag参数里面的start_date,比如今天是8月1日,参数的start_date设成了7月1日,那么Airflow会首先去补7月1日-7月31日的数据,补完以后,拿来计算最新的dag run的日期就变成了8月1日,其实这里面的start_date是指最近的执行日期,如果是8月1日,首次任务会在8月2日00:00:00后执行。
但是如果schedule_interval设成cron表达式会怎样?可能我们会设成每天的15:00:00启动dag,但是当天的15:00:00是不会启动的,即使过了15:00:00到了16:00:00也不会去backfill这个dag。
因为Airflow会把这个cron表达式解释为shedule_interval为1天,所以会在第二天才启动。其实跟设置成@daily的效果一样的。但是具体是从00:00:00启动还是从15:00:00启动这个尚待考证。
那么如果想当天就启动一个dag应该怎么做?答案就是把schedule_interval设短一点。如果还想用cron,那么就设置成*/15 15 * * *这样的形式。dag会在15:00-16:00间每隔15分钟跑一次,程序逻辑里面就多判重一下,没办法,谁叫Airflow这么坑呢。
airflow的cron定时器只能精确到分钟,而不能精确到秒
airflow使用的是utc时区,正式使用的时候需要进行时区转换

脚本名称后面要添加空格
参考http://airflow.apache.org/howto/operator/bash.html

支持钉钉发消息通知
http://airflow.apache.org/howto/operator/dingding.html
https://open-doc.dingtalk.com/microapp/serverapi2/qf2nxq

Admin->connections
http://airflow.apache.org/howto/connection/mysql.html

bitshift组合运算符详细说明
变量的取用
http://airflow.apache.org/concepts.html

命令行界面
http://airflow.apache.org/cli.html

猜你喜欢

转载自blog.csdn.net/qq_30552441/article/details/94136920