airflow官方教程——一个简单案例

做一个官方教程的搬运工,纯粹为了自己过一遍脑子。

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': ['airflow@example.com'],
    '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': ['airflow@example.com'],
    '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

任务的优先级规则如下: 1. 显式传递默认参数 2. default_args 中已经设置好的参数 3. 操作的默认值

一个任务必须包括或继承参数task_idowner,否则将引发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': ['airflow@example.com'],
    '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

发布于 2019-07-19 16:34