在这里插入图片描述

👋 大家好,欢迎来到我的技术博客!
📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。
🎯 本文将围绕Prometheus这个话题展开,希望能为你带来一些启发或实用的参考。
🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!


Prometheus - 告警通知集成:邮件 / 钉钉 / 企业微信 / 短信配置全解 🚨

在现代云原生架构中,监控系统已成为保障服务稳定性的核心组件。Prometheus 作为 CNCF 毕业项目,凭借其强大的时序数据库、灵活的查询语言(PromQL)和活跃的社区生态,已成为事实上的监控标准。然而,仅有数据采集与可视化远远不够——及时、准确、多渠道的告警通知机制,才是将监控价值真正落地的关键。

本文将深入探讨如何将 Prometheus 的告警能力与主流通知渠道(邮件、钉钉、企业微信、短信)无缝集成,提供完整的配置方案、原理剖析及 Java 代码示例,助你构建高可靠、低延迟的告警闭环体系。


一、Prometheus 告警架构解析 🔍

要高效集成外部通知渠道,首先需理解 Prometheus 告警系统的整体架构。它并非由 Prometheus Server 直接发送通知,而是采用 “规则评估 + 告警分发” 的两阶段模型:

1. 持续评估告警规则

2. 分组/抑制/静默

Prometheus Server

Alertmanager

通知渠道

Email

DingTalk

WeCom

SMS Gateway

核心组件说明:

  • Prometheus Server:负责采集指标、执行 PromQL 查询,并根据 alerting rules 判断是否触发告警。一旦条件满足,它会将告警实例(Alert)推送给 Alertmanager。
  • Alertmanager:专为处理告警而设计的独立服务。它接收来自 Prometheus(或其他兼容系统)的告警,执行:
    • 分组(Grouping):将相似告警合并为一条通知,避免信息轰炸。
    • 抑制(Inhibition):当某告警触发时,自动抑制其他相关告警(如主机宕机时抑制其上所有服务告警)。
    • 静默(Silencing):临时屏蔽特定告警,用于维护窗口。
    • 路由(Routing):根据标签将告警分发到不同接收器(Receiver)。
    • 通知(Notification):通过 Webhook、邮件等方式发送告警。

💡 关键点:所有外部通知渠道的集成,本质上都是配置 Alertmanager 的 receivers,使其能调用对应渠道的 API 或协议。


二、环境准备与基础配置 🛠️

在开始具体集成前,请确保以下基础环境已就绪:

  1. Prometheus Server 已安装并运行(版本 ≥ 2.0)。
  2. Alertmanager 已安装并运行(建议与 Prometheus 同版本)。
  3. Prometheus 配置文件 prometheus.yml 中已指定 Alertmanager 地址:
# prometheus.yml
alerting:
  alertmanagers:
    - static_configs:
        - targets: ['localhost:9093']  # Alertmanager 默认端口
  1. Alertmanager 配置文件 alertmanager.yml 结构清晰,我们将在此文件中定义路由和接收器。

📌 提示:Alertmanager 支持热重载配置。修改后可通过 POST /-/reload 触发重载,无需重启服务:

curl -X POST http://localhost:9093/-/reload

三、邮件通知集成 ✉️

邮件是最传统但也最通用的告警渠道,适用于大多数运维场景。Alertmanager 内置 SMTP 支持,配置相对简单。

1. 配置 Alertmanager

编辑 alertmanager.yml,添加邮件相关的 global 设置和 receiver

global:
  smtp_smarthost: 'smtp.exmail.qq.com:465'  # 以腾讯企业邮为例
  smtp_from: 'alert@yourcompany.com'
  smtp_auth_username: 'alert@yourcompany.com'
  smtp_auth_password: 'your_email_password_or_auth_code'
  smtp_require_tls: false  # 若使用465端口(SSL),设为false;587端口(STARTTLS)设为true

route:
  receiver: 'email-notifications'

receivers:
  - name: 'email-notifications'
    email_configs:
      - to: 'ops-team@yourcompany.com'
        send_resolved: true  # 告警恢复时也发送通知

2. 关键参数说明

  • smtp_smarthost:SMTP 服务器地址和端口。常见邮箱端口:
    • QQ 邮箱/企业邮:465 (SSL) 或 587 (STARTTLS)
    • Gmail:465 (SSL) 或 587 (STARTTLS)
    • Outlook/Hotmail:587 (STARTTLS)
  • smtp_require_tls务必根据端口选择。465 通常使用 SSL,不启用 STARTTLS;587 则需启用。
  • send_resolved:强烈建议开启,以便知晓问题已解决。

3. 测试告警规则

创建一个简单的 CPU 使用率过高告警规则 cpu_alerts.yml

groups:
- name: example
  rules:
  - alert: HighCPUUsage
    expr: 100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
    for: 2m
    labels:
      severity: warning
    annotations:
      summary: "High CPU usage on {{ $labels.instance }}"
      description: "{{ $labels.instance }} CPU usage is above 80% (current value: {{ $value }}%)"

prometheus.yml 中引入该规则:

rule_files:
  - "cpu_alerts.yml"

重启 Prometheus 或热重载后,若 CPU 超过阈值,你将收到类似如下邮件:

Subject: [FIRING:1] HighCPUUsage (instance=192.168.1.100:9100 severity=warning)
Body:
[FIRING] High CPU usage on 192.168.1.100:9100
192.168.1.100:9100 CPU usage is above 80% (current value: 85.2%)


四、钉钉通知集成 📱

钉钉作为国内主流办公协同工具,其机器人 Webhook 功能非常适合集成告警通知。Alertmanager 本身不直接支持钉钉,但可通过 Webhook 接收器 + 自定义中间件 实现。

1. 创建钉钉机器人

  1. 在钉钉群聊中点击 “智能群助手”“添加机器人”“自定义”
  2. 设置机器人名称(如 Prometheus Alert),安全设置建议选择 “加签”(更安全)。
  3. 复制生成的 Webhook URLSecret

2. 编写钉钉 Webhook 服务(Java)

由于 Alertmanager 的 Webhook 接收器只发送标准 JSON,而钉钉要求特定格式,我们需要一个轻量级 Java 服务做转换。

Maven 依赖 (pom.xml)
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
</dependencies>
核心代码 (DingTalkWebhookController.java)
import org.springframework.web.bind.annotation.*;
import java.security.MessageDigest;
import java.time.Instant;
import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

@RestController
public class DingTalkWebhookController {

    private static final String DINGTALK_WEBHOOK_URL = "https://oapi.dingtalk.com/robot/send?access_token=YOUR_ACCESS_TOKEN";
    private static final String SECRET = "YOUR_DINGTALK_SECRET"; // 从钉钉机器人获取

    @PostMapping("/dingtalk")
    public String handleAlert(@RequestBody JsonNode alertPayload) {
        try {
            String message = buildMarkdownMessage(alertPayload);
            String signedUrl = getSignedUrl();
            sendToDingTalk(signedUrl, message);
            return "OK";
        } catch (Exception e) {
            e.printStackTrace();
            return "ERROR";
        }
    }

    private String buildMarkdownMessage(JsonNode alerts) {
        StringBuilder sb = new StringBuilder();
        sb.append("## Prometheus 告警通知\n\n");
        
        for (JsonNode alert : alerts.get("alerts")) {
            String status = alert.get("status").asText();
            String summary = alert.get("annotations").has("summary") 
                ? alert.get("annotations").get("summary").asText() 
                : "无摘要";
            String description = alert.get("annotations").has("description") 
                ? alert.get("annotations").get("description").asText() 
                : "无描述";
            String instance = alert.get("labels").has("instance") 
                ? alert.get("labels").get("instance").asText() 
                : "未知实例";
            
            sb.append("- **状态**: ").append(status.toUpperCase()).append("\n");
            sb.append("- **实例**: ").append(instance).append("\n");
            sb.append("- **摘要**: ").append(summary).append("\n");
            sb.append("- **详情**: ").append(description).append("\n\n");
        }
        return "{\"msgtype\": \"markdown\", \"markdown\": {\"title\": \"Prometheus告警\", \"text\": \"" + sb.toString().replace("\"", "\\\"") + "\"}}";
    }

    private String getSignedUrl() throws Exception {
        Long timestamp = Instant.now().toEpochMilli();
        String stringToSign = timestamp + "\n" + SECRET;
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(SECRET.getBytes("UTF-8"), "HmacSHA256"));
        byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
        String sign = Base64.getEncoder().encodeToString(signData);
        return DINGTALK_WEBHOOK_URL + "&timestamp=" + timestamp + "&sign=" + java.net.URLEncoder.encode(sign, "UTF-8");
    }

    private void sendToDingTalk(String url, String message) throws Exception {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost post = new HttpPost(url);
        post.setHeader("Content-Type", "application/json");
        post.setEntity(new StringEntity(message, "UTF-8"));
        HttpResponse response = httpClient.execute(post);
        String responseString = EntityUtils.toString(response.getEntity());
        System.out.println("DingTalk Response: " + responseString);
        httpClient.close();
    }
}

3. 配置 Alertmanager

alertmanager.yml 中添加 Webhook 接收器:

route:
  receiver: 'dingtalk-notifications'

receivers:
  - name: 'dingtalk-notifications'
    webhook_configs:
      - url: 'http://your-java-service-host:8080/dingtalk'
        send_resolved: true

🌐 参考链接钉钉机器人官方文档 提供了详细的签名算法和消息格式说明。

启动 Java 服务后,当告警触发,钉钉群将收到结构化 Markdown 消息,包含状态、实例、摘要等关键信息。


五、企业微信通知集成 💼

企业微信(WeCom)同样提供 Webhook 机器人功能,集成方式与钉钉类似,但消息格式略有不同。

1. 创建企业微信机器人

  1. 在企业微信客户端进入目标群聊 → 点击右上角 “…”“群机器人”“添加机器人”
  2. 设置名称(如 Prometheus Bot),复制 Webhook URL(形如 https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx)。

2. 修改 Java 服务支持企业微信

复用上一节的 Java 项目,新增一个 Controller 方法:

@PostMapping("/wecom")
public String handleWeComAlert(@RequestBody JsonNode alertPayload) {
    try {
        String message = buildWeComMessage(alertPayload);
        sendToWeCom("YOUR_WECOM_WEBHOOK_URL", message);
        return "OK";
    } catch (Exception e) {
        e.printStackTrace();
        return "ERROR";
    }
}

private String buildWeComMessage(JsonNode alerts) {
    ObjectMapper mapper = new ObjectMapper();
    ObjectNode root = mapper.createObjectNode();
    root.put("msgtype", "text");
    
    StringBuilder content = new StringBuilder("【Prometheus告警】\n");
    for (JsonNode alert : alerts.get("alerts")) {
        String status = alert.get("status").asText();
        String summary = alert.get("annotations").has("summary") 
            ? alert.get("annotations").get("summary").asText() 
            : "无摘要";
        content.append("状态: ").append(status.toUpperCase())
               .append("\n摘要: ").append(summary)
               .append("\n-------------------\n");
    }
    root.putObject("text").put("content", content.toString());
    return root.toString();
}

private void sendToWeCom(String url, String message) throws Exception {
    CloseableHttpClient httpClient = HttpClients.createDefault();
    HttpPost post = new HttpPost(url);
    post.setHeader("Content-Type", "application/json");
    post.setEntity(new StringEntity(message, "UTF-8"));
    HttpResponse response = httpClient.execute(post);
    System.out.println("WeCom Response: " + EntityUtils.toString(response.getEntity()));
    httpClient.close();
}

3. 配置 Alertmanager

receivers:
  - name: 'wecom-notifications'
    webhook_configs:
      - url: 'http://your-java-service-host:8080/wecom'
        send_resolved: true

企业微信机器人无需签名(除非启用 IP 白名单),因此实现更简单。消息将以纯文本形式发送到群聊。

🌐 参考链接企业微信机器人文档 详细说明了支持的消息类型和限制。


六、短信通知集成 📞

短信适用于高优先级告警(如核心服务宕机),确保关键人员即使离线也能及时响应。由于短信涉及运营商网关和费用,通常通过第三方云服务(如阿里云、腾讯云)实现。

1. 选择短信服务商

阿里云短信服务 为例(需开通服务并申请签名、模板):

  • 签名:如 [YourCompany]
  • 模板:如 【告警】实例${instance}发生${alertname},当前值${value}。
  • 获取 AccessKey IDAccessKey Secret

2. Java 短信发送服务

使用阿里云 SDK 发送短信:

Maven 依赖
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-core</artifactId>
    <version>4.6.3</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
    <version>2.2.1</version>
</dependency>
核心代码
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.fasterxml.jackson.databind.JsonNode;

@RestController
public class SmsWebhookController {

    private static final String ACCESS_KEY_ID = "YOUR_ACCESS_KEY_ID";
    private static final String ACCESS_KEY_SECRET = "YOUR_ACCESS_KEY_SECRET";
    private static final String SIGN_NAME = "YourCompany";
    private static final String TEMPLATE_CODE = "SMS_XXXXXX"; // 你的模板CODE
    private static final String[] PHONE_NUMBERS = {"13800138000", "13900139000"}; // 告警接收人手机号

    @PostMapping("/sms")
    public String handleSmsAlert(@RequestBody JsonNode alertPayload) {
        try {
            for (JsonNode alert : alertPayload.get("alerts")) {
                if ("firing".equals(alert.get("status").asText())) { // 仅对触发中的告警发短信
                    sendSmsAlert(alert);
                }
            }
            return "OK";
        } catch (Exception e) {
            e.printStackTrace();
            return "ERROR";
        }
    }

    private void sendSmsAlert(JsonNode alert) throws ClientException {
        DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        IAcsClient client = new DefaultAcsClient(profile);

        SendSmsRequest request = new SendSmsRequest();
        request.setPhoneNumbers(String.join(",", PHONE_NUMBERS));
        request.setSignName(SIGN_NAME);
        request.setTemplateCode(TEMPLATE_CODE);
        
        // 构建模板参数
        String instance = alert.get("labels").has("instance") ? alert.get("labels").get("instance").asText() : "N/A";
        String alertName = alert.get("labels").has("alertname") ? alert.get("labels").get("alertname").asText() : "Unknown";
        String value = alert.get("value").asText();
        
        request.setTemplateParam(String.format("{\"instance\":\"%s\",\"alertname\":\"%s\",\"value\":\"%s\"}", instance, alertName, value));

        SendSmsResponse response = client.getAcsResponse(request);
        System.out.println("SMS Response: " + response.getMessage());
    }
}

3. 配置 Alertmanager

receivers:
  - name: 'sms-notifications'
    webhook_configs:
      - url: 'http://your-java-service-host:8080/sms'
        send_resolved: false  # 短信通常不发送恢复通知,避免骚扰

⚠️ 注意

  • 短信费用较高,建议仅用于 severity: critical 的告警。
  • route 中通过 matchmatch_re 限定仅高优先级告警走短信通道。
  • 阿里云短信有发送频率限制,需在代码中加入限流逻辑(如 Guava RateLimiter)。

七、高级路由与多渠道协同 🧩

实际生产环境中,往往需要根据告警严重程度、服务类型等维度,将告警分发到不同渠道。Alertmanager 的 路由树(Routing Tree) 机制完美支持这一需求。

示例:分级告警策略

route:
  receiver: 'default-email'  # 默认接收器
  group_by: ['alertname', 'cluster', 'service']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 3h

  # 子路由:按严重程度分发
  routes:
    - match:
        severity: critical
      receiver: 'critical-sms-and-dingtalk'
      repeat_interval: 15m  # 高优先级告警重复频率更高

    - match:
        severity: warning
      receiver: 'warning-email-and-wecom'

receivers:
  - name: 'default-email'
    email_configs:
      - to: 'fallback@yourcompany.com'

  - name: 'critical-sms-and-dingtalk'
    webhook_configs:
      - url: 'http://alert-service:8080/sms'
      - url: 'http://alert-service:8080/dingtalk'

  - name: 'warning-email-and-wecom'
    email_configs:
      - to: 'warnings@yourcompany.com'
    webhook_configs:
      - url: 'http://alert-service:8080/wecom'

critical

warning

other

Incoming Alert

severity?

SMS + DingTalk

Email + WeCom

Default Email

抑制规则示例

当主机宕机时,抑制其上所有服务的告警:

inhibit_rules:
  - source_match:
      alertname: HostDown
    target_match:
      severity: warning
    equal: ['instance']

八、告警模板定制化 🎨

默认的告警消息可能信息不足或格式混乱。Alertmanager 支持使用 Go 模板语法自定义通知内容。

邮件模板示例 (email.tmpl)

{{ define "email.title" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }}{{ end }}

{{ define "email.html" }}
<h1>Prometheus Alert</h1>
<ul>
{{ range .Alerts }}
  <li>
    <strong>{{ .Labels.alertname }}</strong> ({{ .Labels.severity | upper }})
    <br>Instance: {{ .Labels.instance }}
    <br>Description: {{ .Annotations.description }}
    <br>Value: {{ .Value }}
    <br><a href="{{ .GeneratorURL }}">View in Prometheus</a>
  </li>
{{ end }}
</ul>
{{ end }}

alertmanager.yml 中引用:

templates:
  - '/etc/alertmanager/templates/*.tmpl'

receivers:
  - name: 'email-notifications'
    email_configs:
      - to: 'ops@yourcompany.com'
        html: '{{ template "email.html" . }}'
        title: '{{ template "email.title" . }}'

🌐 参考链接Alertmanager 官方模板文档 提供了完整的模板变量和函数列表。


九、安全与最佳实践 🔒

1. 敏感信息保护

  • 不要在配置文件中明文存储密码、密钥。使用:
    • Vault:HashiCorp Vault 集成
    • Kubernetes Secrets:若部署在 K8s
    • 环境变量:Alertmanager 支持 $ENV_VAR 引用
global:
  smtp_auth_password: '$EMAIL_PASSWORD'  # 从环境变量读取

2. 网络安全

  • 限制 Alertmanager Web UI 访问(默认端口 9093)
  • Webhook 服务应部署在内网,或通过 API 网关增加认证
  • 钉钉/企业微信机器人启用 IP 白名单

3. 告警疲劳预防

  • 合理设置 for 持续时间:避免瞬时抖动触发告警
  • 启用分组group_by 减少通知数量
  • 设置静默期repeat_interval 避免重复轰炸
  • 定期 Review 规则:删除无效或低价值告警

十、故障排查指南 🛠️

常见问题及解决方案

问题现象 可能原因 解决方案
邮件未收到 SMTP 配置错误、防火墙阻断 检查 smtp_smarthost 端口、smtp_require_tls 设置;使用 telnet 测试连通性
钉钉无消息 签名错误、URL 过期 检查 Java 服务日志;确认 Secret 与钉钉后台一致;测试 Webhook URL
短信发送失败 模板未审核、余额不足 登录阿里云控制台查看审核状态和余额;检查模板参数是否匹配
告警未触发 PromQL 表达式错误、规则未加载 在 Prometheus UI 执行 expr 验证;检查 rule_files 路径

调试技巧

  1. 查看 Alertmanager 日志
    docker logs alertmanager-container
    
  2. 使用 Alertmanager API 测试
    # 模拟发送告警
    curl -H "Content-Type: application/json" -d '[{"labels":{"alertname":"TestAlert"}}]' http://localhost:9093/api/v1/alerts
    
  3. 启用 Webhook 服务调试日志:在 Java 代码中打印接收到的原始 JSON。

结语 🌈

通过本文的详细讲解,你已掌握 Prometheus 与邮件、钉钉、企业微信、短信四大主流通知渠道的集成方法。从基础配置到高级路由,从安全加固到故障排查,这套告警体系将为你构建坚如磐石的监控防线。

记住,告警不是目的,快速恢复才是。合理的告警策略应聚焦于可行动的事件,避免噪音干扰。持续优化你的告警规则和通知流程,让每一次告警都成为提升系统稳定性的契机。

🚀 行动建议:立即检查你的 Alertmanager 配置,尝试为关键服务添加多渠道通知,并设置分级路由策略。让告警真正成为你的“数字哨兵”!


🙌 感谢你读到这里!
🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。
💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友!
💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿
🔔 关注我,不错过下一篇干货!我们下期再见!✨

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐