1. 为什么客户端监控是绕不开的一道坎
搞运维的都知道,服务器监控这件事,装个服务端只是万里长征第一步。真正让人头疼的,是后面怎么把几十上百台机器的数据准确、稳定地收上来。Zabbix 作为老牌监控方案,服务端和 Web 界面搭起来不算难,但客户端这一侧如果没配明白,后面看到的全是红叉和未知状态,排查起来能耗掉一整天。
这篇内容聊的就是 Zabbix 客户端在 Linux 环境下的监控落地,重点放在自定义 key 的编写和调试上。适合已经搭好 Zabbix 服务端、正准备批量接入 Linux 主机的朋友,也适合那些内置监控项不够用、需要自己抓业务指标的人。我会从整体设计思路讲到具体配置,再到实际踩过的坑,尽量把每一步为什么这么做说清楚。
先说一个基本认知:Zabbix 的监控数据流向是“客户端采集 → 服务端拉取 → 数据库存储 → 前端展示”。客户端这一层负责的是最前端的采集工作,它决定了你能拿到什么数据、数据准不准、采集会不会拖垮业务。很多人把注意力全放在服务端调优上,结果客户端配置一塌糊涂,采集间隔设成 1 秒、脚本里写死循环,最后机器负载飙升还找不到原因。所以客户端这块值得单独拿出来认真对待。
2. 客户端监控的整体设计与选型思路
2.1 主动模式与被动模式的取舍
Zabbix 客户端有两种工作模式,这个必须先搞清楚,因为它直接决定了你后续的配置方式和网络规划。
被动模式是服务端主动来连客户端,客户端上的 agent 监听 10050 端口,服务端发起请求,agent 返回数据。这种模式配置简单,服务端说什么时候要数据就什么时候来拿。但问题也很明显:如果客户端数量多,服务端要轮询几百上千台机器,压力全压在服务端身上,而且服务端到客户端必须网络可达。
主动模式反过来,客户端主动把数据推给服务端的 10051 端口。客户端自己按配置的间隔采集,攒够了就发过去。这种模式对服务端友好,适合大规模场景,而且客户端在 NAT 后面、服务端连不进来的情况也能用。
我的建议是:机器数量在 50 台以内,被动模式够用,配置直观好排查;超过 100 台,认真考虑主动模式,否则服务端的轮询队列会越来越长,采集延迟肉眼可见地增长。实际生产里我见过 300 多台机器全用被动模式,结果服务端 poller 进程天天跑满,数据延迟十几分钟,最后不得不整体切到主动模式。
2.2 内置 key 与自定义 key 的边界
Zabbix 自带了一大批内置 key,比如system.cpu.load、vm.memory.size、net.if.in这些,覆盖了操作系统层面的常规指标。这些 key 的好处是开箱即用、性能好、由 agent 原生实现,不需要额外脚本。
但内置 key 有个天花板:它只能拿到操作系统暴露出来的通用指标。一旦你要监控业务层面的东西,比如某个 Java 进程的堆内存、某个目录下的文件数量、某个接口的响应时间,内置 key 就无能为力了。这时候就得靠自定义 key。
自定义 key 的本质是:你在 agent 配置文件里声明一个 key 名字,然后把它绑定到一段命令或脚本上。服务端请求这个 key 的时候,agent 就执行对应的命令,把标准输出返回回去。听起来简单,但里面有不少细节决定了它能不能稳定工作。
2.3 采集频率与性能的平衡
采集频率这个事,新手容易走极端。要么设得特别密,觉得数据越细越好;要么设得特别稀,觉得省资源。这两种都不对。
采集频率的设定要基于指标的变化速度。CPU 负载这种秒级波动的指标,60 秒采一次是常规做法;磁盘使用率这种一天可能就变几个百分点的,5 分钟甚至 10 分钟采一次完全够用。自定义 key 如果背后是执行脚本,那更要谨慎,因为每次采集都要 fork 一个进程出来,频率太高对客户端本身就是负担。
我一般的做法是:核心业务指标 60 秒,系统资源指标 60 到 120 秒,容量类指标 300 秒。这个节奏在几百台机器的规模下跑下来,客户端负载基本可以忽略。
3. 客户端安装与基础配置的实操细节
3.1 安装源的选择与版本对齐
Linux 下装 Zabbix agent,第一步是选对安装源。不同发行版的包管理方式不一样,但核心原则只有一个:agent 版本要和服务端版本对齐,至少大版本要一致。
服务端是 6.0,客户端就装 6.0;服务端是 5.0,客户端也尽量用 5.0。跨大版本混用虽然很多时候能跑,但遇到协议变更或者新特性的时候就会出问题,而且排查起来很隐蔽。我吃过一次亏,服务端 6.0 配了新的主动模式参数,客户端还是 5.0 的老 agent,结果主动上报一直不生效,日志里也没明显报错,查了半天才发现是版本不匹配。
安装方式上,主流发行版都有官方仓库。以常见的 RPM 系为例,先装仓库配置包,再装 agent 本体。Debian 系类似,只是包名和仓库地址不同。装完之后用systemctl status确认服务状态,别急着往下走。
3.2 配置文件的关键参数逐项说明
agent 的主配置文件通常在/etc/zabbix/zabbix_agentd.conf,里面参数不少,但真正需要动的就那么几个。我挑最关键的几个说。
Server参数:被动模式下,这个参数指定允许哪些服务端 IP 来连本机的 agent。可以写多个 IP,用逗号分隔。这里有个坑,很多人图省事写0.0.0.0/0或者直接写服务端网段,虽然能通,但等于把 agent 端口暴露给了整个网络。正确做法是只写服务端和代理的实际 IP。
ServerActive参数:主动模式下,这个参数指定客户端把数据推给谁。注意它和Server是两个独立参数,主动模式必须配这个,被动模式必须配Server,两个都配也没问题,agent 会同时支持两种模式。
Hostname参数:这个极其重要,它必须和 Zabbix 前端里配置的主机名完全一致,大小写敏感。很多“数据收不上来”的问题,根源就是这里对不上。前端主机叫web-server-01,配置文件里写成Web-Server-01,agent 就认不出自己是谁,主动模式下数据直接丢弃。
Include参数:这个参数指向自定义配置目录,通常默认就有,指向/etc/zabbix/zabbix_agentd.d/。自定义 key 的配置文件就放在这个目录下,好处是升级 agent 的时候不会覆盖你的自定义配置,管理起来也清晰。
Timeout参数:agent 等待命令返回的超时时间,默认 3 秒。如果你的自定义脚本执行比较慢,比如要查数据库或者调接口,这个值要适当调大,但别调太大,否则一个卡住的脚本会拖累整个采集。一般设 5 到 10 秒比较稳妥。
3.3 防火墙与 SELinux 的处理
配置改完,服务重启,结果服务端还是连不上,十有八九是防火墙或者 SELinux 在拦。
防火墙方面,被动模式要放行 10050 端口,主动模式客户端是往外连的,一般不需要额外放行入站。用firewall-cmd或者iptables按需放行即可,注意只放行服务端 IP,别开成对所有来源开放。
SELinux 这块更隐蔽。默认策略下,SELinux 会限制 agent 执行自定义脚本,尤其是脚本放在非标准目录、或者要访问某些受限资源的时候。表现是脚本手动执行没问题,agent 一调用就失败。处理方式有两种:一是把脚本放到 SELinux 允许的目录并打上正确的上下文标签,二是调整相关布尔值。我一般先看审计日志确认是不是 SELinux 拦的,再决定怎么放行,不要一上来就setenforce 0,那是掩耳盗铃。
4. 自定义 key 的编写与调试全流程
4.1 自定义 key 的语法规则
自定义 key 的声明写在配置文件里,格式是:
UserParameter=key_name,commandkey_name是你自己起的名字,服务端配置监控项的时候就用这个名字。命名建议用点号分层,比如business.order.count、app.heap.used,这样一看就知道是哪个层面的指标,也方便前端做分组。
command是实际执行的命令,可以是系统命令,也可以是脚本路径。命令里可以带参数,参数用$1、$2这样的占位符表示,服务端传参的时候会替换进去。
有个细节要注意:命令的输出必须是单行文本,或者能被 agent 处理成单行。如果脚本输出多行,agent 默认只取第一行,或者直接报错。所以脚本里要确保最终输出是干净的一个值。
4.2 从简单命令到脚本封装
最简单的自定义 key 就是包一层系统命令。比如想监控某个目录下的文件数量:
UserParameter=dir.file.count,ls /data/logs | wc -l这种写法能用,但不推荐。原因是命令直接写在配置里,可读性差,复杂逻辑没法写,而且一旦命令里有特殊字符,转义起来很麻烦。
更好的做法是把逻辑封装成脚本,配置里只调用脚本:
UserParameter=dir.file.count,/etc/zabbix/scripts/count_files.sh /data/logs脚本放在专门的目录里,比如/etc/zabbix/scripts/,权限设成 agent 用户可执行。脚本内部可以写复杂的判断、循环、错误处理,维护起来清爽得多。
4.3 脚本编写的几个硬性要求
写自定义脚本,有几条规矩必须守,否则后面全是坑。
第一,脚本必须有明确的输出,且输出要能被解析成数字或字符串。如果脚本出错,不要输出一堆错误信息,最好输出一个约定的异常值,比如-1,然后在前端配置触发器的时候针对这个值做告警。
第二,脚本要设置超时。agent 虽然有Timeout参数,但脚本内部如果调用了外部接口,最好自己也加超时控制,避免脚本卡死把 agent 拖住。
第三,脚本不要依赖交互式环境变量。agent 执行脚本时的环境和你手动 SSH 登录后的环境不一样,PATH 可能不同,某些变量可能不存在。所以脚本里尽量用绝对路径,需要环境变量的地方自己显式设置。
第四,脚本的退出码要规范。返回 0 表示成功,非 0 表示失败。agent 会根据退出码判断采集是否成功,退出码不对会导致数据被标记为不可用。
4.4 用 zabbix_get 做本地验证
配置写完,别急着去前端加监控项,先在本地用zabbix_get验证。这个工具是 agent 自带的,可以模拟服务端向 agent 请求某个 key:
zabbix_get -s 127.0.0.1 -p 10050 -k "dir.file.count[/data/logs]"如果返回了预期值,说明 agent 配置和脚本都没问题,问题可能出在网络或者前端配置上。如果报错,错误信息会直接告诉你哪里不对,比如 key 不存在、脚本执行失败、超时等等。
这一步是排查问题的分水岭。养成习惯:任何自定义 key 上线前,先用zabbix_get在本地跑通,能省掉大量来回折腾的时间。
5. 自定义 key 的进阶用法与性能考量
5.1 带参数的自定义 key
自定义 key 支持传参,这让一个脚本能覆盖多个同类指标。比如监控多个目录的文件数量,不用为每个目录写一个 key,而是写一个带参数的 key:
UserParameter=dir.file.count[*],/etc/zabbix/scripts/count_files.sh $1服务端配置监控项的时候,把参数写在方括号里,比如dir.file.count[/data/logs]、dir.file.count[/data/tmp]。这样一套脚本就能服务多个监控项,维护成本大大降低。
参数传递的时候要注意,如果参数里可能包含空格或特殊字符,脚本里要做好引号处理,否则参数会被拆开。稳妥的做法是在脚本里对$1加双引号。
5.2 脚本执行的性能开销
每次采集都 fork 一个进程执行脚本,这个开销在单机上看起来微不足道,但乘以采集频率和监控项数量就不一样了。
假设一台机器有 20 个自定义 key,每个 60 秒采一次,那就是每分钟 20 次进程创建。如果脚本本身还比较重,比如要读大文件、查数据库,那 CPU 和 IO 的消耗就会累积。几百台机器同时这么干,对服务端和客户端的压力都不小。
优化的思路有几个:一是合并同类脚本,把多个指标用一个脚本一次性输出,减少进程创建次数;二是降低非核心指标的采集频率;三是对于特别重的采集逻辑,考虑用常驻进程或者缓存机制,而不是每次重新计算。
5.3 输出格式的规范化
自定义 key 的输出,最好保持格式统一。纯数字的指标直接输出数字,不要带单位、不要带千分位分隔符。带单位的输出会让前端做数值计算和触发器判断时很麻烦。
如果确实需要输出多个值,可以用逗号分隔,然后在前端用预处理功能拆分。但更推荐的做法是一个 key 只输出一个值,需要多个指标就定义多个 key,这样每个监控项的职责清晰,触发器也好写。
6. 常见问题排查与避坑经验
6.1 数据收不上来的排查顺序
数据收不上来是最常见的问题,排查要有顺序,别东一榔头西一棒子。
先看 agent 服务状态,systemctl status确认进程活着。再看 agent 日志,通常在/var/log/zabbix/zabbix_agentd.log,里面会记录连接失败、key 不存在、脚本超时等错误。然后本地用zabbix_get验证 key 是否可用。最后检查网络和防火墙,确认服务端到客户端的端口通不通。
这个顺序是从内到外,先排除客户端自身问题,再查网络。很多人一上来就怀疑网络,结果折腾半天发现是配置文件里主机名写错了。
6.2 自定义 key 不生效的典型原因
自定义 key 配了但前端显示不支持,常见原因有这么几个。
配置文件没被加载。检查Include参数指向的目录是否正确,自定义配置文件是否放在那个目录下,文件后缀是不是.conf。agent 只加载符合命名规则的文件。
配置改完没重启 agent。Zabbix agent 不会自动重载配置,改完必须重启服务。
key 名字拼写不一致。前端监控项里的 key 和配置文件里的 key 必须完全一致,包括大小写和参数格式。
脚本权限不对。agent 以zabbix用户运行,脚本必须对这个用户可执行。用ls -l确认权限,必要时chmod +x。
SELinux 拦截。前面提过,看审计日志确认。
6.3 脚本超时与僵尸进程
脚本执行超时是另一个高频问题。表现是监控项间歇性不可用,日志里有 timeout 记录。
原因通常是脚本内部有阻塞操作,比如网络请求没设超时、读文件被锁、死循环。解决办法是给脚本加超时控制,agent 的Timeout参数也要合理设置。
更麻烦的是僵尸进程。如果脚本 fork 了子进程但没正确回收,时间长了会积累一堆僵尸进程。写脚本的时候要注意 wait 子进程,或者用timeout命令包一层,确保超时后进程被清理。
6.4 常见问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 前端显示主机不可达 | 网络不通或防火墙拦截 | 检查 10050 端口连通性 |
| 主动模式数据不上报 | Hostname 不匹配 | 核对前端主机名与配置文件 |
| 自定义 key 显示不支持 | 配置未加载或 key 拼写错误 | 检查 Include 目录和 key 名称 |
| 脚本手动能跑 agent 跑不了 | 权限或 SELinux 问题 | 检查脚本权限和审计日志 |
| 监控项间歇性不可用 | 脚本超时 | 检查脚本执行时间和 Timeout 参数 |
| 数据值异常 | 脚本输出格式不对 | 确认输出为单行纯数值 |
7. 批量部署与配置管理的思路
机器一多,一台台手动配 agent 就不现实了。这时候要考虑批量部署和配置管理。
批量部署的核心是标准化。把 agent 安装、配置文件、自定义脚本打包成一个标准单元,用自动化工具推送到所有机器。配置文件的差异部分,比如 Hostname,通过模板变量在部署时替换。
配置管理工具方面,Ansible 是比较轻量的选择,不需要在客户端装 agent,通过 SSH 就能推送配置。写一个 playbook,把安装、配置、启动、验证串起来,新机器接入就是跑一遍脚本的事。
自定义脚本的版本管理也要跟上。脚本改动后,要能快速同步到所有机器,并且保证 agent 重新加载配置。我一般把脚本放在配置管理仓库里,改动走正常的代码评审流程,部署的时候统一推送,避免出现不同机器脚本版本不一致的情况。
8. 监控项设计的一些个人体会
做了这么多年的监控,我越来越觉得,监控项的设计比监控工具本身更重要。工具再好,监控项设计得乱七八糟,一样起不到作用。
自定义 key 给了我们很大的自由度,但自由度也意味着责任。每加一个自定义 key,都要想清楚:这个指标真的需要监控吗?它的正常范围是多少?异常的时候希望怎么告警?如果这些问题答不上来,那这个 key 可能就不该加。
另外,监控项不是越多越好。我见过有的团队给每台机器配了几百个监控项,结果告警天天响,真正重要的告警被淹没在噪音里。精简监控项,聚焦核心指标,比堆数量有意义得多。
还有一点,自定义脚本要当成代码来维护。写注释、做版本控制、定期 review,这些软件工程的实践同样适用于监控脚本。脚本写得太随意,过几个月自己都看不懂,出了问题只能干瞪眼。
最后分享一个我常用的调试技巧:自定义脚本里加一个调试开关,通过环境变量或者参数控制,需要排查问题的时候打开,输出详细的中间过程到临时文件,平时关闭保持输出干净。这样既不影响正常采集,又能在出问题时快速定位。这个习惯帮我省了很多次抓瞎的时间。