气流任务可以在运行时动态生成DAG吗?

时间:2020-07-17 22:08:29

标签: airflow airflow-scheduler

我有一个上传文件夹,可以不定期上传。对于每个上传的文件,我都希望生成一个特定于该文件的DAG。

我的第一个想法是使用FileSensor来执行此操作,该FileSensor监视上传文件夹,并在存在新文件的情况下触发创建单独DAG的任务。从概念上讲:

Sensor_DAG (FileSensor -> CreateDAGTask)

|-> File1_DAG (Task1 -> Task2 -> ...)
|-> File2_DAG (Task1 -> Task2 -> ...)

在我最初的实现中,CreateDAGTask是一个PythonOperator,它通过将DAG全局变量放置在全局名称空间(see this SO answer)中来创建DAG全局变量,如下所示:

from airflow import DAG
from airflow.operators.dummy_operator import DummyOperator
from airflow.operators.python_operator import PythonOperator
from airflow.contrib.sensors.file_sensor import FileSensor
from datetime import datetime, timedelta
from pathlib import Path

UPLOAD_LOCATION = "/opt/files/uploaded"

# Dynamic DAG generation task code, for the Sensor_DAG below
def generate_dags_for_files(location=UPLOAD_LOCATION, **kwargs):
    dags = []
    for filepath in Path(location).glob('*'):
        dag_name = f"process_{filepath.name}"
        dag = DAG(dag_name, schedule_interval="@once", default_args={
            "depends_on_past": True,
            "start_date": datetime(2020, 7, 15),
            "retries": 1,
            "retry_delay": timedelta(hours=12)
        }, catchup=False)
        dag_task = DummyOperator(dag=dag, task_id=f"start_{dag_name}")

        dags.append(dag)

        # Try to place the DAG into globals(), which doesn't work
        globals()[dag_name] = dag

    return dags

然后,主DAG通过PythonOperator调用此逻辑:

# File-sensing DAG
default_args = {
    "depends_on_past" : False,
    "start_date"      : datetime(2020, 7, 16),
    "retries"         : 1,
    "retry_delay"     : timedelta(hours=5),
}
with DAG("Sensor_DAG", default_args=default_args,
         schedule_interval= "50 * * * *", catchup=False, ) as sensor_dag:

    start_task  = DummyOperator(task_id="start")
    stop_task   = DummyOperator(task_id="stop")
    sensor_task = FileSensor(task_id="my_file_sensor_task",
                             poke_interval=60,
                             filepath=UPLOAD_LOCATION)
    process_creator_task = PythonOperator(
        task_id="process_creator",
        python_callable=generate_dags_for_files,
    )
    start_task >> sensor_task >> process_creator_task >> stop_task

但这不起作用,因为到process_creator_task运行时,Airflow已经解析了全局变量。解析时间之后的新全局变量无关紧要。

临时解决方案

Airflow dynamic DAG and task Ids我可以完全省略FileSensor任务,而让Airflow在每个调度程序脉动信号下生成按文件的任务,代替Sensor_DAG仅执行generate_dags_for_files更新:没关系-尽管这确实在仪表板中创建了DAG,但实际执行遇到了"DAG seems to be missing"问题:

generate_dags_for_files()

这确实意味着我不再可以使用poke_interval的{​​{1}}参数来调节文件夹轮询的频率;相反,Airflow每次收集DAG时都会轮询该文件夹。

这是最好的模式吗?

其他相关的StackOverflow线程

1 个答案:

答案 0 :(得分:1)

简而言之:如果任务写了DagBag的读取位置,是的,但是最好避免使用需要这样做的模式。 您试图在任务中自定义创建的任何DAG可能应该改为是静态的,参数化的,有条件触发的DAG。 y2k-shubham provides an excellent example of such a setup,对于他的指导,我深表感谢关于这个问题的评论。

也就是说,这是一种可以解决问题的方法,不管这个想法有多糟糕,都将使他们变得越来越无礼:

  • 如果您通过变量(like so)动态生成DAG,请修改变量。
  • 如果您从配置文件列表中动态生成DAG,请向要从中提取配置文件的位置添加新的配置文件,以便在下一个DAG集合中生成新的DAG。
  • 使用类似Jinja的模板在dags/文件夹中编写一个新的Python文件。

要在任务运行后保留对任务的访问权,必须保持新的DAG定义稳定并在以后的仪表板更新/ DagBag集合中可访问。否则,the Airflow dashboard won't be able to render much about it.