1. Airflow 如何支持数据血缘?
Airflow 1.10.15+ and 2.0.2+ 已经支持lineage backend,但只是实验性的。
Airflow 通过任务的入口和出口【 inlets and outlets of the tasks】来跟踪数据,继而实现数据血缘关系:
- 任务执行前,pre_execute方法被调用,准备数据血缘的元数据
- 在任务完成后,post_execute方法被调用,推送数据血缘元数据到XCOM【让任务彼此通信的机制】,然后写到指定的数据血缘后端【如DataHub】
数据血缘后端必须继承LineageBackend类,并实现``方法
def send_lineage(
operator: "BaseOperator",
inlets: Optional[List] = None, # unused
outlets: Optional[List] = None, # unused
context: Dict = None,
) -> None:
2. 如何配置Airflow的数据血缘后端?
下面介绍如何配置DataHub作为Airflow的数据血缘后端
2.1. 前提条件
- 安装好Airflow环境,具体可参见: Airflow–基于conda的单机版安装过程
- 安装好datahub环境
pip install --upgrade pip wheel setuptools
pip install --upgrade acryl-datahub
pip install acryl-datahub[airflow]
2.2. 配置一个Airflow hook
# For REST-based:
airflow connections add --conn-type 'datahub_rest' 'datahub_rest_default' --conn-host 'http://localhost:8080'
# For Kafka-based (standard Kafka sink config can be passed via extras):
airflow connections add --conn-type 'datahub_kafka' 'datahub_kafka_default' --conn-host 'broker:9092' --conn-extra '{}'
配置后,可以点击Airflow 的菜单 Admin / Connections 进行查看
2.3. 修改airflow.cfg
注意配置中的缩进。
[lineage]
backend = datahub_provider.lineage.datahub.DatahubLineageBackend
datahub_kwargs = {
"datahub_conn_id": "datahub_rest_default",
"cluster": "prod",
"capture_ownership_info": true,
"capture_tags_info": true,
"graceful_exceptions": true }
配置项说明如下:
- datahub_conn_id (必须): 通常是
datahub_rest_default
或datahub_kafka_default
, 取决于2.2中airflow hook中的Conn Id - cluster (默认是
prod
): 关联DAG和任务的集群 - capture_ownership_info (默认是
true
): 如果设置为true, 将DAG的owner
字段作为DataHub的corpuser
【即Pipelines、Task的Owners】 - capture_tags_info (默认是
true
): 如果设置为true, 将DAG的tags
字段作为DataHub的tags
. - graceful_exceptions (默认是
true
): 如果设置为true, 数据血缘后端中的大多数运行时错误
将被抑制,不会导致整体任务失败。注意,配置问题仍然会抛出异常。
2.4. 重启airflow
如果进程存在,需要先kill掉 (ps -ef |grep airflow
)
airflow webserver -D
airflow scheduler -D
3. 如何将将工作流DAG写入DataHub作为数据血缘?
- 在DAG中的每个任务中配置
inlets
和outlets
- DAG在调度完成后,会根据任务配置的
inlets
、outlets
,将数据血缘写入DataHub- DAG作为一个Pipeline,下面挂多个任务
- 任务作为数据血缘的一个节点,上流是
inlets
指定的数据集,下游是outlets
指定的数据集
注意:指定在DAG中指定的数据集不存在,则会创建一个空的数据集
3.1. 使用普通方式实现
效果如下:
源码如下:
# coding:utf-8
from datetime import timedelta
from airflow import DAG
from airflow.utils.dates import days_ago
try:
from airflow.operators.bash import BashOperator
except ModuleNotFoundError:
from airflow.operators.bash_operator import BashOperator
from datahub_provider.entities import Dataset
default_args = {
"owner": "airflow",
"depends_on_past": False,
"email": ["penriver@163.com"],
"email_on_failure": False,
"execution_timeout": timedelta(minutes=5),
}
with DAG(
"datahub_lineage_backend_demo",
default_args=default_args,
description="An example DAG demonstrating the usage of DataHub's Airflow lineage backend.",
schedule_interval=timedelta(days=1),
start_date=days_ago(2),
tags=["example-tag"],
catchup=False,
) as dag:
task1 = BashOperator(
task_id="run_data_task",
dag=dag,
bash_command="echo 'This is where you might run your data tooling.'",
inlets={
"datasets": [
Dataset("mysql", "ods.tblA"),
Dataset("mysql", "ods.tblB"),
],
},
outlets={"datasets": [Dataset("mysql", "ods.tblC")]},
)
3.2. 使用注解方式实现
效果如下:
源码如下:
# coding:utf-8
from datetime import timedelta
from airflow.decorators import dag, task
from airflow.utils.dates import days_ago
from datahub_provider.entities import Dataset
default_args = {
"owner": "airflow",
"depends_on_past": False,
"email": ["penriver@163.com"],
"email_on_failure": False,
"execution_timeout": timedelta(minutes=5),
}
@dag(
default_args=default_args,
description="An example DAG demonstrating the usage of DataHub's Airflow lineage backend using the TaskFlow API.",
schedule_interval=timedelta(days=1),
start_date=days_ago(2),
tags=["example_tag"],
catchup=False,
)
def datahub_lineage_backend_taskflow_demo():
@task(
inlets={
"datasets": [
Dataset("hive", "ods.tblC"),
Dataset("hive", "ods.tblD"),
],
},
outlets={"datasets": [Dataset("mysql", "stat.tblE")]},
)
def run_data_task():
# This is where you might run your data tooling.
pass
run_data_task()
datahub_lineage_backend_taskflow_dag = datahub_lineage_backend_taskflow_demo()