
做运维监控的人多数都经历过这样的时刻凌晨三点Zabbix告警邮件确实发出了但没人第一时间看邮箱等早上打开手机二十几封告警邮件躺在垃圾箱里重要故障差点被淹没。这套“Zabbix分布式监控”系列写到第六篇我想换个角度不继续聊监控指标采集和模板调优而是聚焦告警触达的最后一公里——部署onalert云告警平台把Zabbix的告警从“发出去”变成“必达”。这篇文章适合两类读者一类是已经在用Zabbix、但还在为邮件告警时效性头疼的运维另一类是刚接触告警平台概念想知道onalert这类工具能解决什么问题的朋友。我会把整条链路拆开讲清楚为什么需要独立的告警推送层、怎么规划通知策略、如何从零部署、如何在Zabbix里接入以及我实操过程中踩过的那些坑。内容尽量给细节尽量可直接照抄。1. 为什么Zabbix需要一个独立的告警推送层1.1 邮件告警的痛点发出不等于必达Zabbix自带的告警方式很全邮件、短信、脚本、Webhook都支持。但邮件告警在实际生产环境里问题最大。第一移动端邮件客户端推送延迟不稳定很多人的手机默认把监控邮件归入推广邮件或垃圾箱等看到的时候故障已经过去半小时。第二邮件无法做“分组订阅”和“告警确认”一条故障邮件发到一个邮件组里谁负责处理、谁已确认完全没有闭环。第三如果环境里同时有Zabbix、Prometheus、云基础设施监控等多套系统每一套都各自配邮件、各自配短信通道重复建设维护成本非常高。有些团队会直接写Python/Shell脚本在Zabbix动作里调用钉钉或企业微信的群机器人接口。对小规模环境来说这个方案确实能用但脚本一旦多起来问题就出现了多个脚本散落在不同服务器上密钥管理混乱告警内容格式不统一新同事接手时根本不敢乱动。这个阶段其实就缺一个统一的告警入口。1.2 onalert在监控链路里的定位onalert是一个开源的告警聚合推送平台核心工作是把来自不同监控系统的告警消息接进来再根据你配置的策略把消息推送到钉钉、企业微信、飞书、邮件等渠道。它不负责收集指标也不负责判断阈值它的定位很纯粹做一个“告警集散中心”。在整个告警链路里它的位置是这样的Zabbix通过告警媒介把消息推给onalert的APIonalert完成消息解析、去重、策略匹配再投递到目标群或目标用户的即时通讯工具。这个设计的好处是Zabbix只管“发生了什么告警”不需要关心“告警发给谁、用什么通道发”通道的切换、人员的调整、策略的变更全部在onalert里完成。如果你已经用了Alertmanager那一套生态那可以不用onalert。但如果你主要监控系统就是Zabbix又希望钉钉/企微/飞书都能稳定收到告警onalert这种轻量级平台会比在Zabbix里堆一堆脚本要清爽得多。部署一个平台服务大概占多少资源一个中小型环境2核2G的虚拟机跑起来完全够用后续如果告警量大再单独扩一个实例或者把数据库迁移到独立实例。2. 部署前的设计与规划2.1 消息推送逻辑与权限模型在部署前我建议先花半小时想清楚告警的流向否则部署完还要反复改配置。onalert里最核心的几个概念是应用、用户、群组、告警类型、通知策略。应用相当于一个“租户”或“业务线”。你在平台里创建一个应用会得到一个密钥Zabbix推送消息时携带这个密钥平台就知道消息属于哪个业务。为什么要有这一层因为如果不区分业务线所有系统共用同一个入口有人误发一条测试消息所有人都会收到排查问题时非常混乱。多个监控系统、多个创业团队用同一个平台实例时应用隔离非常关键。用户和群组对应真实的人和通知渠道。用户需要先绑定自己的钉钉或企业微信账号或者加入对应群组。通知策略则决定了什么级别、什么类型的告警在什么时间段发给哪些人。我见过不少团队把所有人拉到一个群、所有告警都往群里丢结果告警多了以后群里的通知彻底被刷屏真正重要的事反而没人响应。所以策略的规划比部署本身更重要。一个比较合理的推流逻辑可以参考下面这张表告警类型级别接收人渠道备注服务宕机/重启严重值班组全员钉钉群企业微信立即通知CPU/内存使用率高警告对应业务负责人钉钉群聚合后发送磁盘空间不足警告存储管理员值班组邮件飞书按严重程度升级恢复消息通知原告警接收人原告警回复统一发送2.2 部署选型与资源评估部署onalert主要有两种方式用Docker镜像拉起或者用源码安装。对于大多数运维团队我强烈建议用Docker方式原因有三点依赖环境隔离、升级回滚方便、迁移到其他机器时不用重新折腾Python环境。源码安装适合那些对部署安全有特殊要求、或者内网环境没法拉取镜像的团队但配置和依赖管理会更吃力。资源评估这一块可以做一个简单的估算。假设Zabbix环境有500个监控项一天大概产生100条告警消息每条消息平均1KB这个规模下消息存储一年的数据量也只有几十MBSQLite数据库就够用。不过要注意的是如果告警内容里带了很长的事件堆栈或者完整指标历史单条消息的长度会膨胀建议在推送前就把内容截断。内存方面平台本身是轻量应用但Web服务框架和消息推送线程会占用一些内存配2GB比较稳妥。磁盘预留10GB足够主要给数据库文件和日志。端口规划上Web控制台和API端口默认是5200如果你的服务器上已经有别的服务占用记得改掉。部署完成后前面可以不加反向代理直接暴露给内网使用跨网段时再考虑通过网关转发。2.3 确定告警接收人的工作流这里有一个很多团队都会忽略的问题告警不只是“通知人”还要落到“处理人”。我建议在部署前先把告警接收人按照链路角色划分成三档第一档是总值班负责处理大的基础设施故障任何服务不可用级别的告警都要通知到第二档是业务负责人只接收自己负责模块的告警别的模块不要打扰第三档是开发协作者只在需要变更配合时接收通知。Zabbix侧的“用户”和“用户群组”最好在Zabbix里统一创建名字和onalert里保持一致这样动作配置时一眼就能对应上。另外有一个经验通知策略要尽量保持简单不要一开始就搞复杂的“按值班表轮换、按告警级别延迟、按业务线汇总”。先在群里收到所有告警、稳定运行两周再根据实际噪音逐步收敛。告警平台的配置是一门减法艺术少而准永远比多而全有效。3. 部署实操从零搭起云告警平台3.1 环境准备与Docker启动我以一个全新的虚拟机为例系统是常见的CentOS/Rocky环境。先确认机器基础信息然后创建应用目录和数据目录。对于容器化部署建议把数据库文件持久化到宿主机目录防止容器重建后历史告警数据全部丢失。准备一个简单的docker-compose编排文件思路是先挂载数据卷再把容器端口映射到宿主机。镜像不写死标签而是采用一个明确的版本号方便后续排查时对齐行为。version: 3 services: onalert-server: image: your-registry/onalert:latest container_name: onalert restart: always ports: - 5200:5200 volumes: - ./data:/app/data environment: - TZAsia/Shanghai启动之前要重点确认两点一是宿主机的防火墙放行对应端口二是重启策略设置为always否则服务器一重启监控告警平台就悄悄掉了非常被动。启动后进容器看日志确认数据库初始化没有报错。注意如果你是第一次使用这个镜像建议先在测试机上跑通一遍再上生产因为不同版本的初始化用户、默认密码和API路径可能有差异。3.2 平台初始化创建应用与用户启动完成后浏览器访问控制台地址使用默认账号密码登入。第一次登录后第一件事是改密码这个动作别拖默认密码被扫描器尝试的风险非常大。接下来按顺序创建核心资源。第一步是创建应用。应用名称可以叫“Zabbix-Prod”创建后平台会生成一个密钥这个密钥就是后续Zabbix推送时要用到的凭证。密钥只展示一次建议复制后存到密码管理工具里不要明文写在脚本中。第二步是创建用户。为每一位值班人员创建账号并绑定对应的钉钉/企业微信账号。绑定过程一般是跳转到对应的授权页面让用户本人扫码授权这也是为什么不能完全由管理员代操作的原因。绑定完成后我习惯发一条测试消息让用户确认自己在对应群里能看到推送。第三步是设置通知策略。不同级别的告警可以映射到不同的通知渠道例如严重告警推到钉钉群警告告警推到企业微信恢复通知只邮件。这里我建议把恢复消息单独作为一个策略避免恢复消息和故障告警混在同一个群聊里造成混乱。策略的测试可以在创建后立即发送一条“自动化测试”消息验证。3.3 在Zabbix中接入告警Zabbix接入onalert有两种方式我先后都试过。如果你用的是Zabbix 5.0以上版本推荐使用Webhook媒介类型优点是不需要额外装Python环境和依赖直接在Zabbix前端配置JavaScript逻辑。如果你的Zabbix版本比较老或者更习惯脚本方式那就用脚本媒介类型。这里先说Webhook的方式。在Zabbix的“管理-报警媒介类型”中创建一个新媒介类型类型选“Webhook”。主要需要配置三个部分请求方法选POST添加一个自定义Header比如Authorization字段存放之前拿到的密钥请求体按照onalert支持的格式构造JSON。由于不同版本的onalertAPI字段名会有差异这里不写死字段思路就是“把Zabbix的{ALERT.SUBJECT}和{ALERT.MESSAGE}映射到请求体的对应字段”。如果你的版本只支持脚本方式参考下面这个简化版的Python脚本。脚本接收三个参数告警主题、告警内容、接收人名称。#!/usr/bin/env python3 import sys import requests import os ONALERT_API os.environ.get(ONALERT_API, http://your-onalert-host:5200/api/v1/alert/push) APP_TOKEN os.environ.get(APP_TOKEN, your-secret) def send(subject, message, receiver): payload { app_token: APP_TOKEN, subject: subject, content: message, receiver: receiver, } try: resp requests.post(ONALERT_API, jsonpayload, timeout10) if resp.status_code ! 200: print(onalert push failed: {} - {}.format(resp.status_code, resp.text)) return 1 except Exception as exc: print(onalert request exception: {}.format(exc)) return 1 return 0 if __name__ __main__: subject sys.argv[1] if len(sys.argv) 1 else message sys.argv[2] if len(sys.argv) 2 else receiver sys.argv[3] if len(sys.argv) 3 else default sys.exit(send(subject, message, receiver))脚本里有一个细节把API地址和密钥通过环境变量注入而不是写在脚本里。这样做的好处是脚本文件本身可以进版本库不会泄露密钥。如果脚本执行失败Zabbix会在动作日志里记录退出状态这对于排查非常有帮助。创建好媒介类型后还需要给用户配置媒介。在Zabbix“用户-报警媒介”中把刚才创建的类型分配给具体用户写上接收人名称。这样动作里就可以直接使用该媒介。3.4 触发器与动作联动配置接入告警的最后一步是配置动作这一步决定了哪条触发器消息会触发通知。我以一个实际场景为例监控某台Web服务器的CPU负载。先创建一个触发器条件可以设成“最近5分钟CPU负载平均值超过4”然后为这个触发器创建一个动作。动作里配置“操作”标签页选择发送给“值班组”使用媒介类型“onalert”关联要触发的用户。标题和内容尽量用Zabbix的宏来拼接比如标题{TRIGGER.NAME} - {TRIGGER.SEVERITY}内容主机{HOST.NAME}\n故障时间{EVENT.DATE} {EVENT.TIME}\n当前值{ITEM.VALUE}这样写出来的消息接收人一眼能看出是哪台机器、什么问题、现在什么状态。建议不要把一整段指标历史都塞进内容里即时通讯工具的卡片能展示的字数有限太长反而影响阅读。动作里还应该设置恢复操作。Zabbix在故障恢复时会触发“恢复操作”这里同样发送给值班组内容写“故障已恢复”。如果动作里没有配置恢复操作接收人只会看到故障产生、不会看到故障恢复值班人员还要自己跑去确认体验很差。4. 端到端告警链路测试4.1 手动触发与消息推送验证配置完成后不建议立刻在真实环境里等人为制造故障来验证而是分步骤做端到端测试。先做脚本级验证。手动在Zabbix服务器上执行一次上面的Python脚本传入测试标题和内容看onalert控制台是否收到消息。这一步能排除最基础的网络、密钥、接口路径问题。执行时要注意执行用户身份Zabbix动作脚本通常是以zabbix用户运行的提前确认这台服务器上的执行用户能访问目标API地址。再做动作级验证。在Zabbix动作列表里选一个动作点“测试”模拟一条告警消息查看动作日志是否显示发送成功。这里有一个容易忽略的点动作“测试”只是验证动作的条件和操作是否能匹配并不会真正调用发送脚本。真正的触发验证还是要模拟触发器故障。比较稳妥的方式是临时把触发器的阈值改为一个明显会被超出的值比如把CPU阈值临时改成0.1等待一个监控周期后触发器进入异常状态动作自动执行。这个过程会自动走完“触发器异常→事件产生→动作条件匹配→调用媒介→推送onalert→转发到钉钉群”的完整链路。测试完成后记得把阈值改回来并确认触发器恢复恢复消息也会正常发出。4.2 消息合理性与去重配置链路通了好消息但不代表链路配置得合理。我在测试过程中发现一个常见问题Zabbix默认会给每一条事件都发一条通知即使同一个问题连续触发多次。如果不做收敛一次抖动可能让值班人员收到十几条重复告警。好消息是后期可以把去重放在onalert侧完成。onalert收到相同应用、相同告警类型、相同标题的消息时可以在一个时间窗口内合并或者丢弃重复消息。具体是否开启取决于你的版本。另外Zabbix侧也要养成一个习惯触发器尽量设置恢复间隔和容忍次数。比如“5分钟内有3次采样超过阈值才触发”这比“一次超过就发告警”靠谱得多。通知内容里的时间格式也建议统一。Zabbix默认的日期时间格式是“2024.01.01 12:00:00”这种格式在各类通讯工具里都能正常显示没有问题。但如果你有自定义格式需求可以直接在动作内容里用宏拼接。5. 常见问题与排查技巧实录5.1 告警一直没收到先查哪个环节告警丢了很多人的第一反应是看Zabbix动作日志这没错但效率不高。我建议按这个顺序排查Zabbix“报表-动作日志”看动作是否被触发、脚本/Webhook执行是否成功手动执行相同脚本看能否正常推送这一步能区分问题在Zabbix侧还是在onalert侧onalert控制台的消息列表看平台是否收到消息、是否被策略拦截通道侧钉钉/企业微信/飞书的机器人配置看回调地址是否有效。大多数“收不到”的问题最终都出在第4步。群机器人有一个常见坑如果你把企业微信机器人的Webhook地址删除后再重新添加地址会变化而onalert里还是旧的地址推送就必然失败。这种问题看onalert日志时会看到推送返回的HTTP错误码对照错误信息去检查通道配置即可。5.2 消息推送报签名/校验失败怎么办如果你的版本在推送时要求签名验证而脚本或者Webhook请求里没有带正确的签名平台会拒绝消息。处理方式是在请求参数里额外增加一个签名字段它的计算规则一般是对当前时间戳和密钥做组合哈希。建议用脚本方式时把整个签名逻辑封装成一个函数并且使用系统当前时间戳不要用固定时间否则时间一过签名就会失效。另外如果你在推送时经常遇到HTTP 400多半是请求体里的字段类型不对例如把数组写成了字符串或者必填字段为空。这类问题通过查看onalert的服务端日志很快就能锁定具体是哪个字段。遇到这种情况不要盲目改脚本先看日志。5.3 Webhook超时与EOF报错在使用脚本媒介时我遇到过一个比较典型的报错脚本执行很快但Zabbix动作日志里返回了类似“EOF when reading a line”的错误。这个报错有几个可能原因脚本本身的子进程在读取标准输入时Zabbix并没有按预期传入全部参数或者脚本依赖的Python环境缺失。在脚本开头加一段参数数量判断把参数打印到临时日志里是最快的排查方式。还有一个实际经验是在Python脚本里向onalert发送请求时务必设置一个适当的超时时间。设置超时可以防止平台端响应慢时Zabbix动作被长时间挂住。不要无限重试正常网络环境下请求几百毫秒就返回了如果超过3秒甚至更久说明这个链路本身就有问题该查网络而不是调超时参数。5.4 恢复消息发不出来只有故障消息很多团队接入后会发现故障告警能收到但恢复消息始终没动静。这个问题的根源几乎都在Zabbix动作的恢复操作没有正确配置。在创建动作时有三个标签页条件、操作、恢复操作。如果你只在“操作”里配置了通知系统只会在问题产生时发通知恢复时不会触发。正确做法是在“恢复操作”里单独配置一条“发送恢复通知”的操作接收人和媒介类型可以和故障时保持一致。这个问题排查起来很简单但刚接触Zabbix动作的同事真的很容易漏。5.5 实测有效的三条避坑经验第一部署完成后先做“十分钟冒烟测试”。在onalert里创建完应用和用户后先用平台的测试功能发一条真实消息到目标群确认通道配置、人员授权、策略匹配全部正常再接Zabbix节省后面一小时排障时间。第二每个业务线一个应用用密钥分离消息。不要所有监控系统都共用一个应用否则一旦有告警风暴连“哪个系统来的消息”都分不清。密钥也不要直接在Zabbix脚本里明文写用环境变量或宏来管理。第三定期备份数据和导出配置。容器化部署最大的便利是数据和配置都可以挂载出来一个tar包就能完成备份。我开始用onalert时没太在意备份后来一次误操作把应用密钥改了导致所有Zabbix推送失败才发现备份的重要性。现在每天凌晨会自动打包数据目录到对象存储虽然暂时还没用上但这个习惯带来的安全感是很踏实的。说实话从纯邮件告警切换到onalert这种多渠道推送平台之后最大的感受不是“消息变多了”而是值班的人终于能第一时间知道线上状态了。Zabbix本身已经把监控数据、触发器、事件管理都做得足够好缺的只是把告警有效触达出去的那一环。onalert部署起来并不复杂真正值得花时间的是前期把接收人、级别和渠道规划清楚。如果你现在的告警还停留在“邮件发送成功就是胜利”的阶段不妨按照上面的思路先搭一个统一告警入口慢慢把通知策略调到自己舒服的节奏上来。