1. 项目缘起从监控数据到告警邮件的最后一公里在运维和DevOps的日常里监控系统就像是我们的眼睛和耳朵。Prometheus作为云原生时代的监控事实标准以其强大的数据抓取能力和灵活的查询语言PromQL著称。但监控数据的价值最终要体现在“告警”上——当系统出现异常时能及时、准确地将信息送达负责人。很多朋友在部署好Prometheus和Grafana后看着漂亮的图表觉得万事大吉却常常在配置告警邮件发送这一步卡住导致监控系统成了“聋子的耳朵”。我自己在CentOS 7环境下搭建生产级监控告警体系时就深刻体会过这种“最后一公里”的困境。Alertmanager作为Prometheus官方的告警管理组件功能强大但配置项繁多与邮件服务器的集成更是涉及网络、安全、服务配置等多个层面。一个看似简单的“发送邮件”动作背后是Prometheus的告警规则、Alertmanager的路由分组抑制以及邮件服务如Postfix或外部SMTP的协同工作。今天我就以CentOS 7为操作系统基础带你完整走通从Prometheus监控数据产生到Alertmanager处理最终通过邮件成功发送告警的每一个环节并分享那些官方文档里不会写的、我踩过的坑和验证过的技巧。2. 环境准备与核心组件部署在开始配置邮件告警之前一个稳定、互联互通的基础环境是前提。我们假设你已经在CentOS 7上部署了Prometheus Server用于抓取指标现在需要补充Alertmanager和邮件发送能力。2.1 系统基础与网络检查首先确保你的CentOS 7系统处于一个良好的基础状态。使用最小化安装的CentOS 7.9是一个干净的选择。关键的第一步是更新系统并安装必要的工具。# 更新系统到最新稳定状态 sudo yum update -y # 安装后续可能用到的工具如wget, tar, vim, net-tools等 sudo yum install -y wget tar vim net-tools telnet lsof网络连通性检查至关重要特别是出站SMTP端口通常是25但更常用465或587。你需要确认服务器能否访问你计划使用的邮件服务器。例如如果你打算使用腾讯企业邮的SMTP服务smtp.exmail.qq.com:465可以这样测试# 测试网络连通性和端口是否开放 telnet smtp.exmail.qq.com 465 # 或者使用更现代的nc命令如果已安装nmap-ncat nc -zv smtp.exmail.qq.com 465如果连接失败你需要检查服务器的防火墙firewalld或iptables以及可能存在的网络出口安全策略。在测试环境我们可以暂时关闭防火墙并禁用SELinux以排除干扰但在生产环境务必谨慎操作配置精确的规则。# 临时关闭防火墙重启后失效 sudo systemctl stop firewalld sudo systemctl disable firewalld # 临时将SELinux设置为宽松模式 sudo setenforce 0 # 永久修改需要编辑 /etc/selinux/config将SELINUXenforcing改为SELINUXpermissive并重启2.2 Alertmanager的安装与基础配置Alertmanager我们选择通过下载官方二进制包的方式进行安装这样版本可控管理也相对简单。# 创建Prometheus相关组和用户统一管理权限 sudo groupadd --system prometheus sudo useradd -s /sbin/nologin --system -g prometheus prometheus # 下载Alertmanager请替换为最新稳定版链接可从官网获取 cd /tmp wget https://github.com/prometheus/alertmanager/releases/download/v0.25.0/alertmanager-0.25.0.linux-amd64.tar.gz # 解压并移动到标准目录 tar xvf alertmanager-0.25.0.linux-amd64.tar.gz sudo mv alertmanager-0.25.0.linux-amd64 /usr/local/alertmanager # 创建数据目录并更改属主 sudo mkdir -p /data/alertmanager sudo chown -R prometheus:prometheus /usr/local/alertmanager /data/alertmanager接下来是Alertmanager的核心配置文件/usr/local/alertmanager/alertmanager.yml。在配置邮件之前我们先搭建一个最小化的骨架使用null接收器来验证流程。global: resolve_timeout: 5m # 告警恢复后持续多久才标记为已解决 route: group_by: [alertname] # 按告警名称分组 group_wait: 10s # 同一分组内等待多久发送第一批告警 group_interval: 10s # 同一分组内两次发送告警的间隔 repeat_interval: 1h # 如果告警持续未恢复重复发送的间隔 receiver: null-mail # 默认的接收器我们先设为null receivers: - name: null-mail email_configs: [] # 空的邮件配置暂时不发送创建Systemd服务文件/etc/systemd/system/alertmanager.service以便管理服务。[Unit] DescriptionAlertmanager for Prometheus Wantsnetwork-online.target Afternetwork-online.target [Service] Typesimple Userprometheus Groupprometheus ExecStart/usr/local/alertmanager/alertmanager \ --config.file/usr/local/alertmanager/alertmanager.yml \ --storage.path/data/alertmanager \ --web.external-urlhttp://你的服务器IP:9093 # 重要用于生成告警邮件中的链接 Restartalways [Install] WantedBymulti-user.target注意--web.external-url这个参数极其重要却常被忽略。它定义了Alertmanager Web UI的外部访问地址。在告警邮件中Alertmanager会生成指向具体告警详情的链接。如果这里配置的是localhost:9093那么邮件接收者点击链接时将无法访问。务必将其设置为监控团队实际能访问的URL或IP。启动并验证服务sudo systemctl daemon-reload sudo systemctl start alertmanager sudo systemctl enable alertmanager sudo systemctl status alertmanager # 检查端口9093是否监听 sudo netstat -tlnp | grep 9093此时访问http://你的服务器IP:9093应该能看到Alertmanager的Web界面虽然还没有告警信息。2.3 邮件发送代理的选择与配置Postfix vs 外部SMTP这是决定告警能否成功发出的关键环节。你有两个主流选择在监控服务器上自建一个轻量级邮件转发代理如Postfix或者直接让Alertmanager使用外部SMTP服务如公司邮箱、腾讯企业邮、阿里云邮件推送等。方案一配置Postfix作为本地中继如果你的服务器可以访问互联网但公司没有提供现成的SMTP服务或者你想减少对外部服务的依赖配置Postfix将告警邮件转发到外部SMTP是一个常见做法。Postfix在这里扮演一个“中继”角色它从Alertmanager接收邮件然后通过外部SMTP服务器发送出去。安装Postfixsudo yum install -y postfix mailx cyrus-sasl-plain sudo systemctl enable postfix配置Postfix主配置文件/etc/postfix/main.cf 关键修改如下目的是让Postfix监听本地网络并配置通过外部SMTP服务器发信。# 只监听本地回环地址避免成为开放中继 inet_interfaces loopback-only # 设置主机名用于生成邮件头 myhostname alert.yourdomain.com # 设置域名 mydomain yourdomain.com myorigin $mydomain # 指定中继主机即外部SMTP服务器 relayhost [smtp.exmail.qq.com]:465 # 启用SASL认证 smtp_use_tls yes smtp_sasl_auth_enable yes smtp_sasl_password_maps hash:/etc/postfix/sasl_passwd smtp_sasl_security_options noanonymous smtp_tls_CAfile /etc/pki/tls/certs/ca-bundle.crt smtp_tls_wrappermode yes # 对于465端口很重要 smtp_tls_security_level encrypt创建SASL认证文件/etc/postfix/sasl_passwd[smtp.exmail.qq.com]:465 your_emailcompany.com:your_password然后生成hash数据库并设置权限sudo postmap /etc/postfix/sasl_passwd sudo chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db重启Postfix并测试sudo systemctl restart postfix # 使用mailx命令测试发送邮件 echo This is a test email from Postfix. | mailx -s Postfix Test -r senderyourdomain.com recipientexample.com查看邮件发送日志/var/log/maillog确认是否成功。方案二Alertmanager直接使用外部SMTP如果你有稳定、可靠的外部SMTP服务并且希望配置更简洁可以直接在Alertmanager的配置中指定SMTP服务器。这是更推荐的生产环境做法因为它减少了中间环节依赖更清晰。这种方式无需在服务器上安装额外的邮件服务所有配置都集中在alertmanager.yml的global和receiver部分。我们将在下一章详细展开这种配置方式因为它与Alertmanager的集成更紧密也是后续演示的重点。选择建议对于新手或测试环境我建议直接从方案二开始。它逻辑更直接问题排查链路更短。只有当你有特殊需求比如需要在多台服务器上统一邮件出口策略或者外部SMTP服务有复杂的IP白名单限制时才考虑使用Postfix中继。3. Alertmanager邮件告警的深度配置实战现在我们进入核心环节配置Alertmanager使其能够将Prometheus触发的告警通过邮件发送给指定的人员。这里我们采用直接配置外部SMTP的方式。3.1 编写完整的alertmanager.yml配置文件让我们创建一个功能完整的配置文件。假设我们使用腾讯企业邮或其他支持SMTP的服务并计划将告警发送给运维团队。global: resolve_timeout: 5m # 全局SMTP配置所有邮件接收器共享除非在接收器中覆盖 smtp_smarthost: smtp.exmail.qq.com:465 # SMTP服务器地址和端口465是SSL端口 smtp_from: monitor-alertyourcompany.com # 发件人地址必须与认证用户一致或在其域下 smtp_auth_username: monitor-alertyourcompany.com # SMTP认证用户名 smtp_auth_password: YourEmailPassword # SMTP认证密码或授权码 smtp_require_tls: true # 对于465端口通常需要启用TLS/SSL # 告警路由树 route: # 首先按告警标签severity进行分组这样不同严重级别的告警不会混在一起 group_by: [severity, alertname] # 当一个新的告警组创建时等待10秒看是否有同组其他告警触发以便合并发送 group_wait: 10s # 同一分组内两次发送通知的最小间隔 group_interval: 10s # 如果告警持续未解决重复发送通知的间隔 repeat_interval: 12h # 生产环境可以设为4h或6h避免邮件轰炸 # 默认的接收器 receiver: ops-team-email # 子路由可以根据标签匹配将告警路由到不同的接收器 routes: - match: severity: critical receiver: ops-team-email-and-sms # 假设有更紧急的接收器 continue: false # 匹配后不再继续向下路由 - match: severity: warning receiver: ops-team-email continue: false # 定义接收器 receivers: # 运维团队邮件接收器 - name: ops-team-email email_configs: - to: ops-teamyourcompany.com # 收件人可以是多个用逗号分隔 # 邮件主题使用Go模板语法可以嵌入告警标签 subject: {{ if eq .Status \firing\ }}[FIRING:{{ .Alerts | len }}]{{ else }}[RESOLVED]{{ end }} {{ .GroupLabels.alertname }} # 邮件正文模板可选不指定则使用默认模板 # html: {{ template \email.default.html\ . }} # 是否在告警解决时发送恢复邮件 send_resolved: true # 更紧急的接收器示例可集成其他通知方式如钉钉、微信 - name: ops-team-email-and-sms email_configs: - to: oncall-engineeryourcompany.com subject: 【CRITICAL】{{ .GroupLabels.alertname }} # 这里可以添加webhook_configs来调用短信或电话告警接口实操心得一密码安全。明文密码写在配置文件里是极不安全的。在生产环境中务必使用Alertmanager的--web.config.file参数来配置TLS和基础认证或者使用密钥管理服务。一个简单的改进方法是使用环境变量或单独的密钥文件并通过严格的文件权限 (chmod 600) 来控制访问。更高级的做法是使用如HashiCorp Vault等工具动态获取密码。实操心得二group_by的妙用。group_by配置决定了告警如何被分组。合理的分组能避免“告警风暴”。例如按[severity, alertname, instance]分组意味着同一主机、同一严重级别、同一告警名的告警会被合并成一条通知。但如果你按[alertname]分组那么所有服务器的“CPU使用率高”告警可能会被合并导致你无法一眼看出是哪台服务器出了问题。需要根据你的运维习惯来权衡。3.2 配置Prometheus与Alertmanager联动光有Alertmanager还不够需要告诉Prometheus当告警规则触发时将告警信息推送给哪个Alertmanager。编辑Prometheus的配置文件prometheus.yml通常在/usr/local/prometheus/或/etc/prometheus/下。# 在prometheus.yml的全局配置部分之后添加alerting配置 alerting: alertmanagers: - static_configs: - targets: [localhost:9093] # 你的Alertmanager地址和端口 # 可选如果Alertmanager有API路径前缀或需要基础认证 # path_prefix: /alertmanager # basic_auth: # username: admin # password: secret # 定义告警规则文件的位置 rule_files: - /usr/local/prometheus/rules/*.yml # 建议将告警规则放在单独的目录下然后创建你的第一条告警规则文件例如/usr/local/prometheus/rules/node_alerts.yml。这条规则监控节点是否存活。groups: - name: node_alerts rules: - alert: InstanceDown expr: up{jobnode_exporter} 0 # 假设你有一个名为node_exporter的job在抓取节点指标 for: 1m # 持续1分钟条件满足才触发告警避免网络抖动误报 labels: severity: critical # 设置严重级别标签用于Alertmanager路由 team: ops annotations: summary: 实例 {{ $labels.instance }} 下线 description: {{ $labels.instance }} 的节点导出器已经超过1分钟无法访问。\n 当前值: {{ $value }} runbook_url: http://wiki.yourcompany.com/runbook/instance-down重启Prometheus服务以加载新配置和规则sudo systemctl restart prometheus现在你可以通过Prometheus的Web界面默认9090端口的“Alerts”选项卡看到你定义的InstanceDown告警规则。如果up指标为0的条件满足它的状态会从Inactive变为Pending持续for指定的时间后变为Firing。此时Prometheus会将这个Firing状态的告警推送到Alertmanager。3.3 验证告警链路与邮件发送这是最激动人心也最容易出错的环节。我们需要一个可控的方式来触发告警以测试整个链路。停止一个被监控的node_exporter服务如果你有测试服务器可以登录上去systemctl stop node_exporter。或者如果你在用Docker运行exporter就停止那个容器。观察Prometheus Alerts页面等待1分钟后根据规则里的for: 1m你应该看到InstanceDown告警状态变为Firing。观察Alertmanager Web界面打开http://你的AlertmanagerIP:9093。在“Alerts”页面你应该能看到这条告警状态为Active。在“Silences”页面可以设置静默在“Status”页面可以看到它是否已成功发送通知。检查邮件查看配置的收件邮箱ops-teamyourcompany.com应该能收到一封标题类似[FIRING:1] InstanceDown的告警邮件。测试恢复告警重新启动刚才停掉的node_exporter。等待Prometheus抓取间隔通常15-30秒加上for时间告警状态会变回Inactive。Alertmanager会收到恢复信号并发送一封标题为[RESOLVED] InstanceDown的邮件因为我们在配置中设置了send_resolved: true。如果邮件没有收到别慌这是常态。请按以下顺序排查步骤1检查Alertmanager日志。这是第一现场。sudo journalctl -u alertmanager -f或查看其日志文件。重点关注是否有SMTP连接错误、认证失败等信息。常见的错误信息如dial tcp ... connection refused网络/端口不通、535 Error: authentication failed用户名密码错误、501 Syntax error in parameters or arguments发件人地址格式问题。步骤2检查Prometheus日志。确认它是否成功将告警推送到了Alertmanager。sudo journalctl -u prometheus -f。步骤3手动测试SMTP连通性。回到服务器上用命令行工具如swaks或telnet手动测试邮件发送绕过Alertmanager直接验证SMTP服务器是否可用、认证是否通过。步骤4检查垃圾邮件箱。告警邮件可能因为发件服务器信誉、内容包含链接等因素被误判为垃圾邮件。4. 高级配置、模板定制与生产环境调优基础链路打通后我们可以追求更优雅、更实用的告警体验。Alertmanager的强大之处在于其灵活的路由和强大的模板功能。4.1 告警路由的精细化管理上面的配置只是一个简单示例。真实的生产环境可能有多个团队前端、后端、DBA、多种严重级别、多种通知渠道邮件、钉钉、企业微信、短信。这就需要更复杂的路由树。route: receiver: default-blackhole # 默认接收器可以是一个不执行任何操作的空接收器用于捕获未匹配的告警 group_by: [alertname, cluster] group_wait: 30s group_interval: 5m repeat_interval: 4h routes: # 第一级按项目或业务线分 - match_re: project: ^(api-gateway|user-service).* receiver: middleware-team continue: true # 继续匹配子路由 routes: - match: severity: critical receiver: middleware-pagerduty # 关键告警走PagerDuty - match: severity: warning receiver: middleware-email - match: team: dba receiver: dba-team-email - match: severity: page receiver: global-pager这种树状结构让你可以非常精细地控制告警的流向。continue字段是关键它决定匹配当前路由后是否继续尝试匹配其同级或子级路由。4.2 自定义邮件模板让告警信息更清晰默认的邮件模板信息比较基础。我们可以创建自定义模板让邮件内容包含更直观的信息比如当前值、阈值、图表链接等。首先创建一个模板文件例如/usr/local/alertmanager/templates/custom_email.tmpl。{{ define email.custom.html }} !DOCTYPE html html head style body { font-family: Arial, sans-serif; } .alert { border-left: 4px solid; padding: 10px; margin: 10px 0; } .firing { border-color: #e74c3c; background-color: #fdf2f2; } .resolved { border-color: #2ecc71; background-color: #f2fdf6; } .label { display: inline-block; background: #ecf0f1; padding: 2px 6px; border-radius: 3px; margin: 2px; font-size: 0.9em; } /style /head body h2{{ if eq .Status firing }} 告警触发 ({{ .Alerts | len }}条){{ else }}✅ 告警恢复{{ end }}/h2 pstrong分组/strong: {{ .GroupLabels.SortedPairs | join , }}/p pstrong触发时间/strong: {{ .StartsAt.Format 2006-01-02 15:04:05 MST }}/p {{ if eq .Status resolved }}pstrong恢复时间/strong: {{ .EndsAt.Format 2006-01-02 15:04:05 MST }}/p{{ end }} hr {{ range .Alerts }} div classalert {{ if eq .Status firing }}firing{{ else }}resolved{{ end }} h3{{ .Annotations.summary | default .Labels.alertname }}/h3 p{{ .Annotations.description }}/p pstrong严重级别/strong: span classlabel stylebackground-color: {{- if eq .Labels.severity critical -}}#e74c3c {{- else if eq .Labels.severity warning -}}#f39c12 {{- else -}}#95a5a6{{- end -}}; {{ .Labels.severity | default none }} /span/p pstrong实例/strong: {{ .Labels.instance }}/p pstrong当前值/strong: {{ .Annotations.value | default N/A }}/p pstrong相关链接/strong: {{- if .Annotations.runbook_url }} a href{{ .Annotations.runbook_url }}处理手册/a{{ end -}} {{- if .GeneratorURL }} | a href{{ .GeneratorURL }}查看Prometheus表达式/a{{ end -}} {{- if .SilenceURL }} | a href{{ .SilenceURL }}静默此告警/a{{ end -}} /p div strong标签/strong:br {{ range .Labels.SortedPairs }}span classlabel{{ .Name }}: {{ .Value }}/span {{ end }} /div /div {{ end }} /body /html {{ end }}然后在alertmanager.yml中引用这个模板并指定使用它。global: # ... 其他全局配置 ... # 指定模板文件目录 smtp_hello: alertmanager # 注意模板目录路径是相对于Alertmanager二进制文件的或者是绝对路径 # 在顶层添加templates配置 templates: - /usr/local/alertmanager/templates/*.tmpl # 在接收器的email_configs中指定模板 receivers: - name: ops-team-email email_configs: - to: ops-teamyourcompany.com subject: {{ if eq .Status \firing\ }}[FIRING]{{ else }}[RESOLVED]{{ end }} {{ .GroupLabels.alertname }} {{ .GroupLabels.cluster | default \default\ }} html: {{ template \email.custom.html\ . }} # 使用自定义模板 send_resolved: true重启Alertmanager后收到的告警邮件就会是你自定义的HTML格式更加美观和实用。4.3 生产环境关键调优与注意事项时区问题这是最常遇到的坑之一。Prometheus和Alertmanager默认使用UTC时间这会导致邮件中的时间与本地时间对不上。解决方法是在启动参数中指定时区。对于Prometheus在prometheus.yml的global部分或命令行参数中无法直接设置时区。通常需要在告警规则的annotations中使用Go模板函数{{ $value | printf \%.2f\ }}处理数值时间显示依赖接收端如Grafana、Alertmanager模板。对于Alertmanager可以在启动命令中添加环境变量或直接修改系统时区。更推荐在模板中处理时间。在自定义模板中可以使用{{ .StartsAt.Local.Format \2006-01-02 15:04:05\ }}来转换为本地时间。但前提是运行Alertmanager的服务器时区设置正确。确保服务器时区为东八区sudo timedatectl set-timezone Asia/Shanghai。告警分组与抑制Inhibition Rules防止告警风暴。例如当整个集群网络出现问题时所有节点的“节点下线”告警都会触发。此时一条“集群网络故障”的顶级告警应该抑制所有节点级别的告警。这需要在alertmanager.yml中配置inhibit_rules。inhibit_rules: - source_match: # 源告警抑制者 severity: critical alertname: NetworkPartition target_match: # 目标告警被抑制者 severity: critical equal: [cluster] # 当它们在同一个cluster时抑制生效静默Silences对于计划内的维护如服务器重启可以提前在Alertmanager的Web界面创建静默规则在指定时间内屏蔽特定标签的告警避免不必要的告警打扰。高可用部署生产环境建议部署多个Alertmanager实例并以集群模式运行。Prometheus可以配置多个Alertmanager目标实现冗余。Alertmanager集群之间通过Gossip协议同步告警状态和静默信息。监控Alertmanager自身别忘了用Prometheus监控AlertmanagerPrometheus自带了对Alertmanager的监控指标抓取job配置。同时为Alertmanager的关键指标如alertmanager_notifications_failed_total配置告警规则确保这个告警系统本身是健康的。走到这里你的CentOS 7下的Prometheus监控告警邮件发送系统就已经从一个概念变成了一个稳定运行的生产力工具。这套组合拳——Prometheus负责“发现问题”Alertmanager负责“通知人”——是现代化运维体系中不可或缺的一环。配置过程虽然繁琐但每一步的深入理解都会在日后排查故障时给你带来回报。记住一个好的告警系统目标是“告警有意义通知及时信息 actionable可操作”。