前阵子在一台 Rocky Linux 9.8 上部署 Zabbix 7.0,按官方步骤初始化数据库时直接被一个报错拍在脸上:access denied for user 'replace_user'@'localhost' (using password: YES)。当时第一反应是“这什么情况,我明明还没创建用户”,后来一看是自己复制的 SQL 模板里还留着占位符没替换干净。这种报错对老手来说可能扫一眼就能定位,但对刚接触 Zabbix 的人来说,卡个半天很正常。
这些年用 Zabbix 做监控踩过的坑不少,报错也见过一大堆,正好借这个机会整理一份实战向的报错集锦。这篇文章不打算把官方文档再抄一遍,而是挑那些在实际环境里反复出现、网上搜起来答案零零散散的问题,把排查链路完整走一遍。覆盖范围包括安装部署、Server 端状态异常、Agent 采集链路、监控项失效、钉钉告警联动,以及一些“看着没报错但其实很危险”的隐性故障。无论你是在搭建阶段还是日常维护阶段,或者准备 Zabbix 面试,这份东西应该都能派上用场。
1. 安装部署期的经典报错:从 replace_user 数据库权限说起
1.1 access denied for user 'replace_user'@'localhost' 的根源
先说说开头那个报错。replace_user不是真实存在的用户,而是官方安装文档或自动化脚本在生成 SQL 初始化语句时留下的占位符。你拿到手的是这种模板:
CREATE DATABASE zabbix CHARACTER SET utf8mb4 COLLATE utf8mb4_bin; CREATE USER 'replace_user'@'localhost' IDENTIFIED BY 'replace_password'; GRANT ALL PRIVILEGES ON zabbix.* TO 'replace_user'@'localhost';如果直接复制执行,MySQL 里确实会生成一个叫replace_user的账号。但更常见的情况是,安装脚本只把数据库建好了,后面配置/etc/zabbix/web/zabbix.conf.php时却填了你自己定义的用户名和密码,两边对不上,自然就报access denied。
排查时可以分三步走:
- 先确认数据库端到底有没有这个账号:
SELECT user, host FROM mysql.user WHERE user IN ('zabbix', 'replace_user'); - 再确认连接命令走的是哪个 host,
localhost和127.0.0.1在 MySQL 授权里是两个不同实体,有时候账号只授权了localhost,但 Zabbix 前端或 Server 用127.0.0.1去连,也会报同样错误。 - 最后检查密码里有没有特殊字符。如果密码里有
$、#、%这类字符,在命令行里又没做转义,复制到配置文件时可能已经被 shell 处理过一遍,实际存入的密码根本不是你以为的那个。
提示:初始化数据库前,先把 SQL 里的占位符全部全局替换掉,不要手动一个字段一个字段改。用
sed -i 's/replace_user/zabbix/g; s/replace_password/你的强密码/g' db.sql这种命令一次性处理,能避免很多低级问题。
1.2 Rocky Linux 9.8 装 Zabbix 7.0 时三个容易忽略的依赖点
Zabbix 7.0 在 Rocky Linux 9.8 上安装时,官方仓库的 RPM 包会自动拉依赖,但有几个点经常被忽略,导致前端页面能打开、却提示缺模块。
第一个是 PHP 版本。Rocky Linux 9 默认带的是 PHP 8.0,Zabbix 7.0 要求 PHP 8.0 以上,理论上满足。但如果之前手动装过其他版本的 PHP,或者启用了多个软件仓库,版本可能被顶到 8.2/8.3,而 Zabbix 前端对 PHP 版本有兼容性校验。遇到这种情况,先看前端安装页顶部警告,再执行php -v确认版本,必要时通过dnf module reset php和dnf module enable php:8.0把版本固定在系统默认版本上。
第二个是 PHP 扩展。Zabbix 7.0 前端至少需要php-bcmath、php-mbstring、php-gd、php-xml、php-ldap。很多人装完只装了php和php-fpm,打开安装页看到一大堆红色警告,其中一个常见的就是PHP ldap module missing。没有 LDAP 需求的话可以不用管,但如果前端要求必须通过,就执行:
dnf install -y php-bcmath php-mbstring php-gd php-xml php-ldap第三个是 SELinux。Rocky Linux 默认 enforcing 模式,安装完 Zabbix Server 启动没问题,但前端访问数据库或写入临时文件时会被拦住。最直接的判断方法是查看/var/log/audit/audit.log里有没有denied关键字,确认是 SELinux 拦截后,再用setsebool -P httpd_can_network_connect 1放通 Apache 到数据库或后端的网络连接。不要一上来就setenforce 0,那是图省事,后面重启一次就忘了,监控就断了。
1.3 前端安装页报 Unable to connect to database 的处理
如果你已经完成向导,但前端后续打开时报Database error: Unable to connect to database,这不是 MySQL 服务挂了,就是 Zabbix 前端配置文件里的数据库凭据不对。Zabbix 前端的数据库连接配置放在/etc/zabbix/web/zabbix.conf.php,里面只有三样东西:数据库主机、数据库名、账号密码。
我的排查习惯是先看数据库进程:
systemctl status mariadb mysqladmin -u zabbix -p ping如果数据库没问题,就对比配置文件里的$DB['USER']、$DB['PASSWORD']和 MySQL 里实际账号是否一致。这里有个隐蔽坑:Zabbix Server 本身也读一套数据库配置,路径是/etc/zabbix/zabbix_server.conf,里面也有DBUser和DBPassword。前端连接用的是一套,Server 进程用的又是另一套,如果只改了前端配置,Server 能连上数据库但前端连不上,或者反过来,报错信息会完全不同。遇到“数据库连接异常”类报错,两边的配置文件都要检查一遍。
2. “Zabbix server is not running”:不要急着无脑重启
2.1 这个红色提示可能来自哪一层
前端页面顶部飘红提示Zabbix server is not running,是运维圈里最常见也最容易误判的告警。很多人一看这个提示,直接systemctl restart zabbix-server,重启完暂时绿了,过几天又飘红,然后陷入死循环。
实际情况是,这个提示说的是“前端拿不到 Server 运行状态”,而不是“进程一定挂了”。Zabbix Server 每秒钟都会向内部的共享内存写入状态数据,前端通过一个叫status的接口读取。如果 Server 进程活着,但在大量处理数据、线程卡死、数据库连接阻塞,状态数据写不进共享内存,前端一样会判断为未运行。
先看看进程到底在不在:
ps -ef | grep zabbix_server ss -lntp | grep 10051进程还在的话,再去日志里找根因,日志路径默认在/var/log/zabbix/zabbix_server.log。我遇到过很多次的情况是:日志里持续出现History cache is 80% full或者History cache is full,过一阵子前端就飘红了。
2.2 缓存与数据库连接耗尽:最容易被忽视的淹死现场
Zabbix Server 有几块共享内存缓存:CacheSize(配置和数据缓存)、HistoryCacheSize(历史数据缓存)、TrendCacheSize(趋势数据缓存)。这些值在zabbix_server.conf里默认都比较保守,新装好直接跑几百台设备、几万个监控项时,缓存很容易打满。
缓存打满后,Server 处理采集数据的效率断崖式下降,历史数据来不及写入数据库,队列越积越长,最终前端状态读取超时,就报not running。
遇到这种情况,先查日志确认是不是缓存告警,再对监控项规模做个估算。我习惯按 1 个监控项约占用 50 到 100 字节共享内存来粗算,50000 个监控项的话HistoryCacheSize配 128M 以上比较稳。调参公式是:
# 缓存大小配置示例 CacheSize=256M HistoryCacheSize=128M TrendCacheSize=64M同时还要检查 MySQL 连接数。Zabbix Server 默认数据库连接池大小是DBMaxEscalation、HistoryCacheSize这些参数控制不了连接池,连接池由StartPollers、StartTrappers、StartDiscoverers这些进程数决定。进程越多,数据库连接就越多。如果 MySQL 的max_connections不够,Server 日志会报Cannot connect to database或Too many connections。这时不是盲目调小进程数,而是先看show processlist;里 Zabbix 用户建立了多少连接,把max_connections调整到合理值,或者减少不必要的 poller 进程。
注意:调缓存和连接池参数不是越大越好。共享内存太大会导致系统内存吃紧,进程数太多会让 MySQL 负载飙升。生产环境改完一个参数,至少要观察一个采集周期(通常 1 到 5 分钟)。
2.3 poller 不够用的量化判断
StartPollers是负责主动采集监控项的进程数。当队列里大量监控项出现延迟,而缓存和数据库都健康时,很可能是 poller 不够了。
Zabbix 前端“报表”菜单里有一个队列页面,能直接看到某个 Zabbix Server 上延迟超过多少秒的监控项数量。如果看到堆积项持续增长,先把StartPollers从默认的 5 调到 10 或 20,观察队列是否回落。
但这里有一个容易混淆的场景:很多监控项变慢,不是 poller 不够,而是某个目标主机响应慢,一个 poller 被卡在那里等超时。比如有个网络设备 SNMP 超时时间设置成 30 秒,这 30 秒内这个 poller 就废了。所以看到队列堆积,先按监控项排序,找出那几个延迟最高的目标,优先优化超时时间和重试次数,而不是一上来就堆 poller。
2.4 日志里常见却容易被误读的进程记录
Zabbix Server 异常退出后,会在/var/run/zabbix/目录下残留 pid 文件。重新启动时,如果旧 pid 文件还在,而那个 pid 已经不复存在,服务管理器可能会误判“服务已经在运行”。你在systemctl start zabbix-server时看到失败,但ps里又找不到进程,大概率就是 pid 文件脏了。
处理方式很简单:
rm -f /var/run/zabbix/zabbix_server.pid systemctl start zabbix-server这种问题常出现在强制 kill、虚拟机快照回滚、磁盘写满等异常场景之后。磁盘写满这个因素特别容易被忽略,zabbix_server.log和 MySQL 的 binlog 都在同一块磁盘上,磁盘满了,Server 启动时无法写入日志,就会启动失败,但报错信息可能非常隐晦。先df -h看一眼磁盘,再排查其他,能省不少时间。
3. Agent 采集链路报错:连不上、空响应、数据悬空
3.1 Get value from agent failed——从 zabbix_get 排查端口和配置
新主机加入 Zabbix 后,监控项数据一直是灰色,点开“最新数据”看到报错Get value from agent failed: cannot connect to [192.0.2.10]:10050。这种问题,我一般直接拿zabbix_get在 Server 端做连通性测试:
zabbix_get -s 192.0.2.10 -p 10050 -k system.cpu.load[all,avg1]如果返回Connection refused,说明 Agent 端口没监听或者防火墙挡了。在 Agent 主机上查:
ss -lntp | grep 10050端口没监听,先确认 Agent 进程是否启动,再确认配置文件中没有写ListenIP=127.0.0.1。有人为了安全把 Agent 绑定在回环地址上,结果 Server 端从外网来取数据怎么都连不上。
如果端口正常,但zabbix_get返回failed to accept an incoming connection: connection from ... rejected,那问题基本出在 Agent 配置文件里的Server=指令。这个指令本意是“允许哪些 IP 来主动拉取数据”,但很多人把它和ServerActive(Agent 往哪台 Server 推送数据)搞混。Server要填 Zabbix Server 的 IP,不能填 Agent 自己的 IP。填反了或者留空,就会看到“连接被拒绝”的报错。
3.2 received empty response from zabbix_agentd:主动模式下的常见问题
zabbix_get能连上端口,但返回received empty response from zabbix_agentd,这个报错在从被动模式切到主动模式时特别常见。
Zabbix 7.0 的 Agent 默认配置是主动模式,配置文件里有ServerActive=指定 Server 地址。如果是主动模式,Agent 会主动连 Server 的 10051 端口,Server 端监控项的“类型”也要选成“Zabbix 主动式”。如果 Server 端监控项类型还停留在“Zabbix agent”(被动式),但 Agent 端主动模式把数据发到了 Server 的 trapper 端口,两边没对上,就可能出现空响应。
还有一种情况是 Agent 端执行某个自定义 key 时,执行时间超过了Timeout。Zabbix 7.0 默认Timeout=3,也就是 Agent 执行脚本最多等 3 秒,超过后就杀掉脚本,返回空响应。我跑过一些比较重的脚本,比如从日志文件里统计耗时的命令,经常要十几秒,这时候必须把 Agent 的 Timeout 调到 10 甚至 30,同时把监控项的“超时时间”也调高,两边都要改,只改一边没用。
3.3 时间不同步带来的“采集到了但没数据”
Zabbix 监控项数据有时图上有大段缺口,zabbix_get手动执行却一切正常,这就是典型的时间不同步问题。Agent 端采集数据后会带一个本机时间戳,Server 端在写入历史数据时如果发现时间戳偏离当前时间太多,会直接丢弃或标记为无效数据。换句话说,你“采到了”,但 Server 不认。
排查方法很简单:在 Server 和 Agent 上分别执行date,看看两个时间差多少。超过个位数秒就有问题。统一用 NTP 或 chrony 同步时间:
systemctl enable --now chronyd chronyc sources另外,Zabbix 前端、Server、数据库最好都在一个时区,不然图形时间轴和告警时间看着会非常别扭。
3.4 Agent 主动连 Server 时容易漏掉的防火墙放行
被动模式下只有 Server 连接 Agent 的 10050 端口,但主动模式下是 Agent 连接 Server 的 10051 端口。很多环境只在防火墙放行了 10050,完全没管 10051,结果主机部分监控项能通,主动类型监控项全部失败。
我自己踩过这个坑:新主机加进来,zabbix_get测通所有 key,但在“最新数据”里看到主动模式监控项半天没反应,日志里是 Agent 一直在重连 Server 的 10051。放行端口后,10 秒内数据全出来了。
有时是云安全组或公司防火墙策略的问题,记住一个原则:被动模式放行 Server 到 Agent 的 10050,主动模式放行 Agent 到 Server 的 10051,代理场景还要放行 10051 之外的 trapper 端口。
4. Item became not supported:监控项失效的归类排查
4.1 找到 not supported 的第一现场
Item became not supported是 Zabbix 里最磨人的一类报错,因为它把所有失败原因都藏在“不支持”这个笼统的状态里。排查第一步永远是先拿到具体的错误原因,路径有三个:
- “监测 -> 最新数据”里按状态筛选“不支持”,点开监控项详情,会直接给出错误信息。这是最直观的入口。
- “配置 -> 主机 -> 监控项”里点开对应监控项,最下面的“信息”字段也会显示失败原因。
- Zabbix Server 日志里搜
not supported关键字,能看到最近更新为不支持状态的上下文。
大多数时候,错误信息里已经写清楚了原因,比如Cannot read data from filesystem、Timeout while executing a shell script、No Such Instance currently exists at this OID。真正难的是拿到错误信息之后怎么处理。
4.2 key、参数、权限、超时四类原因实例
我按实际遇到的概率,把这四类排了个顺序:
Key 写错。比如磁盘空间监控要用vfs.fs.size[/,free],有人会写成vfs.fs.size[free],或者网卡流量把net.if.in[eth0]写成net.if.in[0]。这类错误往往在监控项创建后立刻变成 not supported。
参数类型不对。Zabbix 的 key 参数是有类型的,有的参数要字符串,有的要数字,有的要是带引号的宏。自定义脚本类的 key 更明显,比如 Agent 端脚本里$1取的是字符串,但监控项里传了整数,脚本解析直接空指针。
权限不足。Agent 默认以zabbix用户运行,很多系统文件或脚本它没有权限读取。比如监控/var/log/messages里的关键字,zabbix 用户对/var/log目录往往没有读取权限。此时要么把 zabbix 用户加入相应组,要么用sudo配合visudo配置白名单。注意 sudo 这里有个坑:Zabbix 执行脚本时没有终端,默认Defaults requiretty会拦掉,需要在 sudoers 里加上Defaults:zabbix !requiretty。
超时。Agent 端脚本执行超过Timeout,Server 端 HTTP agent 或 SNMP 请求超过设定的超时时间。这类报错信息里通常带Timeout while executing a shell script。解决办法是调大 Agent 的 Timeout 和对应监控项的超时时间,同时优化脚本本身,不要指望靠无限调大参数过日子。
4.3 LLD 后来发现为空的排查细节
自动发现规则(LLD)报 not supported 或者“发现结果为空”,问题通常集中在三个位置:发现的 key 在目标上能不能拿到数据、返回的 JSON 结构是不是 LLD 期望的格式、宏变量是否引用一致。
Zabbix 7.0 里很多模板自带的发现规则写的是诸如vfs.fs.discovery这种内置 key,一般没什么问题。但自定义脚本做发现时,脚本打印的内容必须是 JSON 数组,比如:
[{"{#IFNAME}":"eth0","{#IFTYPE}":1},{"{#IFNAME}":"eth1","{#IFTYPE}":1}]如果你在 LLD 规则宏里写的引用名和脚本返回的键名不一致,比如脚本返回{#IFNAME},但规则里写的是{#IFACE},那生成出来的监控项 key 就会带上空的宏值,直接 not supported。这个查起来特别费劲,因为它不报第一现场的错,你看到的是“宏解析失败”或生成出的 key 是net.if.in[]这种空参数。
提示:自定义 LLD 时,脚本先在 Agent 端手动跑一遍,确认输出 JSON,再放到监控项里测。不要直接在 Server 端反复试,浪费的时间够写十个脚本。
4.4 值映射与单位导致的“有值却是错误值”
有些监控项状态是支持(supported)的,但数据明显不对。最典型的是网络流量单位混淆:Linux 下/proc/net/dev拿到的字节数,Zabbix 内置模板一般会帮你做单位换算,但自定义监控项时经常忘掉“B/s”和“bit/s”的 8 倍关系。监控项单位写成B/s,数据是换算过的;不写单位,图形里数字看起来就大得离谱。
值映射问题则容易出在告警上。很多厂商或自定义模板返回的是数字状态码,比如 0 表示正常,1 表示异常,但没配值映射,告警消息里显示“1”,不显示“异常”,值班的人还得翻文档。这个不算报错,但属于监控项配置不完整。把这些数据和模板导入后,一定要顺手检查值映射和单位,尤其从第三方网上下载的模板,几乎每个都要改一遍。
5. 告警联动报错:钉钉通道悄悄挂了之后
5.1 从 zabbix_server.log 中追查无法发送告警
Zabbix 7.0 联动钉钉用得非常多,但告警通道出问题有个特点:它不会把监控项打成 not supported,也不会让 Server 进程崩溃,只会悄无声息地在告警动作里留下一条失败记录。很多人是在值班群里发现“为什么这个告警没收到”时才开始排查。
第一步看 Zabbix Server 日志里有没有和报警相关的错误信息:
grep -i "alert" /var/log/zabbix/zabbix_server.log | tail -50常见报错有cannot send alert notification、No media defined for user、Script execution failed。这些信息能帮你确定问题出在哪个环节。
5.2 钉钉机器人的加签与关键词:在 Zabbix 7.0 里的收发验证
Zabbix 7.0 内置钉钉告警媒介,也可以在告警动作里配置自定义脚本。用内置 Webhook 时,要确认钉钉机器人安全设置里选的是“自定义关键词”还是“加签”。如果选“加签”,Webhook 配置里需要把加签密钥填进去;如果选“自定义关键词”,告警消息正文必须包含关键词,否则钉钉会直接拒收。
我遇到过一种情况:脚本测试手动跑能发出消息,但 Zabbix 触发告警时发不出去。原因就是脚本从 Zabbix 读取的告警消息里不含关键词,钉钉 API 返回keywords not in content。解决方式是在消息模板里加上关键词,或者在钉钉机器人安全设置里改用“加签”方式,避免关键词匹配问题。
5.3 脚本排直通道的技巧
如果你的环境用自定义脚本方式发钉钉,建议先把脚本单独拿出来,手动以zabbix用户执行一遍:
sudo -u zabbix /usr/lib/zabbix/alertscripts/dingding.sh "test message"脚本能跑通,再看 Zabbix 告警动作里写的脚本参数对不对。Zabbix 调用告警脚本时,会按顺序传入三个参数:收件人地址、消息主题、消息内容。不同版本的 Zabbix 对这三个参数的传递方式略有差异,如果脚本里写死了参数位置,很容易错位。
另外,脚本日志一定要留。
echo "$(date) $1 $2 $3" >> /tmp/dingding_alert.log这个习惯能帮你省掉大量“到底执行了没有”的猜疑。我见过很多次告警脚本执行失败,就是因为 Python 脚本里 import 了系统环境里不存在的模块,而手动以 root 执行时用的又是另一个 Python 环境,验证通过,实际跑起来却报错。
6. 那些看起来没报错,其实很要命的隐性故障
6.1 历史数据表膨胀与 housekeeper 跟进
Zabbix 运行一年后,即使页面不报错,也会慢慢变卡。最常见的原因是历史数据表膨胀。Zabbix 默认由 housekeeper 定期清理过期数据,但这个机制有个硬伤:如果数据量太大,清理速度跟不上写入速度,表会越撑越大,MySQL 的 IO 持续拉高,最终影响整个 Server 性能。
排查时看 MySQL 里最大的表:
SELECT table_name, ROUND(((data_length + index_length) / 1024 / 1024), 2) AS size_mb FROM information_schema.tables WHERE table_schema = 'zabbix' ORDER BY size_mb DESC LIMIT 10;如果history或history_uint表特别大,就要考虑调整监控项的历史数据保留时间(默认 90 天对很多场景过长了),或者干脆做分区表。Zabbix 7.0 对分区表的支持已经比较完善,但分区操作涉及数据库结构调整,务必在维护窗口操作。
还有一个经常被忽略的点:housekeeper 本身的进程数。zabbix_server.conf里StartHousekeepers默认是 1,数据量大的环境可以调到 2 或 4,清理速度会有明显提升。
6.2 时区导致的告警时间偏差
这是一个“看着没报错但实际很坑”的问题。如果 Zabbix Server 所在系统的时区是 UTC,而你的预期是北京时间,那告警消息里的所有时间都比实际慢 8 小时。告警内容是正常的,数据也是正常的,但值班人员看到时间不对,很容易误判。
处理方式很简单:系统层面统一成 Asia/Shanghai,同时在 PHP-FPM 和 Zabbix 前端配置里也要把时区设置成一致。前端时区在/etc/zabbix/web/zabbix.conf.php里有$DB['TIMEZONE'],要和系统时区保持同步。顺手把 MySQL 的会话时区也看一下,因为写入历史数据时某些聚合函数会依赖数据库时区。
6.3 新设备没有自动纳入监控:LLD 间隔与模板覆盖问题
监控跑得越久,越容易在“新增主机”这个动作上翻车。很多人手动添加主机后,只填了 IP 和模板就完事,忘了模板里的自动发现规则有执行周期。Zabbix 7.0 模板里的 LLD 规则默认间隔是 1 小时,也就是说新加的设备要等最多 1 小时才会被发现项填充完毕。如果刚加完机就去看监控项,发现全是空的,就会误以为“模板坏了”或者“报错了”。
还有更隐蔽的:如果你在主机关联了多个模板,两个模板里有同名监控项或同名 LLD 规则,后加载的模板会覆盖先加载的配置。这在导入第三方模板时特别常见。Zabbix 不会给你弹任何报错,但数据表现会非常奇怪。排查方法是逐个模板检查主机上的监控项来源,看看同名项到底来自哪个模板。
6.4 Agent 端运行时间过长导致的高水位假象
有时候图上有数据,但数据曲线忽高忽低,怀疑监控项出错。按我的经验,很多“假报错”其实是 Agent 端脚本执行时间不确定导致采样间隔抖动。比如一个自定义脚本监控某个服务的并发连接数,脚本本身执行需要 1 到 5 秒不定,监控项设置为 30 秒采样一次,但脚本执行时间不稳定,导致每次取数间隔不是均匀的 30 秒,画出来的曲线就有毛刺。
解决思路是调整采集间隔,或者把脚本里不必要的耗时操作缓存起来。Zabbix 有数据预处理功能,可以在 Server 端做“改变采样频率”或“简单变化率”等处理,但最根本的还是让 Agent 端返回数据的时间稳定。这个不解决,后续写告警阈值很容易被毛刺误触发。
一些实操感受
这些年做监控排障,我最大的感受是:Zabbix 报错本身不可怕,可怕的是报错来之前没有任何上下文。所以我现在每部署一套 Zabbix,都会先做一个环境信息核对表,把 Agent 的Server=和ServerActive=、Server 的CacheSize、数据库连接池、防火墙端口、时间同步状态全部记下来。排查问题时先核对这张表,把环境变量排除掉,再去看具体报错,效率会高很多。
如果你刚接触 Zabbix,也不用被上面这些报错吓到。大部分问题都是配置层面的,只要理解了被动与主动采集的区别、键值参数的格式要求、日志路径,排查思路就能建立起来。下次再遇到 not supported 或者 server is not running,别急着重启,先去日志里翻 10 分钟,答案大概率就在那里。