一、问题背景与痛点拆解

做过云原生监控的人大概率都踩过这个坑:好不容易把Prometheus、Alertmanager搭好,又跟Slack做了集成,结果告警消息发过来,要么是一堆乱码,要么是格式乱成一锅粥——本该换行的内容挤成一团,关键的告警级别、故障时间没突出显示,甚至连故障描述都缺斤短两,最后整个Slack频道变成了“垃圾消息场”,真正需要紧急处理的告警反而被淹没了。

我之前就碰到过一次:凌晨三点收到Slack告警,结果消息里的故障时间、影响服务全堆在一行,我翻了三分钟才找到核心信息,差点耽误了故障排查。后来花了一周时间啃官方文档、翻社区issue,才把这个问题彻底解决,今天就把踩过的坑、摸透的逻辑全说清楚。

先明确下核心问题的本质:Alertmanager和Slack的集成,本质是“Alertmanager把自己的告警数据,转换成Slack能识别的消息格式”——Alertmanager的告警结构是自己定义的(比如有labels、annotations这些字段),而Slack的消息有自己的规范(比如需要用Markdown、支持分块的Block Kit),如果两者的转换逻辑没对齐,就会出现格式错乱。

二、问题排查的核心逻辑

碰到格式错乱的问题,别上来就改配置,得先搞清楚“乱在哪”“为什么乱”,不然改了半天可能还是错的。这里给大家一套通用的排查步骤,我自己用了不下十次,屡试不爽。

2.1 先定位乱码的来源

首先要搞清楚:乱码是Alertmanager发的时候就错了,还是Slack收的时候解析错了?

怎么验证?很简单,用Alertmanager的调试功能:Alertmanager有个API接口,可以查看它生成的告警内容。具体操作是,先找到Alertmanager的容器(如果是K8s部署的,就用kubectl exec进去),然后执行这个命令:

# 进入Alertmanager容器(替换成自己的命名空间和Pod名)
kubectl exec -it alertmanager-xxxx-xxxx -n monitoring -- bash
# 调用API查看当前活跃的告警,输出是JSON格式
curl http://localhost:9093/api/v1/alerts

把输出的JSON复制出来,重点看两个字段:

  • labels:告警的标签(比如告警级别、服务名、故障类型)
  • annotations:告警的描述(比如故障详情、排查步骤)

如果这里的内容本身是正常的(没有乱码、格式正常),那说明问题出在Alertmanager和Slack之间的转换逻辑;如果这里的内容就乱了,那得先解决Alertmanager本身的告警生成问题。

2.2 再确认Slack的消息规范

Slack的消息有两种主要格式:

  1. 旧版的纯文本+基础Markdown:比如用*加粗、\n换行,这种格式比较简单,但功能有限。
  2. 新版的Block Kit:是Slack推荐的格式,支持分块、按钮、下拉框等复杂功能,格式是JSON数组,每个块对应消息的一部分(比如标题块、内容块、动作块)。

Alertmanager的Slack集成,默认是用旧版格式,但如果配置错了,比如用了Block Kit的格式但参数不对,或者用了旧版格式但没转义特殊字符,就会出问题。

三、常见的格式错乱场景与解决方案

我把碰到过的、社区里常见的格式错乱场景,整理成了三类,每类都给了具体的解决方案和完整的配置示例,大家可以对照着改。

3.1 场景一:告警内容挤成一团,没有换行

这是最常见的场景,比如把“告警级别:严重\n故障时间:2024-05-20 12:00:00\n影响服务:订单服务”挤成了一行,看起来特别费劲。

原因分析

Alertmanager默认生成的告警内容,是把所有字段拼在一起,没有主动加换行符;或者加了换行符,但转义错了(比如把\n写成了\\n)。

解决方案

在Alertmanager的Slack配置里,主动给每个字段加换行符,并且确保转义正确。这里给一个完整的配置示例,用的是Alertmanager的模板功能(模板是Alertmanager用来定制消息格式的核心工具,相当于给消息“排版”)。

首先,我们要写一个模板文件,比如叫slack.tmpl,内容如下:

# 模板的名字是slack.message,后面会用到
{{ define "slack.message" }}
# 告警标题,用Slack的Markdown加粗
*{{ .CommonLabels.severity }} 告警*
# 告警内容,每个字段加换行符
告警服务:{{ .CommonLabels.service }}
故障时间:{{ .StartsAt.Format "2006-01-02 15:04:05" }}
故障详情:{{ .CommonAnnotations.description }}
# 加个空行,区分不同告警
{{ end }}

这个模板的逻辑很简单:

  • *把告警级别加粗(Slack的Markdown支持)
  • 每个字段后面加\n(Go模板里的换行符,直接写在模板里就行)
  • Format函数把故障时间转成人类能看懂的格式(默认的时间是ISO格式,比如2024-05-20T12:00:00Z,转成2024-05-20 12:00:00更直观)

然后,把这个模板配置到Alertmanager的主配置文件(alertmanager.yml)里,具体的配置如下:

global:
  resolve_timeout: 5m
route:
  group_by: ['alertname']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: 'slack-notifications'
receivers:
- name: 'slack-notifications'
  slack_configs:
  - api_url: 'https://hooks.slack.com/services/XXXX/XXXX/XXXX' # 替换成自己的Slack Webhook地址
    channel: '#monitoring-alerts' # 替换成自己的Slack频道
    text: '{{ template "slack.message" . }}' # 调用我们写的模板
    title: '{{ .CommonLabels.alertname }}' # 告警的标题
    # 配置Slack的Markdown支持,确保格式生效
    link_names: true
    icon_url: 'https://www.prometheus.io/assets/prometheus_logo_grey.svg'

这里要注意两个关键点:

  1. 模板里的字段名(比如CommonLabels.severityCommonAnnotations.description)必须和Alertmanager生成的告警字段名一致,不然会显示空值。
  2. link_names: true这个参数必须加,不然Slack的Markdown格式(比如加粗)不会生效。

配置完之后,重启Alertmanager,然后手动触发一个告警(比如把某个服务的端口关了),再看Slack的消息,就会变成清晰的换行格式了。

3.2 场景二:特殊字符导致的乱码

比如告警内容里有&<>这些特殊字符,Slack会把它们当成HTML标签来解析,导致内容乱码。举个例子,比如故障详情是“服务调用依赖失败”,Slack会把<mysql>当成HTML标签,结果显示成“服务调用依赖失败”,把<mysql>吞掉了。

原因分析

Slack的消息解析器会把&<>这些字符当成特殊字符来处理,如果不转义,就会出现解析错误。

解决方案

在Alertmanager的模板里,用Go模板的html函数把特殊字符转义成HTML实体。比如把<转成&lt;,把>转成&gt;,把&转成&amp;

具体的模板修改如下,只需要修改故障详情那一行:

{{ define "slack.message" }}
*{{ .CommonLabels.severity }} 告警*
告警服务:{{ .CommonLabels.service }}
故障时间:{{ .StartsAt.Format "2006-01-02 15:04:05" }}
# 用html函数转义特殊字符
故障详情:{{ html .CommonAnnotations.description }}
{{ end }}

这里的html函数是Go模板内置的,专门用来转义HTML特殊字符,转义之后,Slack就会把这些字符当成普通文本显示,不会再乱码了。

3.3 场景三:Block Kit格式配置错误导致的乱码

有些同学为了让告警更美观,想用Slack的Block Kit格式(比如加个颜色边框、按钮),但配置错了,导致消息乱码。比如我之前碰到过一个例子:把Block Kit的配置写成了纯文本,结果Slack收到的是一堆JSON字符串,根本看不懂。

原因分析

Block Kit是JSON格式的数组,每个块的格式有严格的要求(比如必须有type字段,text字段必须包含typetext),如果格式错了,Slack会解析失败,把整个内容当成纯文本显示。

解决方案

用Alertmanager的模板生成符合Slack规范的Block Kit格式,这里给一个完整的示例,这个示例会生成一个带颜色边框、标题、内容的美观告警:

首先,模板文件slack-blocks.tmpl的内容:

{{ define "slack.blocks" }}
# 生成一个JSON数组,用\n分隔每个块
[
  # 第一个块:标题块,带颜色边框(红色表示严重,黄色表示警告)
  {
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": "*{{ .CommonLabels.severity }} 告警:{{ .CommonLabels.alertname }}*"
    },
    "color": "{{ if eq .CommonLabels.severity "critical" }}#ff0000{{ else if eq .CommonLabels.severity "warning" }}#ffcc00{{ else }}#36a64f{{ end }}"
  },
  # 第二个块:内容块,显示告警详情
  {
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": "告警服务:{{ .CommonLabels.service }}\n故障时间:{{ .StartsAt.Format "2006-01-02 15:04:05" }}\n故障详情:{{ html .CommonAnnotations.description }}"
    }
  },
  # 第三个块:动作块,加一个“查看监控”的按钮
  {
    "type": "actions",
    "elements": [
      {
        "type": "button",
        "text": {
          "type": "plain_text",
          "text": "查看监控"
        },
        "url": "https://grafana.example.com/d/xxxx" # 替换成自己的Grafana地址
      }
    ]
  }
]
{{ end }}

这个模板的逻辑:

  • eq函数判断告警级别,给不同级别的告警加不同的颜色边框(严重用红色,警告用黄色,普通用绿色)
  • mrkdwn类型的text字段,支持Slack的Markdown格式
  • 加了一个“查看监控”的按钮,点击可以跳转到Grafana的对应仪表盘

然后,把这个模板配置到alertmanager.yml里,注意这里要用blocks字段,而不是text字段:

receivers:
- name: 'slack-notifications'
  slack_configs:
  - api_url: 'https://hooks.slack.com/services/XXXX/XXXX/XXXX'
    channel: '#monitoring-alerts'
    blocks: '{{ template "slack.blocks" . }}' # 调用Block Kit模板
    # 配置Slack的Markdown支持
    link_names: true
    icon_url: 'https://www.prometheus.io/assets/prometheus_logo_grey.svg'

配置完重启Alertmanager,触发告警,Slack收到的消息就会是带颜色、带按钮的美观格式,不会再乱码了。

四、场景与技术的详细分析

4.1 适用场景

这个方案适合所有用Prometheus+Alertmanager做监控,并且需要把告警发送到Slack的场景,不管是个人项目、小型团队还是大型企业,都适用。尤其是以下几种场景特别需要:

  • 团队用Slack做日常沟通和协作,需要把告警集成到Slack里。
  • 告警内容比较复杂,需要分块、加按钮,方便快速处理。
  • 告警级别多,需要用颜色区分不同级别的告警。

4.2 技术的优缺点

优点

  1. 定制化程度高:可以用模板完全控制告警的格式、内容、颜色、按钮等,满足不同团队的需求。
  2. 兼容性好:既支持旧版的纯文本格式,也支持新版的Block Kit格式,适配Slack的所有版本。
  3. 维护方便:模板和主配置文件分离,修改格式只需要改模板,不用改主配置,方便维护。

缺点

  1. 学习成本:需要了解Alertmanager的模板语法(Go模板)和Slack的消息规范(Block Kit),对新手来说有一定的学习成本。
  2. 调试麻烦:模板里的语法错误(比如括号不匹配、字段名错误),Alertmanager不会直接报错,只会导致告警发不出去或者格式错乱,调试起来比较麻烦。

4.3 注意事项

  1. 字段名要一致:模板里的字段名(比如CommonLabels.severity)必须和Alertmanager生成的告警字段名完全一致,包括大小写,不然会显示空值。
  2. 转义特殊字符:只要告警内容里有&<>这些特殊字符,一定要用html函数转义,不然会乱码。
  3. 测试模板:修改模板之后,一定要手动触发告警测试,确认格式正确,不要直接上线。
  4. 权限控制:Slack的Webhook地址要保密,不要泄露给外部,不然别人可以用这个地址给你的Slack频道发消息。

五、总结

Alertmanager和Slack集成的格式错乱问题,本质是“两个系统的消息格式不匹配”,解决的核心思路是:先定位乱码的来源,再根据乱码的类型,用模板定制告警的格式,确保符合Slack的规范。

我整理的三个场景的解决方案,覆盖了90%以上的常见问题,大家可以对照着改。只要掌握了模板的用法,不仅能解决格式错乱的问题,还能把告警做得更美观、更实用,比如加按钮、加颜色、加监控链接,让告警真正成为团队处理故障的好帮手,而不是“垃圾消息”。