做监控这些年,Zabbix帮我解决过不少问题,也给我挖过不少坑。它的文档其实写得很全,但真正从文档走到线上,中间隔着一大堆实际操作细节。比如报警配好了却收不到通知、自动发现开关打开了却没有新主机进来、主动模式的数据永远是空的。这篇文章一次性讲清楚我反复用到的五个Zabbix落地场景:Zabbix报警功能、自动发现、主动监控、拓扑图与聚合图形、自定义监控。如果你是做服务器运维、刚要把监控平台真正用起来,或者正在被这些问题折磨,这套案例完全可以当作一份直接抄作业的参考。
我按从易到难的顺序来排:报警是所有监控项的出口,自动发现解决监控项来源,主动监控解决采集方式,拓扑图和聚合图形解决数据展现,自定义监控则把Zabbix的手伸向业务层。每个案例都会聊到配置思路、完整链路、排障方法,以及一些实际操作后才会明白的细节。
1. 案例1:实现Zabbix报警功能——先让告警数据真正流到该看的人手里
1.1 报警不是单个功能,是一条数据链路
很多新手配Zabbix报警时,第一个动作就是去配告警媒介和动作,结果半天过去了还是收不到通知。我踩过同样的坑之后学到的第一课是:要看清楚报警是一条数据链路,而不是一个功能开关。
这条链路大概是这样的:监控项采集到原始数据,触发器拿到数据后做判断,条件满足就生成事件,动作检测到事件后调用媒介,媒介最终把消息发到邮箱或IM群。任何一环断了,整个报警就静默了。你可以这么理解:触发器是那个盯着温度表的哨兵,动作是接到哨兵汇报后的指挥官,媒介则是把命令传递到值班员手里的电话。
所以配置报警之前,我的建议是先在“监测 → 最新数据”页面确认监控项本身有数据。如果一个监控项从来没有数据,后面所有环节都谈不上。这个习惯能帮你把问题范围缩小一大半。
1.2 触发器表达式和通知媒介:两个容易翻车的配置点
触发器是最容易翻车的位置,翻车原因往往不是表达式语法多难,而是主机名和监控项key写得不匹配。一个标准的表达式长这样:
last(/web-prod-01/net.tcp.service[http])=0拆开看:last()取最近一次采样值,/web-prod-01是主机名,net.tcp.service[http]是监控项key,=0表示对这个主机来讲HTTP服务已经不可用。这个监控项本身就返回1和0,所以表达式很直观。
如果是CPU使用率告警,可以写成:
avg(/linux-server-01/system.cpu.util,5m)>85意思是最近5分钟内CPU使用率的平均值超过85%。注意:不同模板里CPU监控项的key可能有细微差别,最稳妥的做法是在“最新数据”页面把实际key复制过来,而不是凭记忆敲。配置完触发器之后,一定要点右下角的“测试”按钮,手动传入一个模拟值看看表达式判断对不对。这个按钮能省掉你无数个深夜排查时间。
通知媒介这边,我建议别一上来就纠结搭建邮件系统。Zabbix支持脚本类媒介,最省事的方案是写一个Shell脚本,把告警推送到IM群的机器人Webhook。脚本接收Zabbix传入的三个参数:收件人、主题、消息内容。一个常见的推送脚本长这样:
#!/bin/bash # 参数1:收件人 参数2:主题 参数3:消息内容 webhook="https://example.com/hook/your-key" payload=$(echo "$3" | jq -Rsa .) curl -s -H "Content-Type: application/json" "$webhook" \ -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"[$2] $payload\"}}" >/dev/null脚本放进/etc/zabbix/下的某个目录,给zabbix用户执行权限,然后在Zabbix前端添加媒介类型时选择“脚本”并指定这个脚本的路径。添加告警媒介时,把用户的“当告警发生时发送”勾选上,否则动作就算匹配了也找不到可用的发送通道。
1.3 动作配置与升级策略:半小时没处理的告警要能自动往上抛
动作配置的核心不只是“发个通知”,而是“把告警发给正确的人”。我自己的习惯是在动作里做升级策略,而不是把所有人一开始就拉进同一个群。下面是一个很实用的分级升级方案:
| 步骤 | 延迟时间 | 通知对象 | 说明 |
|---|---|---|---|
| 第1步 | 0秒 | 值班一线 | 告警立即发出 |
| 第2步 | 600秒 | 二线工程师 | 10分钟仍未恢复则升级 |
| 第3步 | 1800秒 | 运维负责人 | 半小时仍在告警则上报 |
操作步骤里的延迟单位是秒,600就是延迟10分钟。Zabbix会在你配置的延迟时间到达后,再执行下一步动作。这样的好处是:小故障一线自己就能处理,不至于所有人半夜被吵醒;大故障则会自动逐级上抛,不会烂在半路。
这里有一个非常隐蔽的坑:恢复操作和升级操作是分开配置的。恢复动作里如果没配置“发送恢复消息”,那么告警恢复了,群里还会一直显示那条红色告警,让人以为系统还在故障中。我通常是给恢复操作单独配置一条消息模板,内容写清楚“主机恢复正常,告警已关闭”。这样整个告警闭环才算完整。
2. 案例2:Zabbix自动发现——新设备上线后再不靠手工添加监控项
2.1 LLD低级别自动发现:用JSON和宏让监控项自己长出来
报警链路通了之后,紧接着的问题是:新机器上线,难道要一个个手工加监控项?Zabbix的自动发现功能就是用来干这个的,其中最常用的是LLD(Low-Level Discovery,低级别自动发现)。LLD的原理是:Agent端返回一段JSON数组,Server拿到后遍历数组,把数组里的字段替换成宏,然后根据你定义好的“原型”批量生成监控项、触发器和图形。
拿监控磁盘分区举例,LLD规则里的key是vfs.fs.discovery,Agent会返回类似下面的内容:
[ { "{#FSNAME}": "/", "{#FSTYPE}": "ext4" }, { "{#FSNAME}": "/boot", "{#FSTYPE}": "xfs" } ]你需要在“监控项原型”里定义key为vfs.fs.size[{#FSNAME},pused],名称写“磁盘 {#FSNAME} 空间使用率”。当Server发现有一台新主机挂载了/data分区时,它会自动用{#FSNAME}=/data替换原型,生成一条名叫“磁盘 /data 空间使用率”的监控项,不需要任何人手工干预。
触发器原型也是一样的逻辑,表达式大概长这样:
last(/模板名/vfs.fs.size[{#FSNAME},pused])>85由于宏会被自动替换,所以每发现一个新分区,就自动带出一条对应的空间告警规则。这里有一个细节必须提醒:LLD的宏名只能由大写字母、数字和下划线组成,而且大小写敏感。{#fsname}和{#FSNAME}会被当成两个不同的宏,JSON里写错一个字母,后面全部白搭。
LLD规则里面还有过滤器,可以用来排除不想监控的对象。比如想排除/boot分区,就加一个过滤器:宏{#FSNAME},正则表达式^/boot$,匹配结果设为“不满足条件则丢弃”。正则的写法可以在配置页面的“测试”按钮里直接验证,否则很容易出现“明明规则建了,就是没有数据”的情况。
2.2 网络自动发现:扫描网段并自动完成主机接入
LLD针对的是“已经纳管的主机内部的资源”,但如果你接了一批新机器,连主机都还没加进Zabbix,那就得用网络自动发现。配置位置在“配置 → 发现 → 创建发现规则”。
规则里主要填三项:IP地址范围、检测端口、延迟时间。比如我想扫描192.168.10.1到192.168.10.254这个网段,检测22端口或10051端口来判断是不是监控目标,延迟设成60秒,Zabbix就会按照这个频率去扫描。延迟值不要设得太短,否则会对网络设备造成不必要的探测压力。
光有发现规则还不够,必须配合“发现动作”。发现动作分为“主机添加”和“主机移除”两组,在添加动作里选择:发现后自动加入某个主机组、自动链接某个模板、自动设置自定义宏。这样一来,一台新机器只要IP和端口匹配,Zabbix就会自动把它纳入监控范围。有些人配了发现规则却没配发现动作,结果主机在“发现”页面里能看到,却永远不会出现在“主机”列表里,问题就出在这里。
2.3 自动发现用得稳的三个小原则
自动发现不是开了就万事大吉,它把重复劳动自动化了,同时也把“误发现”的风险放大了。我有三个小原则:
第一,扫描范围一定要收敛。能用具体网段就不要扫整个大段,宁可手工补一台机器,也不要让发现规则把无关设备扫进来,否则主机列表会被垃圾设备塞满。
第二,LLD要和模板配合使用。发现规则负责“发现资源”,但具体监控哪些指标、阈值是什么,仍然由模板决定。别在LLD规则里堆一大堆监控项原型,还是建议按主机角色挂不同模板。
第三,上线初期多观察“发现”页面。新设备第一次被自动发现后,先确认监控项原型生成的条目符合预期,再决定要不要全量推广。这个步骤能避免批量误监控。
3. 案例3:Zabbix主动监控——跨区域和大规模采集时的正确姿势
3.1 被动变主动,背后到底变化了什么
默认情况下Zabbix是“被动监控”模式:Server主动向Agent的10050端口发起连接,Agent收到请求后返回数据。这种模式的优点是很直接,一台Agent对应一个连接,但有两个非常现实的麻烦。
第一个麻烦是网络不通。主机放在NAT后面、安全组或防火墙不允许入站10050端口时,Server根本连不过去。第二个麻烦是效率。上千台机器同时采集时,Server要频繁发起连接等待响应,轮询压力和网络开销都很大。
主动模式把这些颠倒过来:Agent周期性地向Server的10051端口发起连接,获取自己应该采集哪些监控项的任务清单,然后执行采集并主动把数据上报给Server。你可以把它理解成,以前是老板挨个打电话问员工要报表,现在是员工每天到系统里认领任务,做完主动上交,老板只负责收表。
3.2 主动模式的四个关键配置点
把Agent切到主动模式,需要盯紧四个地方,少了任何一个都可能让你以为配置成功了,实际上数据全是空的。
第一个是Agent配置文件的ServerActive项。它告诉Agent要连哪个Server:
Server=127.0.0.1,10.0.0.1 ServerActive=10.0.0.1:10051 Hostname=web-prod-01这里的Server是允许谁来被动拉取数据的白名单,ServerActive才是主动上报的目标地址。如果你机器上有多个网卡或需要同时上报到多个Server,ServerActive可以写多个地址用逗号分隔。
第二个是Hostname。主动模式下Agent上报身份靠的就是Hostname,它必须和Zabbix Web前端里的主机名完全一致,一个字符都不能差。很多排查半天都不出数据的情况,最后发现是前端主机名叫web-prod-01,Agent里写成了web_prod_01。
第三个是监控项类型。在Web前端配置监控项时,类型必须选择“Zabbix agent(主动式)”,而不是默认的“Zabbix agent”。如果监控项类型还是被动式,Agent根本不会把它纳入自己的任务清单,主动上报自然就没有这个key的数据。
第四个是防火墙方向。被动模式需要Agent放行10050入站,而主动模式则要求Agent能够访问Server的10051端口。跨公网或跨机房场景下,很多安全策略只管住了Server出来的方向,没管Agent出去的方向,结果一样不通。你只要记住:主动模式下Agent是主动发起连接方,出方向才是重点。
3.3 切换主动模式后怎么确认真的在跑
切完配置,重启Agent后不要急着看前端图形,先去看Agent日志:
tail -f /var/log/zabbix/zabbix_agentd.log如果日志里能看到类似“active check”的字段以及上报目的地址,说明Agent已经和Server建立了主动连接。接着回到Web端,打开该主机的“最新数据”,刷新几次看时间戳有没有更新。时间戳停在几分钟前,说明数据链路多半还是有问题。
还有一个容易踩的坑:切换主动模式时,如果原来模板里同时存在被动和主动两套监控项,同一个key可能会有两条监控项,一个超时一个正常。检查的时候你会发现最新数据里一会儿有值一会儿没有,非常容易误判。遇到这种情况,建议把被动模式的监控项批量停用或删除,只保留主动式监控项,避免双重采集。
4. 案例4:拓扑图与聚合图形——把监控数据变成看得懂的图
4.1 拓扑图的真正用途:看清链路因果
拓扑图在监控体系里的价值不是好看,而是帮你快速判断故障的因果链。比如一个核心交换机的连线在拓扑图上变红,那么下游所有业务主机的告警就都有了合理来源;反过来说,如果没有拓扑图,你可能对着十几个主机告警挨个排查,最后才发现源头只有一个。
拓扑图不是自动生成的。Zabbix能自动发现主机,但主机之间的连接关系必须靠人维护。我在实际项目里通常只为关键链路画拓扑,而不是追求全网络节点都画上去。维护成本要和收益成正比。
4.2 拓扑图创建步骤和连线监控项选择
创建拓扑图的路径是“监测 → 主机图形 → 拓扑图”,新建一张空拓扑图后,把相关主机从上方的“主机”标签里拖到画布上。画布上可以自由拖动节点位置,建议按大致的架构层级摆放:最上面是负载和入口,中间是应用服务,下层是数据库和存储。
节点放好之后,在两个主机之间拖一条连线,连线本身需要一个名字,比如“web-prod-01 到 app-prod-01”。然后选中连线,点击编辑,在配置里给连线绑定一个监控项。可以选择某个网络接口的流量监控项或上下行速率,这样拓扑图上就能直接看到链路流量值,颜色的变化也能反映负载高低。
如果不给连线绑定监控项,拓扑图也能显示节点间的连接关系,但线就不会有任何状态信息,失去了排障时第一眼定位的价值。一个细节是:连线绑定的监控项必须真实存在于这台主机上,否则连线状态还是空的。所以画拓扑图之前,先确保各主机的模板都挂对了。
4.3 聚合图形:将多张图形拼成业务总览
聚合图形相当于一个自定义仪表盘,把分散在不同主机的图形拼到同一页。它非常适合做几类场景:机房总览大屏、业务链路总览、故障复盘时的集中对比。
创建方式很简单:新建聚合图形,设置列数和行数,然后在每个单元格里选择一个“主机 + 图形”的引用。比如第一行放所有Web服务器的CPU图形,第二行放所有数据库实例的内存图形,一屏就能横向对比多个节点的趋势。
聚合图形有一个常见坑:单元格可以选择“图形”,但这个下拉列表来源于主机已经绑定的模板。如果某台主机没有应用包含对应图形的模板,那么它的下拉列表里根本没有那个图形可选,你只能在“主机”下拉列表里看到一堆空选项。排查聚合图形空白格时,先检查主机有没有挂对模板,再检查图形引用是否正确,顺序不要反。
5. 案例5:自定义监控案例——把业务指标也放进Zabbix
5.1 标准模板管不到的地方,才需要自定义
Zabbix自带模板对CPU、内存、磁盘、网络这些基础资源覆盖得已经很全面了,但业务层的指标它管不到。比如某个内部系统的HTTP接口响应时间、消息队列的积压数量、某个特殊进程在不在线、日志文件里错误条目的增长量,这些指标都只能是自己写脚本采集。
自定义监控本质上是利用Zabbix Agent的UserParameter机制:在Agent配置文件里定义一个新key,key被请求时执行一段命令或脚本,脚本输出什么值,Zabbix就记录什么值。只要理解了这一点,你就等于给自己开了一扇无限扩展的大门。
5.2 UserParameter完整示例:监控一个HTTP接口的响应时间
假设我想监控本地某个服务的健康接口响应时间,脚本可以这样写:
#!/bin/bash # /etc/zabbix/scripts/api_time.sh url="http://127.0.0.1:8000/health" code="$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "$url")" if [ "$code" != "200" ]; then echo "0" else curl -s -o /dev/null -w '%{time_total}' --max-time 5 "$url" fi然后在zabbix_agentd.conf里加一行:
UserParameter=api.response.time,/etc/zabbix/scripts/api_time.sh保存后重启Agent,再到Web端新建一个监控项,key填入api.response.time,类型选“Zabbix agent”或“Zabbix agent(主动式)”,单位设为s。这样接口响应时间就成了一个标准的监控项,可以给response_time > 2设置触发器,也可以画趋势图形。
这里有几个非常现实的坑要提醒:
第一,脚本返回值必须是纯数字,不要混入多余文字。如果脚本往标准输出打印了调试信息,Zabbix解析不了,监控项会显示“不支持”。第二,脚本文件属主最好改成zabbix用户,权限至少744,否则Agent进程没有权限执行或者读取。第三,Agent默认的超时时间是3秒,如果你的脚本要执行5秒(比如curl用了--max-time 5),采集必挂。遇到脚本执行慢的情况,把Agent配置里的Timeout调成10或30秒再重启。
如果同一个脚本要监控多个URL,可以把key定义成带参数的格式:
UserParameter=api.response.time[*],/etc/zabbix/scripts/api_time.sh "$1"脚本里用$1接收URL参数。要注意的是,$1在配置文件里存在被Shell展开的风险,稳妥做法是把参数传给脚本后在脚本内部再做引号处理,不要直接在配置行里拼接命令。
5.3 自定义监控的三步排查法
自定义监控如果没生效,我按下面的顺序排查,基本都能快速定位:
先在Agent本机手动执行一次脚本,确认脚本本身能输出正确数字。如果脚本在自己手上有输出、在Zabbix里没数据,才进入下一步。
再用zabbix_get命令测试Agent端能不能正常返回这个key的值:
zabbix_get -s 127.0.0.1 -k "api.response.time"能返回数字,说明Agent配置文件加载成功;返回空或报错,说明UserParameter定义有问题。注意:zabbix_get默认走的是被动模式请求,如果监控项类型是“主动式”,这个命令测不到,需要去看Agent日志确认任务到底有没有下发。
最后到Web端“最新数据”页面看最新值和时间戳。图形还是空的话,重点检查数据里是否出现“不支持”字样。出现“不支持”时,多半是脚本执行超时、输出非数字、或者文件权限不对。一个一个小项排除,自定义监控其实比想象中简单。
6. 综合故障排查速查表与实战心得
6.1 五个案例里最容易踩的坑
下面这张表是我在实际配置和排障中反复用到的高频问题清单,觉得有用的话可以直接存下来:
| 遇到的问题 | 常见原因 | 定位方法 |
|---|---|---|
| 触发器设置了但没产生告警 | 表达式里的主机名或key不对、监控项本身没数据 | 先看“最新数据”,再用触发器的“测试”功能 |
| 告警产生了但收不到通知 | 动作条件不匹配、媒介参数错误、用户没勾选告警媒介 | 查看“报表 → 动作日志”,测试媒介发送 |
| LLD一直发现不到目标 | JSON不是数组格式、宏名大小写错、过滤器正则写反 | Agent端手动执行LLD key验证返回,用正则测试按钮 |
| 网络自动发现没有新主机 | 发现规则范围错、发现动作没有配置添加主机 | 检查发现页面日志,确认动作类型是“主机添加” |
| 主动模式数据一直不更新 | ServerActive或Hostname不匹配、监控项类型仍是被动 | 看Agent日志里的主动检查记录,核对前端主机名 |
| 拓扑图连线不显示流量 | 连线没有绑定监控项、主机没有对应图形 | 编辑连线并绑定真实存在的监控项 |
| 聚合图形某格空白 | 主机没应用对应模板,图形引用下拉列表为空 | 检查主机模板列表,确认图形已存在 |
| UserParameter不生效 | 没有重启Agent、脚本权限不对、返回值非数字 | 手动执行脚本,用zabbix_get验证,看日志 |
6.2 我是怎么让Zabbix从能用到好用的
这几个案例跑通之后,我还会再做几件让监控真正“好用”的事情。
第一是给告警分级。把“严重”、“警告”、“信息”三档认真定义清楚,不要什么指标异常都设成严重级别。告警一旦失去级别区分,值班人员就会失去判断力,最后所有人对告警麻木,这才是监控体系最大的隐患。
第二是优化告警内容模板。告警消息里至少要包含主机名、监控项名称、当前值、触发时间、恢复说明。让接收消息的人不用打开Zabbix就能判断问题方向,能省下不少沟通成本。
第三是定期清理无效监控项。LLD自动发现用的时间长了,主机上会残留很多不再需要的监控项和图形;网络自动发现也可能把下线设备留在列表里。这些无效对象会增加Server的调度负担,也可能在故障时干扰判断。我一般是每月跑一次,通过API拉取无数据的主机清单,逐个确认后清理。
第四是不要把所有人都拉进同一个告警群。告警群收到消息后,更多时候是没人认领。有了分级和升级策略之后,告警先到一线,再逐级上报,效果会好很多。记住,监控平台的最终目的是在故障发生时缩短恢复时间,而不是制造更多噪音。
我自己的习惯是,每次新接一套环境,会把告警触发和恢复消息都发到同一个测试群跑几天,确认没有人半夜收到误报后再正式接人。Zabbix的很多设置第一次看起来都挺绕,但这几个案例本质都是在回答一个问题:数据从哪里来、怎么判断它异常、异常后怎么办、以及给人怎么看。把这四个问题串起来,剩下的大部分配置其实是围绕这套思路的填空。