未记录事实核验 · 状态:维护中
记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。
warning 适用范围与待维护事项
本文用于已有 Python、Airflow DAG 与部署基础的开发及运维人员,在测试环境练习监控告警;代码为局部示例,须补齐导入、业务函数和依赖。原稿未注明 Airflow/Provider 版本,UI、CLI、execution_date、SLA 与邮件配置须按目标版本核对,Celery 排查仅适用于对应部署。机器人接口及 SMTP 参数未经本文实测,须验证签名、权限、响应和实际送达;Clear、Mark Success 或重新调度前,须确认任务已停止及写入幂等性,防止重复写入或跳过数据校验。
监控告警与故障处理
abstract 本文核心内容
- Web UI 各视图的使用技巧
- 任务重试策略配置(retries、retry_delay、exponential backoff)
- on_failure_callback:自定义失败处理逻辑
- 邮件/钉钉/飞书告警集成
- 日志查看与排查技巧
- 常见故障的原因和解决方案
Web UI 使用指南
DAGs 列表页
登录后进入的主页,核心操作:
- 暂停/恢复 DAG:点击 DAG 名称左侧的开关
- 手动触发:点击 "Trigger DAG" 按钮(三角形图标)
- 删除 DAG:在 DAG 列表找到 DAG,点击垃圾桶图标(仅删除元数据,不删文件)
- 查看运行状态:彩色小方块代表最近几次运行状态(绿=成功,红=失败)
DAG 详情页:四个核心视图
Grid View(网格视图):
- 横轴是时间(最近 N 次运行),纵轴是 Task 列表
- 快速看到哪些日期哪些 Task 失败了
- 点击某个格子可以看该 Task Instance 的详情
Graph View(图形视图):
- 显示 DAG 的依赖关系图
- 可以选择某次 DAG Run,查看各 Task 的实时状态
- 点击任意 Task 可以查看日志、重新运行、标记成功等
Calendar View(日历视图):
- 按月日历显示每天的 DAG Run 状态
- 快速定位哪天出了问题
Gantt View(甘特图视图):
- 展示每个 Task 的开始和结束时间
- 用于分析任务耗时和并行度是否合理
常用操作
在 Graph View 点击某个 Task 后,可以:
- View Log:查看运行日志
- Run:在支持该操作的版本中请求运行所选 Task Instance;是否执行及执行范围取决于依赖检查和所选选项,任务输出也可能影响下游
- Clear:清除状态,等待 Scheduler 重新调度
- Mark Success:手动标记为成功,不执行任务逻辑;可能使下游满足触发条件,但不保证下游一定运行,使用前必须确认所需数据已就绪
- Mark Failed:强制标记为失败
任务重试策略
基础重试配置
from datetime import timedelta
default_args = {
# 失败后最多重试 3 次
"retries": 3,
# 每次重试等待 5 分钟
"retry_delay": timedelta(minutes=5),
# 指数退避:等待时间随重试次数增长;实际间隔可能含抖动,并受版本实现和 max_retry_delay 限制,不保证恰为 5、10、20 分钟
"retry_exponential_backoff": True,
# 最大重试等待时间(配合指数退避使用)
"max_retry_delay": timedelta(hours=1),
}
with DAG(
dag_id="resilient_etl",
default_args=default_args,
# ...
) as dag:
pass
针对不同 Task 设置不同重试策略
# 网络请求容易偶发失败,多重试几次
api_task = PythonOperator(
task_id="call_external_api",
python_callable=call_api,
retries=5,
retry_delay=timedelta(minutes=2),
retry_exponential_backoff=True,
)
# Spark 作业比较稳,失败通常是真实问题
spark_task = SparkSubmitOperator(
task_id="run_spark_job",
application="/jobs/etl.py",
retries=1, # 只重试 1 次
retry_delay=timedelta(minutes=30), # 等 30 分钟再重试(让集群资源释放)
)
# 数据质量检查失败不应该重试(失败本身就是有意义的信号)
quality_check = PythonOperator(
task_id="data_quality_check",
python_callable=check_quality,
retries=0, # 不重试
)
on_failure_callback:自定义失败处理
当 Task 失败时,可以执行自定义的回调函数,比如发送告警通知。
基础用法
def task_failure_alert(context):
"""Task 失败时的回调函数"""
task_instance = context["task_instance"]
dag_id = context["dag"].dag_id
task_id = task_instance.task_id
execution_date = context["execution_date"]
log_url = task_instance.log_url
message = f"""
Airflow Task 失败告警
DAG: {dag_id}
Task: {task_id}
执行时间: {execution_date}
查看日志: {log_url}
""".strip()
print(f"发送告警:{message}")
# 这里调用你的告警通知函数
def dag_failure_alert(context):
"""DAG 级别的失败回调(整个 DAG Run 失败时触发)"""
pass
with DAG(
dag_id="monitored_dag",
on_failure_callback=dag_failure_alert, # DAG 级别失败回调
# ...
) as dag:
critical_task = PythonOperator(
task_id="critical_task",
python_callable=run_critical,
on_failure_callback=task_failure_alert, # Task 级别失败回调
on_success_callback=task_success_log, # 成功回调
on_retry_callback=task_retry_alert, # 重试回调
)
钉钉告警集成
本文以钉钉群机器人 Webhook 演示一种告警接入方式;是否适用取决于组织使用的平台和机器人安全配置。
import requests
import json
from datetime import datetime
def send_dingtalk_alert(context):
"""发送钉钉群告警"""
task_instance = context["task_instance"]
dag_id = context["dag"].dag_id
task_id = task_instance.task_id
execution_date = str(context["execution_date"])
log_url = task_instance.log_url
# 从环境变量读取钉钉 Webhook URL(不要硬写在代码里)
import os
webhook_url = os.environ.get("DINGTALK_WEBHOOK_URL")
if not webhook_url:
print("DINGTALK_WEBHOOK_URL 未配置,跳过钉钉告警")
return
# 构建钉钉消息(Markdown 格式)
message = {
"msgtype": "markdown",
"markdown": {
"title": f"Airflow 告警:{dag_id}",
"text": f"""## Airflow Task 失败告警
**DAG**: {dag_id}
**Task**: {task_id}
**执行日期**: {execution_date}
**失败时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
[查看日志]({log_url})
""",
},
"at": {
"isAtAll": False,
},
}
# 发送告警
try:
response = requests.post(
webhook_url,
headers={"Content-Type": "application/json"},
data=json.dumps(message),
timeout=10,
)
if response.json().get("errcode") == 0:
print("钉钉告警发送成功")
else:
print(f"钉钉告警发送失败:{response.text}")
except Exception as e:
print(f"钉钉告警发送异常:{e}")
# 在 DAG 中使用
with DAG(
dag_id="production_etl",
default_args={
"on_failure_callback": send_dingtalk_alert,
"retries": 2,
},
# ...
) as dag:
pass
飞书告警集成
飞书机器人的接入方式与钉钉类似:
def send_feishu_alert(context):
"""发送飞书群告警"""
import requests
import json
import os
task_instance = context["task_instance"]
dag_id = context["dag"].dag_id
task_id = task_instance.task_id
execution_date = str(context["execution_date"])
log_url = task_instance.log_url
webhook_url = os.environ.get("FEISHU_WEBHOOK_URL")
if not webhook_url:
return
# 飞书消息格式(Rich Text)
message = {
"msg_type": "post",
"content": {
"post": {
"zh_cn": {
"title": f"Airflow 告警:{dag_id}",
"content": [
[{"tag": "text", "text": f"DAG: {dag_id}"}],
[{"tag": "text", "text": f"Task: {task_id}"}],
[{"tag": "text", "text": f"执行日期: {execution_date}"}],
[
{"tag": "text", "text": "查看日志:"},
{"tag": "a", "text": "点击这里", "href": log_url},
],
],
}
}
},
}
try:
response = requests.post(webhook_url, json=message, timeout=10)
response.raise_for_status()
print(f"飞书告警请求已提交,须按当前接口约定检查业务响应并验证实际送达:{response.text}")
except Exception as e:
print(f"飞书告警异常:{e}")
邮件告警配置
Airflow 内置支持邮件告警,通过配置 SMTP 即可:
# airflow.cfg 或环境变量
[smtp]
smtp_host = smtp.exmail.qq.com # 企业邮箱 SMTP 地址
smtp_starttls = True
smtp_ssl = False
smtp_user = airflow-alert@company.com
smtp_password = your_email_password
smtp_port = 587
smtp_mail_from = airflow-alert@company.com
# 在 default_args 中启用邮件告警
default_args = {
"email": ["data-team@company.com"],
"email_on_failure": True, # 失败时发邮件
"email_on_retry": False, # 重试时不发(避免邮件轰炸)
# 标准任务参数不提供 email_on_success;如需成功通知,可在 on_success_callback 中显式发送
}
日志查看与排查技巧
Web UI 查看日志
- 进入 DAG 的 Grid View 或 Graph View
- 点击失败的 Task(红色方块)
- 点击 "View Log"
- 每次运行会有单独的日志文件(attempt_1.log、attempt_2.log...)
命令行查看日志
# 查看特定 Task Instance 的日志:使用 Web UI 的 View Log 或部署配置的日志后端
# 不假定存在 airflow tasks logs 子命令;先查看当前版本支持的任务命令
airflow tasks --help
# 查看最近的错误日志
tail -100 $AIRFLOW_HOME/logs/scheduler/latest/my_dag.py.log
# Docker 环境下
docker compose logs -f airflow-scheduler | grep -i "error\|exception"
docker compose logs -f airflow-worker | tail -100
常见日志模式
# 任务找不到 Python 模块
ModuleNotFoundError: No module named 'pandas'
# 解决:在 Airflow 环境中安装缺少的包
# 数据库连接失败
sqlalchemy.exc.OperationalError: (mysql.connector.errors.InterfaceError)
# 解决:检查 Connection 配置,验证数据库是否可达
# XCom 数据太大
sqlalchemy.exc.DataError: (1406, "Data too long for column 'value'")
# 解决:XCom 不适合传大数据,改用文件路径传递
# 任务僵尸(zombie task)
[2024-01-15 02:30:00] {taskinstance.py:1} WARNING - Detected zombie job
# 解决:先确认原任务进程已停止、外部作业状态及写入幂等性,再清除对应的 Task Instance 并重新调度
常见故障处理手册
故障 1:DAG 不出现在 Web UI
症状:把 DAG 文件放到 dags 目录后,Web UI 里看不到
排查步骤:
# 1. 检查 DAG 文件是否有语法错误
python dags/my_dag.py
# 2. 查看 Airflow 导入错误
airflow dags list-import-errors
# 3. 检查 Scheduler 日志
docker compose logs airflow-scheduler | grep "ERROR\|my_dag"
# 4. 在支持该命令的版本中重新生成序列化 DAG;这不等同于触发 Scheduler 立即扫描
# 操作前先确认当前版本的命令及参数
airflow dags reserialize --help
常见原因:
- Python 语法错误
- 缺少 import 的模块
- 循环导入
- DAG 实例未被当前版本的发现机制识别:检查是否通过模块全局变量暴露 DAG 实例,或使用该版本支持的自动注册方式;变量名不必为
dag,也不必须使用with DAG(...) as dag:
故障 2:任务一直是 queued 状态
症状:任务应该运行,但状态停在 queued 很久
排查步骤:
# 检查 Worker 是否在线
# 不要用 airflow celery worker --pid 查看进程;worker 子命令用于启动 Worker
# 在部署平台的进程或容器列表中检查已有 Worker
# 或
docker compose ps | grep worker
# 检查 Worker 并发是否已满:结合 Worker 配置、日志及执行器监控查看活动任务数
# 另检查 Pool slot 是否耗尽:在提供该菜单的版本中,通过 Web UI → Admin → Pools 查看任务所属 Pool 的使用情况
# 检查任务队列:在 CeleryExecutor 部署中使用已配置的 Celery 应用执行 inspect active
# Airflow CLI 不保证提供 airflow celery inspect 子命令;先确认当前版本支持的命令
airflow celery --help
常见原因:
- Worker 进程没有启动或崩溃
- Worker 并发数(
worker_concurrency)设置太小 - Pool slot 耗尽
故障 3:任务成功但下游没有触发
症状:某个 Task 显示成功,但依赖它的下游 Task 没有运行
排查步骤:
# 检查下游 Task 的 trigger_rule 配置
# 检查下游 Task 的上游是否存在 BranchPythonOperator,以及未选中的分支是否被 skip
# 默认 trigger_rule="all_success" 要求所有直接上游成功;直接上游存在 skipped 时不满足该条件
# 检查 DAG 的 depends_on_past 配置
# 如果上一次这个 Task 失败了,本次可能被阻塞
故障 4:Scheduler 没有触发应该运行的 DAG
症状:cron 时间到了,但 DAG 没有新的 DAG Run
排查步骤:
# 1. 检查 DAG 是否被暂停
airflow dags list | grep my_dag
# is_paused 应该为 False
# 2. 检查 start_date 是否比当前时间晚
# start_date 设置了未来的日期?
# 3. 检查 max_active_runs 是否已满
# 已有 DAG Run 在执行,达到 max_active_runs 限制?
# 4. 检查 Scheduler 是否运行正常
# 不要假定 scheduler 提供 --health-check;使用目标版本支持的健康检查方式
# 支持 jobs check 的 Airflow 2.x 部署可检查本机 SchedulerJob 心跳
airflow jobs check --job-type SchedulerJob --local
故障 5:僵尸任务(Zombie Task)
症状:任务明明已经结束(Worker 进程消失),但状态还显示 running
原因:Worker 意外宕机,来不及更新 MetaDB 中的状态
解决方案:
# 在 Web UI 中:
# 找到对应的 Task Instance → 右键 → Mark Failed
# 然后 Clear 重新调度
# 命令行清除
airflow tasks clear my_dag -t stuck_task -s 2024-01-15 -e 2024-01-15
# 配置 Scheduler 的僵尸任务检测(airflow.cfg)
# [scheduler]
# scheduler_zombie_task_threshold = 300 # 适用于提供该配置项的 Airflow 2.x;表示判定僵尸任务的心跳阈值,实际检测还受检查周期影响
告警通知最佳实践
| 告警场景 | 推荐方式 | 配置位置 |
|---|---|---|
| 关键任务失败 | 钉钉/飞书 + 邮件 | on_failure_callback |
| 任务重试 | 仅记录日志(避免轰炸) | email_on_retry=False |
| SLA 超时 | 钉钉/飞书 | sla_miss_callback |
| 任务成功 | 仅在最后汇总任务发送 | on_success_callback(最后一个 Task) |
| 整个 DAG 失败 | 高优先级告警 | DAG 的 on_failure_callback |
小结
- Web UI 的 Grid View 是日常监控的核心,快速定位失败的 Task
- 重试策略要区分场景:网络请求多重试,数据质量检查不重试
- 告警用
on_failure_callback,国内推荐钉钉/飞书 Webhook - 排查问题的第一步永远是 查日志:Web UI 的 View Log 或 Scheduler 日志
- 僵尸任务处理:先确认原任务及外部作业已停止、重跑不会重复写入,再按实际状态使用 Mark Failed 或 Clear 重新调度
下一篇:10-数据管道实战 — 完整端到端案例:MySQL → 清洗 → 数仓 → 报表 → 邮件
所属系列:00-Airflow学习路线图 | 上一篇:08-变量连接与配置管理 | 下一篇:10-数据管道实战
info 前置知识
- 了解 Airflow Web UI 的基本操作(DAG 列表、Graph View),参考 Airflow 安装部署
- 理解 Task Instance 的状态机(running/success/failed/skipped),参考 Airflow 核心概念
- 有 Python 函数回调(callback)的基本概念
掌握检查
- [ ] 能写出一个带钉钉 Webhook 告警的
on_failure_callback函数,并在 DAG 的default_args中注册它 - [ ] 能区分
retries(重试次数)、retry_delay(重试间隔)、execution_timeout(执行超时)三个参数的作用 - [ ] 知道"僵尸任务"的成因(Worker 宕机未更新 MetaDB),以及如何通过 Web UI 或命令行清除
- [ ] 能说出排查 DAG 不触发问题的完整步骤(至少 4 步:检查开关、检查 start_date、检查 max_active_runs、检查 Scheduler 日志)
- [ ] 理解为什么
email_on_retry应该设为False,而email_on_failure设为True
实践练习
基础题:给你的测试 DAG 配置钉钉告警:在 default_args 中注册 on_failure_callback,函数内用 requests.post 调用钉钉 Webhook,发送包含 DAG ID、Task ID 和失败时间的消息。手动让一个 Task 失败,验证能否收到钉钉消息。
进阶题:生产环境中有一个 Task 经常因为短暂的网络波动失败,重试 2 次后通常会成功。但数据质量检查 Task 一旦失败说明数据本身有问题,不应该重试。如何在同一个 DAG 中为不同的 Task 配置不同的重试策略?写出具体代码。
#Airflow #监控 #告警 #故障排查 #重试策略 #钉钉 #飞书