4. Airflow 实战:编写第一个 DAG、任务依赖设置、调度周期配置、变量与连接管理

好,咱们开始动手。前面聊了那么多理论,现在终于要写代码了。Airflow 的核心就是 DAG——说白了,就是一张有向无环图,把一个个任务串起来。这一章我会带你从零写一个 DAG,然后一步步加上依赖、调度、变量和连接。嗯,都是我在生产环境里天天用的东西。

4.1 第一个 DAG:Hello World 的升级版

先别急着搞复杂的东西。我们写一个最简单的 DAG,让它每天打印一句 "Hello from Airflow"。你想想看,这就像学编程时的 "Hello World",但咱们这个能真正跑在调度系统里。

from datetime import datetime, timedelta
from airflow import DAG
from airflow.operators.python import PythonOperator

def print_hello():
    print("Hello from Airflow! 当前时间: ", datetime.now())

default_args = {
    'owner': 'data_team',
    'depends_on_past': False,
    'email_on_failure': False,
    'email_on_retry': False,
    'retries': 1,
    'retry_delay': timedelta(minutes=5),
}

dag = DAG(
    'hello_airflow_dag',
    default_args=default_args,
    description='我的第一个 DAG',
    schedule_interval='0 8 * * *',  # 每天早上8点
    start_date=datetime(2024, 1, 1),
    catchup=False,
    tags=['example'],
)

task_hello = PythonOperator(
    task_id='say_hello',
    python_callable=print_hello,
    dag=dag,
)
个人习惯:我写 DAG 时一定会加 tags 参数。项目多了之后,按标签过滤 DAG 列表比翻页找快得多。另外 catchup=False 也建议一开始就设上,不然你设个历史 start_date,Airflow 会拼命补跑过去的任务。

这段代码放到 $AIRFLOW_HOME/dags/ 目录下,Airflow 会自动解析。你可以在 Web UI 的 DAGs 页面看到它。嗯,这里要注意:文件名就是 DAG ID 的一部分,别乱起名。

4.2 任务依赖设置:让任务按顺序跑

实际作业流不可能只有一个任务。比如我们做数据 pipeline,通常是:抽取 → 转换 → 加载。这三个步骤有先后顺序。Airflow 里设置依赖非常简单,用位移运算符就行。

from airflow.operators.bash import BashOperator

# 定义三个任务
task_extract = BashOperator(
    task_id='extract_data',
    bash_command='echo "抽取数据..."',
    dag=dag,
)

task_transform = BashOperator(
    task_id='transform_data',
    bash_command='echo "转换数据..."',
    dag=dag,
)

task_load = BashOperator(
    task_id='load_data',
    bash_command='echo "加载数据..."',
    dag=dag,
)

# 设置依赖:抽取 → 转换 → 加载
task_extract >> task_transform >> task_load

这行 task_extract >> task_transform >> task_load 就是依赖关系。箭头指向谁,谁就等前面的跑完。你也可以用 set_downstream() 方法,但我个人觉得位移运算符更直观。

复杂依赖场景:如果任务 A 和 B 都完成后才能跑 C,可以写成 [task_a, task_b] >> task_c。如果 A 完成后要同时跑 B 和 C,就写成 task_a >> [task_b, task_c]。说白了,列表就是「与」的关系。

我在项目中遇到过一种坑:任务依赖写成了循环依赖。比如 A→B→C→A,Airflow 会直接报错,DAG 都解析不出来。所以画 DAG 图的时候,脑子里过一遍有没有环。

4.3 调度周期配置:cron 表达式与 timedelta

调度周期是 DAG 的灵魂。Airflow 支持两种方式:cron 表达式和 timedelta 对象。我一般用 cron,因为更精确。

调度需求 cron 表达式 说明
每天凌晨 2 点 0 2 * * * 分钟 0,小时 2
每小时整点 0 * * * * 分钟固定为 0
每周一早上 9 点 0 9 * * 1 星期天=0,星期一=1
每月 1 号凌晨 3 点 0 3 1 * * 日字段为 1
每 15 分钟 */15 * * * * 步进值
我曾经踩过的坑:Airflow 的调度时间是基于 start_date + schedule_interval 来计算的。比如你设 start_date=2024-01-01,schedule_interval='0 2 * * *',那么第一个调度时间其实是 2024-01-02 02:00,而不是 1 号当天。很多人刚接触时以为设了 start_date 就会立即跑一次,其实不会。如果你想让 DAG 创建后马上跑一次,可以手动触发。

如果你用 timedelta,写法是这样的:

dag = DAG(
    'my_timedelta_dag',
    schedule_interval=timedelta(hours=1),  # 每小时跑一次
    ...
)

但 timedelta 有个问题:它没有「对齐到整点」的概念。比如你 8:23 启动 DAG,下一次就是 9:23,再下一次 10:23。而 cron 表达式会严格对齐到整点。我个人建议:生产环境用 cron,开发测试可以用 timedelta 图省事。

4.4 变量与连接管理:别再硬编码了

你想想看,如果数据库连接串、 这些东西直接写在 DAG 文件里,那每次换环境都得改代码。Airflow 提供了两个好东西:Variables(变量)和 Connections(连接)。

4.4.1 Variables:存配置参数

Variables 就是键值对。你可以在 Web UI 的 Admin → Variables 里添加,也可以用 CLI 或代码操作。

from airflow.models import Variable

# 获取变量
db_name = Variable.get("db_name", default_var="default_db")
# 设置变量
Variable.set("api_endpoint", "https://api.example.com/v1")
# 删除变量
Variable.delete("temp_config")
避坑指南:不要在 DAG 解析阶段大量调用 Variable.get()。因为每次调用都会查数据库,如果 DAG 文件多,解析会变得很慢。我习惯在 PythonOperator 的函数内部调用,只在任务执行时才去读变量。

4.4.2 Connections:管理外部系统连接

Connections 比 Variables 更强大,它专门用来存数据库、SSH、HTTP 等外部系统的连接信息。你可以在 Web UI 的 Admin → Connections 里配置。

连接类型 常用字段 示例
MySQL Host, Schema, Login, Password, Port 192.168.1.100:3306/mydb
PostgreSQL Host, Schema, Login, Password, Port pg-host:5432/analytics
HTTP Host, Login, Password https://api.example.com
SSH Host, Username, Password/Key 跳板机地址
S3 Extra(JSON 格式存 access key) {"aws_access_key_id":"...", "aws_secret_access_key":"..."}

在 DAG 里使用连接:

from airflow.hooks.base import BaseHook

# 获取连接对象
conn = BaseHook.get_connection("my_mysql_conn")
# 拿到具体信息
host = conn.host
port = conn.port
login = conn.login
password = conn.password
schema = conn.schema  # 数据库名

我在项目中遇到过最头疼的事:测试环境和生产环境的连接信息不一样,但 DAG 代码是同一份。解决方案就是连接名保持一致,比如都叫 prod_mysql,然后在不同环境的 Airflow 里配置不同的连接信息。代码不用改,只改配置。

4.5 本章知识体系总览

下面这张图把这一章的核心逻辑串起来了。你可以看到 DAG 从定义、任务编排、调度配置到外部依赖管理,是一条完整的链路。

Airflow DAG 实战核心结构 DAG 定义 dag_id / start_date / tags 任务定义 PythonOperator / BashOperator 依赖设置 >> 运算符 / set_downstream 调度配置 cron 表达式 / timedelta 变量管理 Variable.get() / Variable.set() 连接管理 BaseHook 关键要点 • DAG 定义时务必设置 catchup=False,避免历史任务补跑 • 依赖关系用 >> 运算符,清晰且不易出错 • 生产环境调度用 cron 表达式,确保整点对齐 • 敏感信息(密码、Key)必须存 Connections,不要硬编码 • 变量和连接的读取尽量放在任务函数内,避免 DAG 解析变慢

这一章的内容就到这里。你照着上面的代码写一个 DAG,放到 Airflow 里跑起来,就能看到任务在 Web UI 上跳动了。记住:先跑通最简单的,再慢慢加依赖和配置。别一上来就想搞复杂的 pipeline,容易把自己绕进去。