Apache Airflow 邮件通知配置详解:SMTP 后端、Jinja 模板、Connection 凭据与 Provider 替代后端
【免费下载链接】airflowApache Airflow - A platform to programmatically author, schedule, and monitor workflows项目地址: https://gitcode.com/GitHub_Trending/ai/airflow
本篇基于 Apache Airflow 核心文档airflow-core/docs/howto/email-config.rst展开,系统讲解 Airflow 邮件通知体系的完整配置方式:如何选择与替换email_backend、如何用subject_template/html_content_template定制邮件标题与正文、如何通过smtp_defaultConnection 安全存放 SMTP 凭据,并结合源码 airflow-core/src/airflow/utils/email.py 剖析邮件发送的完整调用链、重试机制与 SSL 上下文处理,读完即可独立完成生产环境的邮件告警搭建。
一、[email] 配置段:选择邮件后端
Airflow 所有邮件(任务失败/重试回调、DAG 级邮件、send_email工具函数发出的邮件)统一走一个可插拔的后端机制。在airflow.cfg的[email]段中设置email_backend:
[email] email_backend = airflow.utils.email.send_email_smtp subject_template = /path/to/my_subject_template_file html_content_template = /path/to/my_html_content_template_file等价的环境变量写法:
AIRFLOW__EMAIL__EMAIL_BACKEND=airflow.utils.email.send_email_smtp AIRFLOW__EMAIL__SUBJECT_TEMPLATE=/path/to/my_subject_template_file AIRFLOW__EMAIL__HTML_CONTENT_TEMPLATE=/path/to/my_html_content_template_file从源码看,email_backend的默认值正是airflow.utils.email.send_email_smtp(通过 SMTP 发信),这一默认值定义在配置模板 airflow-core/src/airflow/config_templates/config.yml 中。
email_backend的本质是一个点分路径(dotted path):入口函数 send_email() 通过conf.getimport("email", "EMAIL_BACKEND")动态导入该路径指向的可调用对象并执行:
# airflow-core/src/airflow/utils/email.py 中的核心逻辑 backend = conf.getimport("email", "EMAIL_BACKEND") backend_conn_id = conn_id or conf.get("email", "EMAIL_CONN_ID") from_email = conf.get("email", "from_email", fallback=None) ... return backend(to_comma_separated, subject, html_content, files=files, dryrun=dryrun, cc=cc, bcc=bcc, ..., conn_id=backend_conn_id, from_email=from_email, ...)从源码结构看,这意味着任何符合send_email(to, subject, html_content, ...)签名的函数都可以作为后端,包括社区 provider 提供的第三方 API 实现(见第六节)。
1.1 [email] 段完整参数一览
结合配置模板 airflow-core/src/airflow/config_templates/config.yml,[email]段可用选项及默认值如下:
| 配置项 | 说明 | 默认值 |
|---|---|---|
email_backend | 邮件后端函数的点分路径 | airflow.utils.email.send_email_smtp |
email_conn_id | 用于存放凭据的 Connection ID | smtp_default |
default_email_on_retry | 任务重试时是否发邮件告警 | True |
default_email_on_failure | 任务失败时是否发邮件告警 | True |
subject_template | 邮件标题的 Jinja2 模板文件路径(未设置时使用内置基础模板) | 空 |
html_content_template | 邮件正文的 Jinja2 模板文件路径(未设置时使用内置基础模板) | 空 |
from_email | 发件人地址,支持Sender Name <sender@email.com>格式 | 空 |
ssl_context | SMTP/IMAP SSL 连接使用的上下文:default或none | default |
二、用 Jinja2 模板定制邮件标题与正文
subject_template与html_content_template均为文件路径,文件内容使用Jinja2 模板语法渲染,模板中可以直接访问当前任务实例(Task Instance)的信息,例如try_number、max_tries、exception_html、ti.log_url、ti.hostname、ti.mark_success_url等。
官方文档给出的html_content_template示例文件内容如下:
Try {{try_number}} out of {{max_tries + 1}}<br> Exception:<br>{{exception_html}}<br> Log: <a href="{{ti.log_url}}">Link</a><br> Host: {{ti.hostname}}<br> Mark success: <a href="{{ti.mark_success_url}}">Link</a><br>subject_template同样支持 Jinja2 变量,通常写成一个简短的单行文本(如[{{ dag.dag_id }}] Task failed: {{ ti.task_id }})。两个模板文件都未设置时,Airflow 使用内置的基础模板渲染标题与正文。
三、配置发件人地址 from_email
在[email]段设置from_email指定发件人:
[email] from_email = "John Doe <johndoe@example.com>"等价环境变量:
AIRFLOW__EMAIL__FROM_EMAIL="John Doe <johndoe@example.com>"这里有一个容易被忽略的优先级细节。从 send_email_smtp() 源码看,mail_from的取值顺序是:
- 优先取
[smtp] smtp_mail_from配置(默认airflow@example.com,见 config.yml); - 若未设置则回退到
from_email; - 两者都拿不到时抛出
ValueError("You should set from email - ...")。
也就是说smtp/smtp_mail_from的实际生效优先级高于email/from_email,调整发件人时建议同时检查两处配置,避免"改了 from_email 却没生效"的困惑。
四、SMTP 服务器参数:[smtp] 配置段
若email_backend使用默认的send_email_smtp,SMTP 服务器参数只能从配置中读取(这一点源码中写得很明确:host、port 等一律来自conf,只有账号密码可来自 Connection)。[smtp]段完整参数及默认值(来自 config.yml):
| 配置项 | 说明 | 默认值 |
|---|---|---|
smtp_host | SMTP 服务器地址 | localhost |
smtp_port | SMTP 端口 | 25 |
smtp_starttls | 连接后是否使用 STARTTLS | True |
smtp_ssl | 是否使用 SSL 隐式连接(SMTP_SSL) | False |
smtp_mail_from | 默认发件人地址 | airflow@example.com |
smtp_timeout | 建立连接的超时时间(秒) | 30 |
smtp_retry_limit | 首次尝试后重试连接的次数 | 5 |
对应的典型配置:
[smtp] smtp_host = smtp.example.com smtp_port = 587 smtp_starttls = True smtp_ssl = False smtp_mail_from = airflow@example.com smtp_timeout = 30 smtp_retry_limit = 5源码 send_mime_email() 中的发送行为值得注意:
- 重试机制:最多进行
smtp_retry_limit + 1次尝试,捕获smtplib.SMTPServerDisconnected后继续重试,超出重试上限才向上抛出异常; - STARTTLS 上下文:
smtp_starttls为真时调用smtp_conn.starttls(context=_get_ssl_context()),而 _get_ssl_context() 根据email.ssl_context决定返回ssl.create_default_context()(default,推荐)还是None(none,跳过证书校验——官方明确警告这会暴露于中间人攻击,只应在修复证书问题期间临时使用); - 连接方式:
smtp_ssl为真时创建smtplib.SMTP_SSL(465 端口常见),否则创建普通smtplib.SMTP。
五、用 Connection 存放 SMTP 凭据:smtp_default
若不想把 SMTP 用户名/密码直接写进配置文件或环境变量,可以创建一个类型为Email的 Connection(默认 ID 为smtp_default),把凭据存在其中;也可以自选 Connection 名,并在[email]段用email_conn_id指定它。文档明确说明:该 Connection 可以是任意类型(例如 HTTP connection),[smtp]段的 host、port 等参数仍然只从配置读取。
源码印证了这一点。在 send_mime_email() 中:
smtp_user = None smtp_password = None if conn_id is not None: try: from airflow.models import Connection airflow_conn = Connection.get_connection_from_secrets(conn_id) smtp_user = airflow_conn.login smtp_password = airflow_conn.password except AirflowException: pass if smtp_user is None or smtp_password is None: log.debug("No user/password found for SMTP, so logging in with no authentication.")即 Airflow 从 Connection 中只取login/password两个字段,取不到时降级为无认证登录(本地自建 SMTP 常见场景)。另外,conn_id的解析顺序是:调用方显式传入的conn_id参数 >email.email_conn_id配置(默认smtp_default),见 send_email()。
六、验证当前生效的邮件后端
配置完成后,可以用 CLI 直接查看当前生效的后端,确认配置没有写错位置:
$ airflow config get-value email email_backend airflow.utils.email.send_email_smtp该命令返回的是综合了airflow.cfg与环境变量覆盖之后的最终生效值。关于配置项设置方式的更多说明,可参考 set-config 指南。
七、发送链路全貌:从 send_email 到 MIME 报文
把前几节串起来,Airflow 默认后端的完整调用链为:
- 入口:send_email() 解析
email_backend与email_conn_id,将收件人归一化为逗号分隔字符串后调用后端函数; - SMTP 后端:send_email_smtp() 按优先级确定
mail_from,随后构建 MIME 报文; - 构建报文:build_mime_message() 创建
MIMEMultipart,写入Subject/From/To/Date头,正文以MIMEText(html_content, "html", ...)挂载;支持files参数附加文件(以MIMEApplication作为附件),CC 地址会出现在头字段并计入收件人列表,而BCC 只计入收件人、不出现在头字段中;还支持custom_headers注入任意自定义头; - 投递:send_mime_email() 读取
[smtp]段参数、从 Connection 取凭据,带重试地完成连接(可选 STARTTLS/SSL)、登录、sendmail与quit。
dryrun=True时第 4 步只记日志不真正发信,可用于开发环境联调。
八、替代邮件后端:通过 Provider 走第三方 API
除了 SMTP,email_backend还可以指向社区 provider 提供的实现,让邮件经由第三方服务的 API 发出。使用步骤(与原文档一致):
- 安装对应的 provider 发行包;
- 将
email_backend设置为该 providersend_email函数的点分路径; - 将
email_conn_id设置为存放所需凭据的 Connection。
社区管理的 provider 所提供的邮件后端完整清单见 providers-summary-docs/core-extensions/email-backends.rst,该页面汇总了所有通过 community-managed providers 暴露的 email backend 实现。
由于send_email()的后端解析完全基于conf.getimport的通用导入机制(从源码结构看),provider 后端只要遵循相同的函数签名即可无缝替换,无需改动任何 Airflow 核心代码。
九、小结:一份可落地的最小配置
综合以上各节,一个生产可用的最小配置示例:
[email] email_backend = airflow.utils.email.send_email_smtp email_conn_id = smtp_default subject_template = /opt/airflow/templates/email_subject.jinja2 html_content_template = /opt/airflow/templates/email_body.jinja2 from_email = "Airflow Alert <alert@example.com>" ssl_context = default [smtp] smtp_host = smtp.example.com smtp_port = 587 smtp_starttls = True smtp_ssl = False smtp_mail_from = alert@example.com smtp_timeout = 30 smtp_retry_limit = 5再配合 UI 中创建的smtp_defaultConnection(凭据)与两个 Jinja2 模板文件,即可完成 Airflow 的邮件告警体系;default_email_on_retry与default_email_on_failure默认均为True,任务重试与失败通知开箱即用。
【免费下载链接】airflowApache Airflow - A platform to programmatically author, schedule, and monitor workflows项目地址: https://gitcode.com/GitHub_Trending/ai/airflow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考