Zabbix和Prometheus告警接入声光语音终端实战
2026/9/7 10:20:01 网站建设 项目流程

上周三凌晨两点半,我正被手机上一连串告警推送吵得半睡半醒,翻来覆去点开看,才发现最要命的那条“数据库主库连接数打满”已经被其他消息淹没在十分钟前了。邮件、短信、微信群其实一个都没漏发,问题出在“人”身上——手机不在手上、开着勿扰、或者被消息风暴炸到麻木。

也就是从那次之后,我开始认真琢磨怎么把告警从“数字通道”拉到“物理世界”来。花了一个下午,我把手头的 Zabbix 和 Prometheus 告警通过 HTTP API 接到了工位旁边的博灵语音通知终端上,实现了声光报警 + TTS 语音播报。这篇内容我就把当时的完整做法拆一遍,包括设备侧怎么调、Zabbix 侧怎么配、Alertmanager 侧怎么对接,以及我在里面踩过的几个非常典型的坑。

1. 告警送达的最后一公里:为什么说手机不是终点

1.1 手机告警的三个盲区

很多做运维的朋友觉得告警能推到手机就已经够了,但真实值班场景里,手机这个通道远没有我们想的那么可靠。我总结下来有三个特别典型的盲区:

第一个是勿扰模式。晚上睡觉大家都开着勿扰,重要告警推送到手机上的结果就是折叠进通知中心,第二天早上才看到,故障早就发生了。第二个是消息风暴。一次故障往往同时触发几十条关联告警,微信群、钉钉群、邮件列表一起刷,真正关键的“P0”信息反而被淹没在一堆“连接数升高”“响应时间变长”的次要告警里。第三个是信号盲区。机房、楼层电梯间、地下室这些地方,手机网络本身就差,你在现场处理故障时想掏出手机看一眼最新告警,结果界面一直在转圈。

这三个问题说白了就是:手机告警的触达率极度依赖“人愿意看手机 + 手机有网络 + 通知没被折叠”。任何一个环节掉了链子,告警就等于白发了。

1.2 声光终端的定位:物理存在感 + 语音播报

博灵语音通知终端的形态很像一个小型工控盒,自带喇叭、LED 灯带,有的型号还支持状态屏。把它放在值班室桌面或者机房门口,它干的事情很简单——通过 HTTP API 收到告警请求后,用内置 TTS 引擎把文本合成语音播报出来,同时控制灯光的颜色、闪烁频率和蜂鸣次数。

这玩意儿和手机推送最大的区别在于“物理存在感”。再响的手机铃声你也有可能下意识划掉,但房间里一个红色灯光在闪、喇叭在喊“严重告警,请尽快处理”,这种刺激你没法忽视。尤其是后半夜值班,手机放在桌上充着电,你躺着半梦半醒,声音一响整个人直接就清醒了。

TTS 播报还有一个好处是信息密度高。不用解锁手机去看推送内容,语音直接把告警名称、影响位置、故障级别念给你听,你闭着眼都知道是哪个系统出了问题。对于需要同时盯监控大屏和处理故障的人,这种“耳朵也能看告警”的方式真的很省事。

1.3 整体数据流与方案选型逻辑

这次方案的核心数据流是这样的:

Zabbix / Prometheus 产生告警 ↓ Zabbix 告警媒介脚本 / Alertmanager Webhook ↓ HTTP POST 请求 ↓ 博灵语音通知终端 ↓ 声光报警 + TTS 语音播报

为什么选 HTTP API 而不是其他方式?我对比了几种:

对接方式实时性可控性实施成本结论
邮件推送要配置邮箱解析,延迟不可控
Modbus/SNMP 直接控制 Io需要工业网关,协议复杂
HTTP API监控系统原生支持,最合适

HTTP API 对 Zabbix 和 Prometheus 都很友好。Zabbix 原生支持脚本媒介,脚本里 curl 一下就能发请求;Prometheus 生态里有 Alertmanager 的 webhook,也能直接 POST JSON。而且 HTTP 接口可以在请求体里带上灯光颜色、音量、播报次数这些参数,分级告警的差异化联动就很容易实现了。

还有人会问,为什么不用终端自带的邮件接收功能或者短信网关?实话实说,终端的 HTTP 接口是往监控系统这边靠的,邮件服务会出现解析失败、延迟、垃圾邮件误判等问题,短信网关还要额外买服务。HTTP API 是最短路径,链路越短越可靠。

2. 博灵终端侧的准备:先把硬件调通再联监控

2.1 终端上线与网络配置

拿到博灵终端之后,第一步先把设备本身调通,别一上来就折腾监控侧,不然出了问题你都不知道该查哪边。

我这边的情况是终端通过网线接入办公网,接通电源后,终端自带的小屏幕上会显示获取到的 IP 地址。你用浏览器访问这个 IP,进入终端的管理后台。如果设备支持屏幕操作,也可以直接在面板上配置静态 IP。这里有一个建议:最好给终端配置一个固定 IP,不要用 DHCP,否则 IP 变了,监控侧脚本里写的地址也要跟着改,值班室没有显示器的时候排查起来相当痛苦。

管理后台里需要重点设置三个东西:

  • 管理员密码:出厂默认密码一定要改,终端挂在办公网里,被同事拿着 curl 瞎打一发,半夜播报一条“测试告警”可不是什么好体验。
  • NTP 时间同步:终端日志和布防时段都依赖系统时间,时间不对,定时静默就会乱套。
  • 对外 HTTP API 开关:有的型号默认关闭,需要在后台打开,并生成一个访问 Token。这个 Token 就相当于终端的钥匙,监控侧调用的时候要用。

终端摆放位置也有讲究。如果是放值班室,尽量放在工位对角线的墙面上,LED 灯带的余光能被眼睛余光扫到就行,不要直射眼睛。喇叭音量你先用管理后台的测试功能放一段,调到“能吵醒你但不至于炸耳”的程度,我一般放在中档偏上。

2.2 用 curl 实测 HTTP API 的两种调用方式

设备管理后台里通常会写接口路径和调用示例。我手头这台设备的对外接口是/api/v1/play,不同的固件版本可能不一样,建议你们以自己设备的官方文档为准,但调用逻辑是通用的。

我推荐统一用 POST + JSON 的方式,因为请求体里可以携带更丰富的参数,也不会像 GET 一样遇到中文 URL 编码的坑。可以用 curl 直接测:

curl -X POST "http://<终端IP>:<端口>/api/v1/play" \ -H "Content-Type: application/json" \ -H "X-Auth-Token: <你的Token>" \ -d '{ "text": "这是一条测试告警,请检查终端声音和灯光", "light": "red", "volume": 8, "times": 2, "tts": { "voice": "female", "speed": 1.0 } }'

正常的话,终端应该立刻开始播报语音,并亮起红色灯光。如果没反应,先看管理后台的请求日志,日志里会记录收到请求的时间、请求体内容以及返回的响应状态。

还有一种方式是 GET 请求:

curl -G "http://<终端IP>:<端口>/api/v1/play" \ --data-urlencode "text=这是一条测试告警" \ --data-urlencode "light=red" \ -H "X-Auth-Token: <你的Token>"

GET 方式胜在简单,但特殊字符转义麻烦。我的建议很直接:能用 POST 就别用 GET,减少一类编码问题。

关于灯光参数,不同型号可能支持不同的颜色枚举值,常见的有 red、yellow、blue、green。如果你传了一个设备不支持的枚举,设备会返回 500 错误,所以先看文档里支持哪些颜色,做好映射再上线。

2.3 我报错最多的 500 internal server error 排查

很多朋友在对接博灵终端时会遇到一个很典型的报错:

request returned 500 internal server error for api route and version http://...

我第一次遇到这个错也懵了一下,排查了几轮之后发现,绝大多数 500 不是设备内部故障,而是请求本身有问题。我按概率排个序:

  1. URL 路径写错了,或者漏了 API 版本号。比如设备文档写的是/api/v1/play,你写成了/api/play,或者端口从 8080 写成了 80。
  2. Token 没传对。有些型号对鉴权失败的响应也是 500,不会像标准 Web 服务那样返回 401/403,所以要先确认 Token 有没有复制错、Header 名字对不对。
  3. JSON 字段不在设备支持列表里。单词拼错、参数类型不对、灯光枚举值不支持,都会导致 500。
  4. 网络不通或端口被防火墙拦了,请求被重置,表现也可能被误判成 500。

排查的时候千万别对着报错硬看,用curl -v看完整交互,再打开终端管理后台的日志,两边一对比基本就能定位。我把几种常见情况和排查思路放进 5.3 的速查表里了,到时候对号入座就行。

3. Zabbix 告警接入实战:一个脚本加一次动作配置

3.1 准备告警媒介脚本

Zabbix 对接第三方告警通道最通用的方式,就是“脚本媒介”。原理很简单:告警触发时,Zabbix Server 会调用一个外部脚本,把发送目标告警主题告警内容三个参数传给脚本,脚本再通过 curl 把它们转成 HTTP 请求打到终端。

先找一下 Zabbix Server 的 AlertScriptsPath 目录,一般在配置/etc/zabbix/zabbix_server.conf里:

grep AlertScriptsPath /etc/zabbix/zabbix_server.conf # 常见路径 /usr/lib/zabbix/alertscripts 或 /etc/zabbix/alert.d

在这个目录下新建脚本boling_alert.sh并授权:

chown zabbix:zabbix /usr/lib/zabbix/alertscripts/boling_alert.sh chmod +x /usr/lib/zabbix/alertscripts/boling_alert.sh

脚本内容我用的版本如下,基于系统自带的 Bash 和 Python3,不需要额外装依赖:

#!/bin/bash # 博灵语音通知终端 - Zabbix 告警媒介脚本 # 标准输入传入三行:SendTo / Subject / Message IFS= read -r sendto IFS= read -r subject IFS= read -r message token="<你的终端Token>" severity="$subject" light="red" volume="8" times="1" case "$severity" in *Disaster*|*High*) light="red" volume="10" times="3" ;; *Warning*) light="yellow" volume="7" times="2" ;; esac # TTS 播报只取主题,避免念一长串详情 speak_text="${subject}" # 用 Python3 构造 JSON,中文不会被截断或转义错 payload=$(python3 -c ' import json, sys print(json.dumps({ "text": sys.argv[1], "light": sys.argv[2], "volume": int(sys.argv[3]), "times": int(sys.argv[4]) }, ensure_ascii=False))' "$speak_text" "$light" "$volume" "$times") curl -s -X POST "http://${sendto}/api/v1/play" \ -H "Content-Type: application/json" \ -H "X-Auth-Token: ${token}" \ -d "$payload" \ || echo "$(date '+%F %T') 博灵终端调用失败: $sendto $subject" >> /var/log/zabbix/boling_alert.log

这里有个细节要提醒:Zabbix 的脚本媒介是从标准输入传参,和普通 shell 脚本的$1$2不一样,你如果写成用位置参数接收,会发现收不到任何内容。我先用IFS= read -r把三行分别读进来,这样最稳妥。

脚本里我只取了subject作为播报文本,message没有用。原因是 Zabbix 默认的告警消息非常长,直接 TTS 会念得头晕。播报文本的第一原则是“短”,详情留着看监控面板就好。

服务器的 curl 和 Python3 都确认一下装了没有,一般 Zabbix Server 上都有。没 curl 就apt install curl,没 Py3 就apt install python3,都是顺手的事。

3.2 创建媒体类型并绑定用户

脚本准备好之后,进入 Zabbix Web 管理界面,按这个路径配置:

  1. 左侧菜单:报警媒介类型->创建媒体类型
  2. 类型选“脚本”,脚本名称填boling_alert.sh
  3. 参数位置保持默认的三个框:{ALERT.SENDTO}{ALERT.SUBJECT}{ALERT.MESSAGE}。如果界面只显示一个参数框,可以手动添加三个,依次填入上面的宏。
  4. 保存后,再进入用户-> 找到你的值班用户 ->报警媒介->添加,媒介类型选择“博灵语音通知终端”,收件人填终端的IP:端口,比如192.168.1.50:8080

千万注意这个收件人字段,它不是填手机号或邮箱,而是填终端地址。脚本收到的sendto变量就是这个值,拼 URL 的时候直接拼接在http://后面。

这里我推荐给值班组单独建一个 Zabbix 用户,比如叫ops-alert,然后把这个用户关联终端媒介。这样以后要调整声光策略,只需要改这一个用户,不需要动每个管理员个人账号的配置。

3.3 设置告警动作与分级模板

Zabbix 侧最后一步是配置动作,也就是“什么情况下,把告警发给谁”。

进入配置->动作->Trigger actions->创建动作

  • 名称:博灵终端声光告警
  • 条件:故障级别大于等于“警告”。我的习惯是只把 Warn 及以上打到终端,信息级别的低级告警量太大,全部打到终端会变成噪音。
  • 操作步骤:发送消息给用户ops-alert,媒介选择“博灵语音通知终端”。
  • 消息主题模板我填的是:[{TRIGGER.SEVERITY}] {TRIGGER.NAME}
  • 恢复操作同样配置一遍,这样告警恢复后终端会播报一条“恢复”消息,灯变绿色,值班员能直观看到故障闭环。

关于时间段布防:配置->动作里可以对步骤设置时间段,白天和夜间的播报策略可以拆成两个动作。比如晚上 22 点到早上 8 点,只允许“灾难”级别的告警触发终端播报,其余级别静默。这样既不会被低频告警吵醒,又保证最严重的问题能在深夜叫醒人。

Zabbix 侧配置完之后,可以手动在监控项上执行一次“测试”,或者直接在终端上 curl 一发,验证整条链路是否通了。如果你发现终端收到了请求但没声音,先检查脚本里的参数,特别是light值是否合法、音量是否设置成了 1。

4. Prometheus 接入实战:Alertmanager webhook 与中转服务

4.1 Alertmanager 的 webhook 配置

Prometheus 生态这边,告警统一由 Alertmanager 负责路由和发送。要让告警打到博灵终端,最直接的办法是在alertmanager.yml里配一个 webhook receiver。

我的配置片段如下:

route: receiver: boling group_by: ['alertname', 'instance'] group_wait: 10s group_interval: 2m repeat_interval: 30m receivers: - name: boling webhook_configs: - url: http://127.0.0.1:9021/alert send_resolved: true

这里url指向的是我本地的一个中转服务,后面会讲。为什么不是直接指向博灵终端?因为 Alertmanager 发出来的 webhook 请求体是一个结构复杂的 JSON,里面包含 alerts 数组、labels、annotations、status、startsAt 等一堆字段,博灵终端解析这种工业 JSON 很容易翻车。用一个中转服务做“适配层”,把 Alertmanager 的复杂结构转成终端认识的简单字段,是最省事也最稳的做法。

group_waitgroup_interval这两个参数建议认真调一下。默认值如果太小,同一批告警会被频繁重复推送到终端;repeat_interval如果太短,比如 5 分钟,那一个未恢复的告警每 5 分钟就会把终端喊一次,值班室里就跟开了复读机一样。我这边repeat_interval设在 30 分钟,从效果看,白天不至于太烦,晚上也足够敏感。

4.2 写一个 50 行的 Flask 中转服务

中转服务我直接用 Flask,代码量少,逻辑清晰,适合自己维护。你把它跑在监控服务器上,监听127.0.0.1:9021,只让本机的 Alertmanager 访问,不对外开放。

示例代码:

from flask import Flask, request, jsonify import urllib.request import json app = Flask(__name__) BOLING_HOST = "http://192.168.1.50:8080" BOLING_TOKEN = "<你的终端Token>" def speak(text, light="red", volume=8, times=1): data = json.dumps({ "text": text, "light": light, "volume": volume, "times": times, "tts": {"voice": "female", "speed": 1.0} }, ensure_ascii=False).encode("utf-8") req = urllib.request.Request( f"{BOLING_HOST}/api/v1/play", data=data, headers={"Content-Type": "application/json", "X-Auth-Token": BOLING_TOKEN} ) try: with urllib.request.urlopen(req, timeout=5) as resp: return resp.read().decode("utf-8", errors="ignore") except Exception as e: print("调用博灵接口失败:", e) return None @app.route("/alert", methods=["POST"]) def alert(): payload = request.json for a in payload.get("alerts", []): status = a.get("status") labels = a.get("labels", {}) annotations = a.get("annotations", {}) name = labels.get("alertname", "未知告警") instance = labels.get("instance", "unknown") severity = labels.get("severity", "warning") summary = annotations.get("summary", name) if status == "resolved": text = f"告警恢复,{name},{instance} 已恢复正常。" light = "green" times = 1 volume = 5 else: text = f"告警触发,{summary},位置{instance}。" if severity == "critical": light, volume, times = "red", 10, 3 else: light, volume, times = "yellow", 7, 2 speak(text, light=light, volume=volume, times=times) return jsonify({"code": 0}) if __name__ == "__main__": app.run(host="127.0.0.1", port=9021)

启动方式很简单:

pip install flask python3 boling_webhook.py

跑起来之后,先在本地用 Alertmanager 的 webhook 测试工具或者手工 POST 一个测试 JSON,确认终端有响应。生产环境建议用一个 systemd service 托管,这里不展开说,反正别直接挂在终端窗口下,服务器重启一下就没了。

这个服务还能扩展出很实用的能力。比如在/alert路径里按当前时间判断是否处于免打扰时段,凌晨两点只放行 critical,其他级别直接丢弃;再比如记录调用日志,把每次告警播报的文本写入文件,方便后续复盘。这些都是几行代码的事情,但能帮你省掉一大半半夜被吵醒的痛苦。

4.3 把 Grafana 告警也捎带上

如果你在用 Grafana 展示监控面板,它的告警功能(Alerting)同样可以接入这个中转服务。Grafana 在 v9 及以上版本里支持配置自定义 webhook 联系人点,只需要把 URL 指向同一个中转服务的/grafana/alert路由。

Grafana 的请求体和 Alertmanager 不一样,字段主要是titlemessagestate(alerting/resolved)和labels。所以在中转服务里加一个分支:

@app.route("/grafana/alert", methods=["POST"]) def grafana_alert(): payload = request.json state = payload.get("state") title = payload.get("title", "Grafana告警") message = payload.get("message", title) labels = payload.get("labels", {}) severity = labels.get("severity", "warning") if state == "alerting": text = f"Grafana告警,{title}。" light, volume, times = "red", 9, 3 elif state == "resolved": text = f"Grafana告警恢复,{title}。" light, volume, times = "green", 5, 1 else: return jsonify({"code": 0}) speak(text, light=light, volume=volume, times=times) return jsonify({"code": 0})

这样同一个中转服务可以同时服务 Prometheus、Zabbix 和 Grafana 三套系统,统一管理声光策略。维护的时候只需要改这一个服务的代码,监控侧不用动,非常清爽。

5. 声光联动、TTS 播报细节与问题速查

5.1 声光布防时段与分级映射

终端接入之后,最重要的就是做好级别映射。如果不管什么告警都用红色灯、最大音量,那用不了两天值班同事就会把终端电源拔了。

我这边实际用的映射策略是这样的:

告警级别灯光颜色音量播报次数适用场景
Critical / Disaster红色常亮或闪烁9-103 次,间隔 5 秒主库宕机、业务大面积不可用
Warning / High黄色闪烁6-72 次,间隔 15 秒磁盘使用率过高、实例重启
Info / 信息不亮灯或蓝色呼吸不播报0只记录,不打扰
Recovery / 恢复绿色亮 3 秒4-51 次值班员确认故障闭环

布防时段我会分成工作日白天、工作日夜间、周末全天三段。工作日白天所有 Warning 以上都播,夜间和周末只播 Critical。实现方式有两种:Zabbix 动作里按时间段配置,Alertmanager 路由里配静默,或者直接在中转服务里用 Python 的datetime.now().hour判断。我实际更推荐在中转服务里做,因为所有系统共用一套逻辑,改一处就全局生效。

5.2 TTS 播报文本怎么写才不“翻车”

TTS 播报文本这个坑,估计很多没做过的人想不到。同样是中文,不同写法播出来的效果差别巨大。

第一,播报文本一定要短。终端念一条告警,30 个字以内是最舒服的,超过 80 个字就会听得非常疲惫。我的做法是只播报“级别 + 告警名称 + 关键实例”,例如:“严重告警,主数据库连接数达到百分之九十五。”把%直接写成“百分之”,TTS 就不容易吞字。

第二,特殊符号要处理。&/=\`` 这些符号在 TTS 引擎里很容易被逐字念出来,比如node_exporter中的下划线会变成“下划线”或者被忽略,听起来特别别扭。所以在脚本或中转服务里,要对文本做一次清洗,把下划线替换成空格,把百分比符号替换成文字,把http://` 这种前缀去掉。

第三,主机名和 IP 的朗读问题。像web-01这种名字,有的 TTS 会念成“web 零一”,有的会念“web 杠零一”。我建议在播报前给主机做一层别名映射,直接把web-01改成“生产网站服务器一”,或者干脆用中文报警名,少用具体机器名。TTS 的意义是让人快速理解“哪里出了问题”,而不是报出准确的内部主机名。

第四,语速和音色。博灵终端一般支持多音色和语速调节。我的建议是:普通告警用标准女声、语速 1.0,严重告警用男声、语速 1.2。更换音色本身就是一个很强的“级别差异”,值班员一听声音就知道事情多大了。

5.3 常见问题速查表

我把对接过程中最容易遇到的一批问题整理成了速查表,按“症状 -> 可能原因 -> 解决办法”的顺序列出来,供你直接对照排查:

症状可能原因解决办法
终端收不到任何请求网络不通、端口被防火墙拦先在监控服务器上 curl 终端地址,确认通不通
curl 返回 500 internal server errorURL 路径错、Token 错、JSON 字段不合法curl -v看响应体,核对路径和字段是否与文档一致
请求成功但终端没声音音量设置成了 0、播报文本为空、TTS 音色参数非法检查 JSON 里的 volume、text、tts.voice 参数
灯光不亮light 参数值设备不支持确认设备支持的灯光枚举值,不要传自定义颜色
Zabbix 动作显示已发送但终端没反应脚本没有执行权限、curl 未安装、脚本报错/var/log/zabbix/boling_alert.log,手动运行脚本测试
同一个告警反复播报repeat_interval 太小、Alertmanager 分组参数不合理调大 repeat_interval 和 group_interval
半夜被低频告警吵醒没有设置布防时段在中转服务或 Zabbix 动作里增加时间判断
TTS 播报出现乱码或吞字文本包含特殊符号、HostName 带下划线清洗文本后再播报,去掉特殊符号并做中文替换

网上搜索这件设备时经常会看到那条“request returned 500 internal server error for api route and version http://”的报错,我在这件事上消耗的时间最多。回头看,本质上就是接口路径和 Token 没对齐。大家在对接任何设备时,先花十分钟把设备文档里的 API 示例跑通,再接入监控系统,能帮你省下数小时的排查时间。

最后再分享一个小技巧:我在中转服务里加了一个/test路由,调用它就能向终端发一条固定测试消息。每次调整完告警模板或者改完声光参数,我都会先 curl 一下这个接口,30 秒内就能确认链路是否还正常,不用去翻监控页面等真实告警触发。这个习惯帮我避免了好几次配置改坏后才发现问题的尴尬。

监控告警这件事,说到底拼的不是工具数量,而是信息能不能在正确的时机、以正确的方式到达正确的人。手机推送我照常用,但那些真正重要的 P0/P1 告警,现在一定会同步打到声光终端上。几次深夜的系统故障,最后都是被那一声清晰的女声叫醒的——手机上几十条推送,你根本没力气一条一条去翻。用 HTTP API 把 Zabbix 和 Prometheus 的告警接到博灵终端,是我最近半年做的最值钱的运维改造之一。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询