【前情提要】 Alertmanager 装好了,告警能从 Prometheus 推到 Alertmanager 了。但小林还没收到任何邮件。老周说:“Alertmanager 只会‘调度’,不会‘发邮件’。发邮件得靠 Postfix。”小林打开 Postfix 文档,准备配置 SMTP。但他不知道,QQ 邮箱授权码、sasl_passwd、main.cf,每一个都可能让他折腾。

一、Alertmanager 有嘴,但嘴被封住了

早上到工位,小林打开 Alertmanager UI,看到昨天触发的 HighCpuUsage 还挂在那。

“周哥,Alertmanager 能看到告警了,但我邮箱里啥都没有。”

“因为 Alertmanager 的‘嘴’还没通。”老周说。

“啥意思?”

“Alertmanager 知道要发邮件,但它不会自己发。它得调用一个‘邮件传输代理’,也就是 MTA。”老周说,“Linux 上最常用的 MTA 就是 Postfix。”

“那 Alertmanager 和 Postfix 啥关系?”

“Alertmanager 把邮件内容交给 Postfix,Postfix 负责把邮件发出去。”老周说,“你可以把 Alertmanager 理解成‘写信的人’,Postfix 理解成‘邮局’。信写好了,得有人送到邮局,邮局再发出去。”

“那为什么不用 Alertmanager 直接连 QQ 邮箱?”

“可以,但配置更复杂。”老周说,“Alertmanager 的 email_configs 直接配 smtp_smarthost 也能发,但 SMTP 认证、TLS、授权码这些,全得在 Alertmanager 里配。用 Postfix 做本地中继,Alertmanager 只需要写 localhost:25,剩下的交给 Postfix。 这样 Alertmanager 的配置最简单。”

“那我 Mac 上能用 Postfix 吗?”

“Mac 也有 Postfix,但 Mac 的 Postfix 配置和 Ubuntu 不一样。”老周说,“而且你这是虚拟机里的服务,邮件从 Ubuntu 发出去,跟 Mac 没关系。Postfix 装在 Ubuntu 上。”

小林记下:

  • Alertmanager 不直接发邮件,它调用 MTA
  • Linux 上最常用的 MTA 是 Postfix
  • Postfix 做本地中继,Alertmanager 只写 localhost:25
  • Postfix 装在 Ubuntu 上

二、安装 Postfix

“装 Postfix。”老周说。

sudo apt update
sudo apt install -y postfix mailutils

安装过程中,弹出一个界面:

Postfix Configuration
General type of mail configuration:
  No configuration
  Internet Site
  Internet with smarthost
  Satellite system
  Local only

“选哪个?”小林问。

“选 Internet Site。”老周说,“然后系统主机名填你的 Ubuntu 主机名,或者直接回车用默认。”

小林选了 Internet Site,回车。

安装完成。

“mailutils 是啥?”小林问。

“mailutils 提供 mail 命令,用来测试发信。”老周说,“Postfix 本身没有发信客户端,mailutils 补上。”

“那 Postfix 现在在跑吗?”

systemctl status postfix

输出:

● postfix.service - Postfix Mail Transport Agent
     Active: active (running)

“在跑。”小林说。

“验证端口。”

sudo ss -lntp | grep :25

输出:

LISTEN 0 100 127.0.0.1:25 0.0.0.0:* users:(("master",pid=1234,fd=13))

“127.0.0.1:25,说明 Postfix 只监听本地。”老周说,“这正好,Alertmanager 和 Postfix 在同一台机器,localhost:25 就够了。外部访问不了 25 端口,更安全。”

小林记下:

  • 安装:sudo apt install postfix mailutils
  • 配置类型:Internet Site
  • 默认监听:127.0.0.1:25
  • mailutils 提供 mail 测试命令

三、配置 QQ 邮箱 SMTP 中继

“现在配 QQ 邮箱。”老周说,“你先去 QQ 邮箱开 SMTP 服务,拿授权码。”

“授权码?不是密码?”

“QQ 邮箱不能用登录密码做 SMTP 认证。”老周说,“你得在 QQ 邮箱设置里开 SMTP 服务,它会给你一个授权码。这个授权码就是密码。”

小林打开 QQ 邮箱网页版,设置 → 账户 → POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务,开启 SMTP 服务。

弹出一个授权码:

授权码:xxxxxxxxxxxxxxxx

“记下来。”老周说,“这个码只显示一次,关了就得重新生成。”

“然后呢?”

“编辑 Postfix 配置。”

sudo nano /etc/postfix/main.cf

在文件末尾追加:

relayhost = [smtp.qq.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_use_tls = yes
smtp_tls_security_level = encrypt

“一行一行解释。”老周说。

  • relayhost = [smtp.qq.com]:587:中继服务器。所有邮件都发给 QQ 邮箱的 SMTP 服务器,由它转发。
  • smtp_sasl_auth_enable = yes:启用 SASL 认证。SMTP 需要用户名密码。
  • smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd:密码文件路径。
  • smtp_sasl_security_options = noanonymous:禁止匿名认证。
  • smtp_use_tls = yes:启用 TLS 加密。
  • smtp_tls_security_level = encrypt:强制加密。

“注意,[smtp.qq.com] 外面有方括号。”老周说,“方括号表示‘不要做 MX 查询,直接连这个主机’。没有方括号,Postfix 会去查 DNS 的 MX 记录,可能连不上。”

“那 587 端口呢?”

“QQ 邮箱 SMTP 支持 465 和 587。587 是 STARTTLS,465 是 SSL/TLS。你写 587,配合 smtp_use_tls = yes 和 encrypt,就是 STARTTLS。”

小林记下:

  • 中继:[smtp.qq.com]:587
  • 启用 SASL 认证
  • 密码文件:/etc/postfix/sasl_passwd
  • 强制 TLS 加密
  • 方括号表示不做 MX 查询

四、创建 sasl_passwd 密码文件

“现在写密码文件。”老周说。

sudo nano /etc/postfix/sasl_passwd

内容:

[smtp.qq.com]:587 your_email@qq.com:your_auth_code

“把 your_email@qq.com 换成你的 QQ 邮箱,your_auth_code 换成刚才的授权码。”老周说,“注意,中间是冒号,不是空格。”

小林写:

[smtp.qq.com]:587 xiaolin@qq.com:abcdefghijklmnop

保存,退出。

“现在生成数据库文件。”

sudo postmap /etc/postfix/sasl_passwd

“postmap 把文本文件转成 Postfix 能读的数据库文件。”老周说,“你以后改了 sasl_passwd,要重新 postmap。”

“那权限呢?”

“关键一步。”老周说,“sasl_passwd 和 sasl_passwd.db 权限必须是 600,只有 root 能读。不然 Postfix 会拒绝加载。”

sudo chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
sudo chown root:root /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db

“验证一下。”

ls -l /etc/postfix/sasl_passwd*

输出:

-rw------- 1 root root  ... /etc/postfix/sasl_passwd
-rw------- 1 root root  ... /etc/postfix/sasl_passwd.db

“-rw-------,说明只有 root 能读写。”老周说,“这就对了。”

“重启 Postfix。”

sudo systemctl restart postfix

“看日志。”

sudo tail -20 /var/log/mail.log

输出:

postfix/master: starting the Postfix mail system
postfix/master: daemon started

“没报错。”小林说。

五、测试发信

“现在测试。”老周说,“用 mail 命令发一封。”

echo "这是一封测试邮件。" | mail -s "Postfix 测试" your_email@qq.com

“把 your_email@qq.com 换成你自己的邮箱。”老周说,“你发给自己就行。”

小林发完,等了半分钟,打开 QQ 邮箱。

收件箱里出现了一封邮件:

发件人:xiaolin@qq.com
主题:Postfix 测试
内容:这是一封测试邮件。

“收到了!”小林兴奋地说。

“成了。”老周说,“Postfix 能发邮件了。现在配 Alertmanager,让它调用 Postfix。”

“等等,我有个问题。”小林说,“from 是 xiaolin@qq.com,这是我在 sasl_passwd 里写的邮箱。但 QQ 邮箱允许我发给自己,如果发给别人呢?”

“QQ 邮箱 SMTP 要求 from 必须是登录的邮箱本身。”老周说,“你不能用 alertmanager@company.com 做发件人,除非你有那个邮箱的 SMTP 权限。你现在的场景,发件人和收件人都是你自己,没问题。 你以后公司里用企业邮箱,配置类似,但认证信息不同。”

小林记下:

  • 测试:echo "内容" | mail -s "主题" 收件人
  • 发件人必须是 SMTP 登录邮箱本身
  • 个人测试:发件人和收件人都是自己

六、让 Alertmanager 调用 Postfix

“Postfix 通了,现在配 Alertmanager。”老周说。

“改 alertmanager.yml?”

“对。”

sudo nano /etc/alertmanager/alertmanager.yml

改成:

global:
  resolve_timeout: 5m
  smtp_smarthost: 'localhost:25'
  smtp_from: 'xiaolin@qq.com'
  smtp_require_tls: false

route:
  group_by: ['alertname']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: 'default-receiver'

receivers:
  - name: 'default-receiver'
    email_configs:
      - to: 'xiaolin@qq.com'
        send_resolved: true

“一行一行解释。”老周说。

  • smtp_smarthost: 'localhost:25':邮件交给本地 Postfix。
  • smtp_from: 'xiaolin@qq.com':发件人。必须和 Postfix 里 sasl_passwd 的邮箱一致。
  • smtp_require_tls: false:Alertmanager 到 Postfix 是本机通信,不需要 TLS。Postfix 到 QQ 邮箱才需要 TLS。
  • email_configs.to:收件人。你发给自己。
  • send_resolved: true:告警恢复后,也发一封邮件通知你。

“保存,检查配置。”

amtool check-config /etc/alertmanager/alertmanager.yml

输出:

Checking '/etc/alertmanager/alertmanager.yml'  SUCCESS
Found:
 - global config
 - route
 - 1 receiver(s)

“重启 Alertmanager。”

sudo systemctl restart alertmanager
sudo systemctl status alertmanager

输出:

Active: active (running)

“现在触发一次告警。”

七、排错挑战:邮件发不出去

小林跑:

stress --cpu 4 --timeout 300

等了几分钟,Prometheus 的 HighCpuUsage 变成 firing,Alertmanager UI 里也看到了告警。

但小林刷了五分钟 QQ 邮箱,一封邮件都没有。

“周哥,Alertmanager 有告警,但邮件没来。”

“看 Alertmanager 日志。”

sudo journalctl -u alertmanager -n 30

输出:

level=error msg="Notify for alerts failed" err="send email: dial tcp [::1]:25: connect: connection refused"

“dial tcp [::1]:25: connect: connection refused。”老周说,“注意 [::1],IPv6 的 localhost。Postfix 只监听了 127.0.0.1:25,没监听 IPv6 的 [::]:25。”

“那咋办?”

“把 Alertmanager 的 smtp_smarthost 改成 127.0.0.1:25,强制 IPv4。”

小林改:

smtp_smarthost: '127.0.0.1:25'

“重启 Alertmanager。”

sudo systemctl restart alertmanager

再次触发告警。这次,QQ 邮箱收到邮件了:

发件人:xiaolin@qq.com
主题:[FIRING:1] HighCpuUsage (warning)
内容:
  CPU 使用率超过 80%
  CPU 使用率已持续 2 分钟超过 80%,当前值 85.3%

“收到了!”小林兴奋地说。

排错思路总结:

  1. Alertmanager 有告警但没收到邮件,先看 Alertmanager 日志:sudo journalctl -u alertmanager -n 30。
  2. 如果日志显示 connection refused,检查 smtp_smarthost 里的 IP 版本。
  3. [::1] 是 IPv6,127.0.0.1 是 IPv4。
  4. 把 localhost:25 改成 127.0.0.1:25,强制 IPv4。
  5. 如果日志显示 authentication failed,检查 sasl_passwd 里的授权码和邮箱。
  6. 如果日志显示 Connection timed out,检查 Ubuntu 能否访问 smtp.qq.com:587。
  7. 如果日志显示 permission denied,检查 sasl_passwd 权限是否为 600。

“记住,”老周说,“localhost 在 IPv4 和 IPv6 环境下解析结果不同。 你以后配 SMTP,127.0.0.1 比 localhost 稳。”

八、send_resolved:告警恢复了也告诉你

“你刚才收到的是‘告警触发’邮件。”老周说,“但告警恢复了呢?”

“我 stress 停了之后,告警应该恢复了吧?”

“对。”老周说,“但你没收到‘恢复’邮件。因为 send_resolved 虽然写了 true,但你刚才触发的是 firing,恢复后 Alertmanager 会再发一封。”

小林等 stress 结束,等了 5 分钟(resolve_timeout),QQ 邮箱收到第二封邮件:

主题:[RESOLVED] HighCpuUsage
内容:
  CPU 使用率超过 80%(已恢复)

“这就是 send_resolved: true 的效果。”老周说,“告警触发时发一封,恢复时发一封。这样你知道问题解决了,不用一直挂着心。”

“那如果我不想要恢复邮件呢?”

“改成 false,只发触发邮件。”老周说,“但建议你开着。运维最怕的就是‘告警来了,不知道好没好’。恢复邮件让你心里有底。”

小林记下:

  • send_resolved: true:告警恢复也发邮件
  • 恢复邮件主题带 [RESOLVED]
  • 恢复时间由 resolve_timeout 决定,默认 5 分钟

九、邮件模板:让告警邮件更好看

“默认的告警邮件,内容比较糙。”老周说,“Alertmanager 支持 HTML 模板,你可以自定义。”

“怎么弄?”

“在 email_configs 里加 html 和 headers。”

email_configs:
  - to: 'xiaolin@qq.com'
    send_resolved: true
    html: '{{ template "email.default.html" . }}'
    headers:
      Subject: '{{ template "email.default.subject" . }}'

“但你需要先定义模板文件。”老周说,“你现在入门,先用默认模板就够了。以后想美化,再去定义。”

“那默认模板的主题格式是啥?”

“[FIRING:数量] 告警名 (级别)。”老周说,“比如你刚才收到的 [FIRING:1] HighCpuUsage (warning)。RESOLVED 开头的是恢复邮件。”

小林记下:

  • 默认主题:[FIRING:N] 告警名 (级别)
  • 恢复主题:[RESOLVED] 告警名
  • 自定义模板:在 email_configs 里加 html 和 headers

十、练习

  1. 练习一:安装 Postfix 和 mailutils,配置 QQ 邮箱 SMTP 中继。用 echo "测试" | mail -s "Postfix 测试" 你的邮箱 发送一封测试邮件,确认收到。
  2. 练习二:修改 /etc/alertmanager/alertmanager.yml,配置 smtp_smarthost: '127.0.0.1:25'、smtp_from 和 email_configs.to 为你的 QQ 邮箱。用 amtool check-config 检查通过后,重启 Alertmanager。触发 CPU 告警,确认收到触发邮件。
  3. 练习三:等 stress 结束后,等 5 分钟,确认收到 [RESOLVED] 恢复邮件。然后把 send_resolved 改成 false,再次触发告警,确认恢复后不再发邮件。

参考答案

练习一:

sudo apt install -y postfix mailutils

sudo nano /etc/postfix/main.cf

追加:

relayhost = [smtp.qq.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_use_tls = yes
smtp_tls_security_level = encrypt
sudo nano /etc/postfix/sasl_passwd

内容:

[smtp.qq.com]:587 你的邮箱@qq.com:你的授权码
sudo postmap /etc/postfix/sasl_passwd
sudo chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
sudo systemctl restart postfix
echo "测试" | mail -s "Postfix 测试" 你的邮箱@qq.com

练习二:

sudo nano /etc/alertmanager/alertmanager.yml

内容:

global:
  resolve_timeout: 5m
  smtp_smarthost: '127.0.0.1:25'
  smtp_from: '你的邮箱@qq.com'
  smtp_require_tls: false

route:
  group_by: ['alertname']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: 'default-receiver'

receivers:
  - name: 'default-receiver'
    email_configs:
      - to: '你的邮箱@qq.com'
        send_resolved: true
amtool check-config /etc/alertmanager/alertmanager.yml
sudo systemctl restart alertmanager
stress --cpu 4 --timeout 300

检查邮箱。

练习三:

等 stress 结束后 5 分钟,检查邮箱收到 [RESOLVED] 邮件。

sudo nano /etc/alertmanager/alertmanager.yml

把 send_resolved: true 改成 false。

sudo systemctl restart alertmanager
stress --cpu 4 --timeout 300

等恢复后,确认不再收到恢复邮件。

十一、下集预告

Postfix 配好了,Alertmanager 能发邮件了。小林第一次收到告警邮件,兴奋得不行。但老周说:“光收邮件不够,你得知道告警来了之后该干什么。”第二天凌晨,小林的手机响了——磁盘告警。他第一次体验 on-call。

下一集:《故障排查实战:当告警真的来了》