
数据流水线总是半夜崩?Airflow 把「定时任务」变成了可观测的工程
一堆 crontab 散落各处、崩了不知道跑到第几步、重跑要从头来——这是数据流水线最常见的困境。这篇讲清楚 Airflow 是什么、为什么值得上、五分钟怎么跑起来,以及哪些情况其实不该用它。
定时任务跑到一半崩了,日志只留下一句 traceback,你不知道它跑到第几步、要不要从头再来。这是很多人维护数据流水线最熟悉的噩梦。
更常见的场景是:一堆 crontab 散落在几台机器上,谁依赖谁全靠人脑记。上游数据延迟了,下游照跑不误,跑出一堆脏数据,第二天业务方来问你才发现。
Apache Airflow 解决的就是这一类问题。它在 GitHub 上有 4.7 万 star,Apache-2.0 协议,商用完全没问题。这篇把它讲清楚:它到底是什么、为什么值得上、怎么最快跑起来,以及哪些情况其实不该用它。

它到底是什么
一句话:Airflow 是一个用 Python 代码来编写、调度和监控工作流的平台。
关键词是"用代码"。你的工作流不是在某个网页上点出来的,而是一个 Python 文件,里面有任务、有依赖关系。这件事带来的好处是决定性的——工作流可以进 Git、可以 code review、可以做版本管理、可以用 CI 跑测试。运维同学最怕的"配置漂移",在 Airflow 里从根上被掐掉了。
Airflow 里有两个必须分清的概念:
- DAG(有向无环图):描述"哪些任务、谁先谁后"。一个 DAG 就是一个工作流。
- Task:DAG 里的一个具体步骤,比如跑一段 SQL、调一个接口、搬一个文件。
DAG 定义的是结构,不是数据。任务之间传递少量元数据,大数据量走外部存储。理解这一点,能帮你避开后面很多坑。
为什么不用 crontab
很多人第一反应是"我用 cron 也能定时跑"。能跑,但缺的东西太多:
- 依赖管理:cron 不知道 B 要等 A 跑完,你只能靠"预估 A 要 10 分钟,那 B 定在 10 分钟后"这种脆弱的约定
- 失败重试:cron 崩了就是崩了,没有自动重试,更没有指数退避
- 回溯补数:上游修好了要重跑过去 7 天的数据,cron 得你手动一天天点
- 可观测性:谁在跑、跑了多久、哪一步慢了,cron 一概不知
- 告警:失败了谁能第一时间知道
Airflow 把这些全做成了内置能力。更关键的是回填(backfill):你指定一个时间区间,它自动把这段时间缺失的任务补跑一遍,而且保证幂等。这一条在数据场景里能救命。
五分钟跑起来
最快的方式是 Docker。官方提供了 compose 文件,几步就能起一个能用的环境。
一个最小可用的 DAG 长这样:
from airflow import DAG
from airflow.operators.bash import BashOperator
from datetime import datetime
with DAG(
dag_id="daily_report",
start_date=datetime(2026, 1, 1),
schedule="@daily",
catchup=False,
) as dag:
extract = BashOperator(
task_id="extract",
bash_command="python /opt/scripts/extract.py",
)
load = BashOperator(
task_id="load",
bash_command="python /opt/scripts/load.py",
)
extract >> load
最后那行 extract >> load 就是在声明依赖:extract 跑完才跑 load。你也可以用 <<,或者列表形式声明多个依赖。
把文件丢进 dags/ 目录,Airflow 会自动加载,Web UI 里立刻能看到。改一行代码、刷新页面、看到依赖图变了——这个反馈循环非常快,是 Airflow 上手体验最好的部分。
上手要理解的三个机制
调度时间不等于执行时间。 这是新人最常踩的坑。Airflow 的 DAG 有一个"数据区间"的概念:@daily 的任务,在 1 月 2 日凌晨跑的那次,处理的是 1 月 1 日的数据区间。它不是"到点就跑",而是"到点了,该处理上一区间的数据了"。想明白了这一点,execution_date 的很多诡异行为就都能解释了。
幂等性要自己保证。 Airflow 会重试、会回填,意味着同一个任务可能被执行多次。如果你的任务不是幂等的(跑两次结果不一样,或者产生重复数据),重跑就是灾难。正确做法是让任务写入时用"覆盖分区"而不是"追加"。
Executor 决定能跑多大。 默认的 executor 只适合开发调试。生产环境你需要在 Celery Executor 和 Kubernetes Executor 之间选:前者靠一组常驻 worker,后者每个任务开一个 Pod。任务多、资源需求差异大的选 K8s;稳定、量大、追求吞吐的选 Celery。
什么人适合上,什么情况先别急
适合的:
- 有明确的批处理流水线,任务之间有依赖关系
- 需要回溯补数、失败自动重试
- 团队希望流水线能被 code review、能进版本控制
- 数据量在"单机跑不动、上 Spark 又太重"这个区间
先别急的:
- 实时流处理。Airflow 是批处理调度器,不是流引擎,秒级延迟的需求请找 Flink 这类
- 只有两三个独立脚本。为三个定时任务上一套 Airflow,运维成本远超收益
- 任务里要传大数据。Airflow 的 XCom 是给元数据用的,把 DataFrame 塞进去会直接把元数据库压垮
生产环境必须做的四件事
元数据库换成 Postgres 或 MySQL。 Airflow 默认的 SQLite 只支持单线程,生产上会直接锁死。这个改动要在上量之前做,之后迁移会比较麻烦。
把 DAG 解析和执行分开。 DAG 文件会被调度器周期性解析,如果解析逻辑里放了重型操作(比如查数据库、读大文件),会拖垮整个调度器。原则:DAG 文件只定义结构,重活放进 Task 里。
资源隔离用 Pool。 不同 DAG 之间会争抢 worker 资源。用 Pool 给关键流水线预留槽位,避免一个不重要的大任务把核心报表的资源吃光。这是多团队共用一套 Airflow 时的必备配置。
日志要外挂。 默认日志存在本地,容器一重启就没了。配远程日志(S3、GCS 或 NFS),出事之后你才有东西可查——排查问题时最有价值的往往就是那几行日志。
排查问题的三个常用入口
任务失败时不要只看最后一行的报错。三个地方按这个顺序查:
1. Task 实例的日志:Web UI 里点进具体任务看完整日志,这是最直接的
2. 调度器日志:如果 DAG 压根没被调度,问题在调度器侧(常见原因是 DAG 文件有语法错误)
3. 元数据库的 dag_run 表:看这次运行的状态是什么、卡在哪一步
一个经验规律:任务根本没启动,八成是依赖没满足或者 DAG 解析失败;任务启动了但失败,才是代码本身的问题。
真实建议
如果你现在有一堆散落的 cron,最务实的迁移路径不是一次性全搬。挑一条最痛的流水线——就是那个"崩了要人工介入、还得从头跑"的——先把它改成 DAG。等你在 Web UI 里第一次看到"哪一步慢了 40 分钟"这个信息,你会立刻明白这套东西值不值。
官方仓库和完整文档在 github.com/apache/airflow。
适合谁:维护批处理任务的后端/数据工程师、需要回溯补数的团队、想把流水线纳入版本控制的工程团队



