做一个官方教程的搬运工,纯粹为了自己过一遍脑子。
1.工作流定义示例
下面是定义工作流的示例代码:
"""
Code that goes along with the Airflow tutorial located at:
https://github.com/airbnb/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, 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)
# 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)
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)
这个Airflow Python脚本实际上只是一个配置文件,将DAG的结构指定为代码。这里定义的实际任务将在与此脚本上下文不同的上下文中运行。不同的任务在不同的时间点上运行在不同的工人身上,这意味着这个脚本不能用于任务之间的交叉通信。请注意,出于这个目的,我们有一个更高级的特性称为
XCOM
。
人们有时会认为DAG定义文件是他们可以进行一些实际数据处理的地方——事实并非如此!脚本的目的是定义DAG对象。它需要快速评估(秒,而不是分钟),因为调度程序将定期执行它,以反映任何更改(如果有的话)。
2.示例代码分析
(1)导入模块
一个 Airflow 工作流是一个定义 Airflow DAG的python脚本 ,我们从引入模块开始。
# 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
(2)默认参数
我们即将创建一个DAG和一些任务,我们可以选择将一组参数显式地传递给每个任务的构造函数(这将变得多余),或者(更好!)。
我们可以定义一个默认参数字典,我们可以在创建任务时使用这些参数。
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),
}
关于基本参数和它们的作用请参考文档:py:class:airflow.models.BaseOperator
另外,请注意,您可以很容易地定义不同的参数集,这些参数集将服务于不同的目的。这方面的一个例子是在生产环境和开发环境之间设置不同的设置。
(3)实例化一个DAG
我们需要一个DAG对象来嵌套我们的任务。在这里,我们传递一个字符串,它定义了 dag_id
,它作为DAG的唯一标识符。我们还传递我们刚刚定义的默认参数字典,并为DAG定义一个调度间隔schedule_interval
为1天。
dag = DAG(
'tutorial', default_args=default_args, schedule_interval=timedelta(1))
(4)任务
任务在实例化运算符对象时生成。从操作符实例化的对象称为构造函数。第一个参数 task_id
充当任务的唯一标识符。
t1 = BashOperator(
task_id='print_date',
bash_command='date',
dag=dag)
t2 = BashOperator(
task_id='sleep',
bash_command='sleep 5',
retries=3,
dag=dag)
请注意,我们如何将操作符特定的参数(bash_command
)和从BaseOperator继承的所有操作符(retries
)共有的参数传递给操作符的构造函数。这比为每个构造函数调用传递每个参数更简单。另外,请注意,在第二个任务中,我们给retries
参数重新赋值为3
。
任务的优先级规则如下:
- 显式传递默认参数
-
default_args
中已经设置好的参数 - 操作的默认值
一个任务必须包括或继承参数task_id
和owner
,否则将引发Airflow异常。
(5)Jinja模板
airflow利用了Jinja模板的力量,并为工作流作者提供了一组内置参数和宏。airflow还为工作流作者提供接口来定义他们自己的参数、宏和模板。
本教程仅仅触及了在airflow中使用模板可以做什么,但是本节的目标是让您知道这个特性的存在,让您熟悉双花括号,并指向最常见的模板变量{{ ds }}
(今天的“日期戳”)。
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)
我们可以注意到,templated_command
代码{% %}
中的代码逻辑,引用像{{ ds }}
这样的参数,调用{{ macros.ds_add(ds, 7) }}
这样的函数,并引用用户定义参数{{ params.my_param }}
.
BaseOperator
中的Params
接口允许将参数和/或对象的字典传递给模板。请花时间了解参数my_param
是如何将其传递到模板的。
还可以将文件传递给bash_command
参数,如bash_command =‘ templated_command.sh’
,其中文件位置是相对于包含工作流文件的目录(本例中为tutorial.py)。这可能是出于许多原因,例如分离脚本的逻辑和管道代码,允许在以不同语言编写的文件中突出显示适当的代码,以及构造工作流时的一般灵活性。还可以将template_searchpath
定义为指向DAG构造函数中的任何文件夹位置。
使用相同的DAG构造函数,就可以定义user_defined_macros
,它允许你指定自己的变量。例如,将dict(foo=‘bar’)
传递给此参数允许在模板中使用{{ foo }}
。此外,指定user_defined_filters
允许注册您自己的过滤器。例如,将dict(hello=lambda name:‘Hello %s’ % name)
传递给这个参数,允许您在模板中使用{{ ‘World’| hello }}
。有关自定义过滤器的更多信息,请参阅Jinja文档。
有关可以在模板中引用的变量和宏的更多信息,请确保通过宏节读取。
(6)设置依赖
我们有两个不相互依赖的简单任务。以下是定义它们之间的依赖关系的几种方法:
t2.set_upstream(t1)
# This means that t2 will depend on t1
# running successfully to run
# It is equivalent to
# t1.set_downstream(t2)
t3.set_upstream(t1)
# all of this is equivalent to
# dag.set_dependency('print_date', 'sleep')
# dag.set_dependency('print_date', 'templated')
请注意,在执行脚本时,当ailflow在DAG中找到循环或当依赖项被引用不止一次时,会引发异常。
(7)概括
到目前为止,我们有一个非常基本的DAG。此时,你的代码应该如下所示:
"""
Code that goes along with the Airflow located at:
http://airflow.readthedocs.org/en/latest/tutorial.html
"""
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, 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(1))
# 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)
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)
(8)测试
1)执行脚本
是时候做些测试了。首先,让我们确保工作流解析。假设我们是在你的airflow.cfg
中引用的DAGS文件夹中的tutorial.py
中保存了上一步的代码。DAG的默认位置是~/airflow/dags
。
python ~/airflow/dags/tutorial.py
如果脚本没有引发异常,这意味着没有任何严重的错误,你的的airflow环境是健全的。
2)命令行元数据验证
让我们运行一些命令来进一步验证这个脚本。
# 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
3)测试
让我们通过在特定日期上运行任务实例来进行测试。在此上下文中指定日期的是execution_date
,它模拟在特定日期时间运行任务或DAG的调度程序:
# command layout: command subcommand dag_id task_id date
# testing print_date
airflow test tutorial print_date 2015-06-01
# testing sleep
airflow test tutorial sleep 2015-06-01
现在还记得我们之前对模板做了什么吗?通过运行以下命令来查看,此模板如何呈现和执行:
# testing templated
airflow test tutorial templated 2015-06-01
这将导致显示事件的冗长日志,并不停地运行BASH命令并打印结果。
注意,airflow test
命令在本地运行任务实例,将它们的日志输出到stdout(屏幕上),不需要依赖项,也不同步状态(运行、成功、失败、…)到数据库。它只允许测试单个任务实例。
4)回填(Backfill)
一切看起来都很好,所以让我们运行一个回填。backfill
将尊重您的依赖关系,将日志发送到文件中,并与数据库对话以记录状态。如果你启动了web服务,你将能够跟踪进度。airflow webserver
服务将启动一个Web服务器,如果你有兴趣跟踪进度,并查看可视化回填进展。
请注意,如果使用depends_on_past=True
,则单独的任务实例将依赖于前一个任务实例的成功,除了指定的start_date日期之外,它对此依赖性不予理会。
此上下文中的日期范围是start_date和可选的end_date,用于对此DAG中的任务实例填充运行计划。
# optional, start a web server in debug mode in the background
# airflow webserver --debug &
# start your backfill on a date range
airflow backfill tutorial -s 2015-06-01 -e 2015-06-07