1. 为什么这套环境必须走离线路线内网机房里那台 CentOS-7 顶着两台生产库跑了大半年一直没人敢动它直到采购的短信通道终于批下来领导甩过来一句把监控和短信报警搞上。问题是这台机器所在网段物理隔离连不上任何公网源yum 敲下去就是一堆Could not resolve host。这种场景下要做 Zabbix 离线安装部署再叠加上自定义监控和短信报警本质上不是装个软件而是要在两个世界之间搬一整套依赖关系——一台能联网的机器负责把所有 rpm 抓齐一台离线机器负责还原出完整可用的运行环境。我见过太多人在这件事上栽跟头在联网机器上yum install zabbix-server-mysql装完就完事然后把/etc/zabbix打包拷过去结果离线机上一启动全是libmysqlclient.so.18: cannot open shared object file或者 PHP 版本不够导致前端白屏。根子在于 Zabbix 不是一个单体程序它是一组组件的组合Server、Agent、Web 前端PHP Apache/Nginx、数据库MySQL/MariaDB 或 PostgreSQL、以及一堆运行时库。离线部署的难点从来不是 Zabbix 本身而是依赖闭包到底有多深。这篇内容我会按真实实施顺序走一遍先把依赖账算清楚再落地 Server 和前端然后拆自定义监控的设计逻辑最后把短信报警这条链路从触发器一直打到运营商网关。适合两类人看——一是第一次在隔离网段部署监控系统的运维二是已经从别的通道邮件、钉钉群机器人切过来、想把报警收口到自己短信平台的人。中间会穿插几个我实际踩过的坑包括那个在搜索里高频出现的access denied for user replace_userlocalhost。1.1 离线环境的真实约束与踩坑预判先明确约束这决定了后面所有技术选型。隔离网段通常有三个硬条件没有外网 DNS 解析、没有可用的 yum 仓库、不允许开放到办公网的随机出向端口。第三条经常被忽略——你要是打算让 Zabbix Server 通过公网 API 发短信结果防火墙只放行了 80/443 到某些白名单 IP那后期报警发不出去排查起来能耗掉你一个通宵。所以第一步不是装东西是画一张网络与端口清单方向源目的端口用途双向Zabbix ServerAgent10050被动模式取数单向AgentZabbix Server10051主动模式上报单向Zabbix Server短信网关 API8080/HTTPS发短信单向管理员 PCZabbix Web80前端访问单向Zabbix Server数据库3306本地连接这张表填不满后面全是空转。我遇到过一次Agent 装好了、zabbix_get在服务器本地测通但从 Server 侧发起就是 timeout查了两小时才发现中间隔了一道策略路由10050 被静默丢弃连 reject 都不给。提示离线环境的端口策略请提前用nc -zv或telnet从两端各测一次不要只看配置文件。另一个常被低估的约束是时间同步。Zabbix 的事件、触发器、趋势数据全依赖时间戳。离线机器没有 NTP 源如果两台机器差了十几分钟你会看到触发器刚触发就立刻恢复历史数据画出来的曲线是断的。我的做法是在内网找一台能对时的机器做本地时间源或者干脆用chronyd指向内网 IP。1.2 版本组合怎么定CentOS 7 上的取舍逻辑CentOS 7 自带的 PHP 是 5.4这个数字是整件事的关键。Zabbix 各个大版本对 PHP 的最低要求差别很大Zabbix 版本最低 PHPCentOS 7 默认 PHP 5.4 是否够用离线部署难度4.0 LTS5.4够用低5.0 LTS7.2不够需 SCL 或第三方源中6.0 LTS7.2.5不够中高6.4 及以上7.4不够高我最后选的是Zabbix 5.0 LTS rh-php72SCL 软件集合。理由很实际5.0 是长期支持版本前端功能完整而 rh-php72 是官方软件集合rpm 包齐全离线抓取时依赖闭包可控。如果你不介意用老版本Zabbix 4.0 在 CentOS 7 上确实是最省事的——默认 PHP 就能跑离线包数量少一大截。但 4.0 已停止维护新增功能和安全补丁都没有内网用可以我不太推荐新项目再上。数据库这边CentOS 7 自带 MariaDB 5.5。它能跑 Zabbix 5.0但有两个问题一是字符集处理比较糙建库时必须显式指定utf8而不是utf8mb4Zabbix 5.0 的 schema 对utf8mb4有兼容问题二是 5.5 的 InnoDB 性能一般监控主机数上到三位数后 housekeeping 会明显拖慢。如果条件允许MariaDB 10.3 或 10.5 是更好的选择离线包也就多十几个。注意不要在同一套环境里混用 MySQL 与 MariaDB 的 rpm库文件路径会打架libmysqlclient与libmariadb冲突后 zabbix_server 直接起不来。1.3 把本地光盘变成可用的 yum 仓库离线环境的第一件事是恢复一个最小可用的软件源。大多数机房都会有 CentOS-7 的安装 ISO这个 ISO 里的Packages目录就是天然的 rpm 仓库。mkdir -p /mnt/cdrom mount -o loop /opt/CentOS-7-x86_64-DVD-2009.iso /mnt/cdrom cat /etc/yum.repos.d/local-dvd.repo EOF [dvd-base] nameCentOS-7 DVD Base baseurlfile:///mnt/cdrom enabled1 gpgcheck0 EOF yum clean all yum repolistgpgcheck0是离线环境的常规妥协因为 ISO 的 GPG key 导入有时会卡在交互确认上脚本化部署时容易断。如果你对包完整性有要求可以提前把 RPM-GPG-KEY-CentOS-7 导进去把gpgcheck打开。DVD 源覆盖的是系统基础包Zabbix、rh-php72 这些第三方包不在里面。我的做法是在离线机上再建一个自定义仓库目录把从联网机器抓下来的所有 rpm 集中放进去mkdir -p /opt/localrepo cp /opt/pkgs/*.rpm /opt/localrepo/ createrepo /opt/localrepo cat /etc/yum.repos.d/local-ext.repo EOF [local-ext] nameLocal Extra Packages baseurlfile:///opt/localrepo enabled1 gpgcheck0 EOFcreaterepo这个命令本身也需要 rpm如果不方便装有个更土但有效的办法用yum --disablerepo* localinstall /opt/pkgs/*.rpm。yum 在处理localinstall时会把命令行里给的所有 rpm 当成一个集合来解依赖只要依赖闭环在文件列表里它就能一次装完。我在一台只有 12 个包的场景里用过这个办法比装 createrepo 还快。2. 离线包清单的梳理依赖清单扒得不干净后面就是无穷无尽的补包。我的思路是在联网机器上先做一次同款环境预演把系统版本、内核小版本、已装软件都对齐然后再抓包。这一步很多人省掉结果就是离线机上 glibc 版本对不上rpm -ivh报一堆Failed dependencies。2.1 用 repotrack 把依赖树一次抓全yumdownloader --resolve是我最早用的方式但它有个毛病只解析目标包的直接依赖间接依赖依赖的依赖经常漏。后来换成repotrack配合 yum-utils抓得干净很多。# 在联网机器上先装 Zabbix 官方源 rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm # 再装 SCL 源rh-php72 yum install -y centos-release-scl # 抓取 Zabbix 相关全部依赖 mkdir -p /opt/pkgs repotrack -a x86_64 -p /opt/pkgs \ zabbix-server-mysql \ zabbix-agent \ zabbix-get \ zabbix-web-mysql \ zabbix-web \ zabbix-sql-scripts # 抓取 rh-php72 与 Web 服务相关依赖 repotrack -a x86_64 -p /opt/pkgs \ rh-php72 \ rh-php72-php-fpm \ rh-php72-php-mysqlnd \ rh-php72-php-bcmath \ rh-php72-php-gd \ rh-php72-php-mbstring \ rh-php72-php-xml \ rh-php72-php-ldap \ httpd \ mariadb-server \ mariadb这里有个容易漏的点Zabbix Web 前端需要一堆 PHP 扩展bcmath、gd、mbstring、xml、ldap、mysqli5.0 用 mysqlnd缺一不可。少一个前端安装向导第一步就报PHP 扩展未满足而且报错不会告诉你是哪个扩展版本不对只会红字一片。抓完之后建议核对一下包总数和体积ls /opt/pkgs/*.rpm | wc -l du -sh /opt/pkgs我这套环境抓下来是 180 多个包约 400MB。如果数量明显偏少八成是 repotrack 没吃到某个源回去检查yum repolist。2.2 打包回传与校验传输这一步别偷懒。我一般这么打cd /opt tar czf zabbix-offline-$(date %Y%m%d).tar.gz pkgs localrepo md5sum zabbix-offline-*.tar.gz zabbix-offline.md5回传之后先校验 md5 再解压。听起来像废话但我在一次用隔离摆渡机的传输里遇到过文件被截断解压报unexpected EOF浪费了半天。离线环境中转一次链路长校验成本极低收益极高。解压后的目录结构建议固定下来后面出问题时好定位/opt/zabbix-deploy/ ├── pkgs/ # 所有 rpm ├── install.sh # 安装脚本 ├── conf/ # 配置文件模板 ├── scripts/ # 自定义监控脚本 └── alertscripts/ # 报警脚本2.3 数据库与 PHP 的版本红线装之前再确认一次四条红线任何一条不满足后面都是白干组件最低要求检查命令PHP7.2.0php -vMariaDB/MySQL5.5 / 5.6mysql --version内存Server 建议 4GBfree -m磁盘/var/lib/mysql 预留 50GBdf -h内存这条特别值得说。Zabbix Server 默认启动 20 多种 poller 进程加上 PHP-FPM 和 MariaDB2GB 内存的机器跑起来会频繁 swap表现出来是前端点一下卡三秒。我一般给 Zabbix 单机部署至少配 8GB其中 InnoDB buffer pool 给 4GB剩下的留给 Server 和 Web。至于 PHP 版本CentOS 7 原生是 5.4一定要用 SCL 切换到 7.2。SCL 的特点是不覆盖系统 PHP而是装在/opt/rh/rh-php72/下通过scl enable rh-php72 bash进入环境。这带来一个额外好处系统自带的 yum 依赖不会被破坏风险很低。提示SCL 环境下的 php-fpm 配置文件在/etc/opt/rh/rh-php72/php-fpm.d/不是/etc/php-fpm.d/改错地方是新手最常见的坑。3. Zabbix Server 与 Web 前端的离线落地到这一步rpm 都在本地目录里了可以正式安装。我习惯把整个过程写成一个脚本因为离线环境经常要重复部署到多台机器上。3.1 一次性安装全部 rpm 与那个经典报错cd /opt/zabbix-deploy/pkgs yum --disablerepo* localinstall *.rpm -y注意--disablerepo*这是为了让 yum 不去找那些根本连不上的外网源避免长时间卡在正在尝试其他镜像。如果本地仓库已经配好了直接yum install更规范。装完之后立刻检查服务状态systemctl enable --now mariadb systemctl enable --now rh-php72-php-fpm systemctl enable --now httpd systemctl enable zabbix-server systemctl enable zabbix-agent然后是数据库初始化。这一步就是access denied for user replace_userlocalhost的高发区。先说建库CREATE DATABASE zabbix CHARACTER SET utf8 COLLATE utf8_bin; CREATE USER zabbixlocalhost IDENTIFIED BY Zbx2024Secret; GRANT ALL PRIVILEGES ON zabbix.* TO zabbixlocalhost; FLUSH PRIVILEGES;关于那个replace_user报错我总结出四种成因按出现频率排序第一种配置文件里的占位符压根没替换。Zabbix 的官方文档和不少模板里DBUserreplace_user、DBPasswordreplace_password就是字面占位符有人复制粘贴的时候只改了密码用户名忘了改。Server 拿着一串replace_user去连库当然是 access denied。这个问题在zabbix_server.conf、/etc/zabbix/web/zabbix.conf.php、以及某些 Web 服务器配置里都可能出现建议全局搜一遍grep -rn replace_ /etc/zabbix/ /etc/httpd/ 2/dev/null第二种授权主机名不匹配。MySQL 的用户是userhost二元组zabbixlocalhost和zabbix127.0.0.1是两个完全不同的账号。如果 Server 配置里写的是DBHost127.0.0.1但授权只给了localhost连接就会被拒。要么把配置改成localhost走 socket更快要么补一条zabbix127.0.0.1的授权。第三种认证插件问题。如果你用的是 MySQL 8.0默认认证插件是caching_sha2_passwordZabbix 5.0 编译时链接的客户端库可能不支持。解决方法是指定 mysql_native_passwordALTER USER zabbixlocalhost IDENTIFIED WITH mysql_native_password BY Zbx2024Secret;第四种密码里的特殊字符被配置文件解析掉了。zabbix_server.conf对#开头的值有特殊处理如果密码以#或;开头会被当注释。另外空格也会有问题。我现在的习惯是密码只用大小写字母、数字、下划线和短横线长度 16 位以上既够强又不会踩解析坑。注意改完密码后数据库里的账号密码和zabbix_server.conf、zabbix.conf.php三处必须同时更新漏一处就复现同一个报错。3.2 导入初始 Schema 与字符集处理Zabbix 5.0 的初始数据要单独导入这一步在大机器上可能要跑 3 到 5 分钟别以为是卡死了。zcat /usr/share/doc/zabbix-sql-scripts/mysql/create.sql.gz | mysql -uzabbix -p zabbix导入前有两个参数必须调否则导入过程会慢到无法忍受# /etc/my.cnf [mysqld] innodb_file_per_table 1 innodb_buffer_pool_size 4G innodb_flush_log_at_trx_commit 2 innodb_log_file_size 512Minnodb_buffer_pool_size给物理内存的 50%~70% 比较合适单机部署时我一般直接给一半。innodb_flush_log_at_trx_commit设成 2 是监控场景的常规妥协牺牲一点极端断电场景下的事务安全换取写入性能对 Zabbix 这种写入密集又不太在意丢最后几秒数据的场景很合适。导入过程中如果报Specified key was too long; max key length is 767 bytes说明你的innodb_large_prefix没开或者建库时字符集选错了。Zabbix 5.0 必须用utf8三字节而非utf8mb4四字节后者会把索引撑爆。导入完成后验证一下表数量mysql -uzabbix -p -e SELECT COUNT(*) FROM information_schema.tables WHERE table_schemazabbix;正常应该是 150 张左右。数量明显偏少说明导入中途断了。3.3 Server 与 Agent 配置里真正需要改的参数zabbix_server.conf里默认值大部分能用但有这么几个必须动的ListenPort10051 DBHostlocalhost DBNamezabbix DBUserzabbix DBPasswordZbx2024Secret StartPollers20 StartTrappers10 StartPingers5 CacheSize64M HistoryCacheSize64M TrendCacheSize32M ValueCacheSize64M Timeout10 LogSlowQueries3000 AlertScriptsPath/usr/lib/zabbix/alertscriptsTimeout10值得单独说。默认是 3 秒对于本地执行的自定义监控脚本比如采集连接数、检查服务状态经常不够脚本稍微慢一点就变成ZBX_NOTSUPPORTED: Timeout while executing a shell script。我在采集网络设备 SNMP 数据时吃过这个亏把 Timeout 提到 10 后问题消失。但也不要无脑调大Timeout 越长单个 poller 卡住的时间越久整体吞吐会下降。Agent 的配置在/etc/zabbix/zabbix_agentd.confServer10.10.20.15 ServerActive10.10.20.15 Hostnameapp-node-01 Include/etc/zabbix/zabbix_agentd.d/*.conf Timeout10Hostname这一项必须和 Web 前端里新建主机的主机名称完全一致大小写敏感。主动模式下 Agent 上报的数据里带着这个 HostnameServer 靠它匹配主机。不一致的话Server 侧会报no active checks on server或者数据全落到未匹配里。3.4 Web 前端安装向导的卡点Apache 加 PHP-FPM 的组合在 SCL 环境下需要手工配一下。核心是让 Apache 把 PHP 请求转发给 rh-php72 的 fpm# /etc/httpd/conf.d/zabbix.conf Alias /zabbix /usr/share/zabbix VirtualHost *:80 DocumentRoot /usr/share/zabbix Directory /usr/share/zabbix Options FollowSymLinks AllowOverride None Require all granted /Directory /VirtualHost Directory /usr/share/zabbix/conf Require all denied /DirectoryPHP-FPM 侧的关键参数; /etc/opt/rh/rh-php72/php-fpm.d/zabbix.conf listen.acl_users apache,nginx php_value[max_execution_time] 300 php_value[memory_limit] 128M php_value[post_max_size] 16M php_value[upload_max_filesize] 2M php_value[max_input_time] 300 php_value[max_input_vars] 10000 php_value[date.timezone] Asia/Shanghaidate.timezone是必填项不填的话前端向导第一步直接报时区未设置。max_input_vars默认 1000在导入大模板或者编辑复杂触发器时会不够用直接拉到 10000。浏览器打开http://10.10.20.15/zabbix后跟着向导走数据库那一步填的信息必须和zabbix_server.conf完全一致。向导完成后会生成/etc/zabbix/web/zabbix.conf.php这个文件是前端连库的唯一凭据后面改密码也要同步改这里。提示如果向导页面打开是空白或者下载了一个 php 文件说明 Apache 没有把 php 交给 FPM 处理。检查mod_proxy_fcgi是否加载以及 FPM 的listen配置和 Apache 的ProxyPassMatch是否匹配。4. 自定义监控从写脚本到把数据画成图Zabbix 自带的模板覆盖了 CPU、内存、磁盘、网络这些通用指标但真正体现价值的往往是自定义监控——业务队列积压多少、某个进程还在不在、连接池里有多少空闲连接、某个接口的返回码是不是 200。这些东西没有现成模板必须自己写。4.1 UserParameter 的写法与权限坑自定义监控的入口是UserParameter。它本质上是一行键值对声明左边是监控项 key右边是执行命令。# /etc/zabbix/zabbix_agentd.d/custom.conf UserParametercustom.tcp.conn[*],/etc/zabbix/scripts/tcp_conn.sh $1 UserParametercustom.proc.alive[*],/etc/zabbix/scripts/proc_alive.sh $1 UserParametercustom.api.code[*],/etc/zabbix/scripts/api_check.sh $1配套脚本#!/bin/bash # /etc/zabbix/scripts/tcp_conn.sh case $1 in ESTABLISHED) ss -ant 2/dev/null | awk NR1{c[$1]} END{print c[ESTABLISHED]0} ;; TIME_WAIT) ss -ant 2/dev/null | awk NR1{c[$1]} END{print c[TIME_WAIT]0} ;; *) echo 0 ;; esac这里有几个经验点值得展开。第一脚本输出必须是一行一个数值。Zabbix 对数值型监控项有严格解析如果你的 awk 输出多行或者带了单位比如1234 个监控项会变成不支持的文本类型图形直接画不出来。我见过有人在脚本里echo 当前连接数: $N调试了半天才发现是这个原因。要带单位的用监控项的单位字段不要写进脚本输出。第二脚本得让 zabbix 用户能执行。Agent 默认以zabbix用户运行脚本和执行链路上所有命令都必须有这个权限chown zabbix:zabbix /etc/zabbix/scripts/*.sh chmod 750 /etc/zabbix/scripts/*.sh chmod 750 /etc/zabbix/scripts目录本身也要有 x 权限否则Permission denied会来得莫名其妙。第三SELinux 是隐藏杀手。CentOS 7 默认 SELinux 是 enforcing从/etc/zabbix/scripts/里执行脚本会被拦截而且报错非常隐晦——zabbix_get返回一个空值或者 ZBX_NOTSUPPORTED日志里只说执行失败。快速验证方法getenforce setenforce 0 # 重新测试 zabbix_get如果关掉 SELinux 就好了说明是标签问题。生产环境不建议直接关正规做法是给脚本目录打上正确的上下文semanage fcontext -a -t bin_t /etc/zabbix/scripts(/.*)? restorecon -Rv /etc/zabbix/scripts第四UserParameter 里用[*]声明参数。custom.tcp.conn[*]表示这个 key 可以带任意个参数调用时写成custom.tcp.conn[ESTABLISHED]。脚本里用$1、$2接收。注意不是$1而是直接在命令末尾写$1因为 Zabbix 会在执行前做替换。改完配置后重启 agentsystemctl restart zabbix-agentZabbix 5.0 虽然支持zabbix_agentd -R userparameter_reload做热加载但它对Include文件的新增/删除不生效只改已有文件里的键值时才有用。稳妥起见还是重启。4.2 主动模式与被动模式怎么选这是自定义监控里最容易被忽略的设计决策。两种模式的区别很实际维度被动模式主动模式连接方向Server → Agent:10050Agent → Server:10051触发时机Server 按间隔轮询Agent 按 Server 下发的清单上报适用场景主机数量少、指标少主机数量多、指标密集防火墙要求需开放 Server 到 Agent 的入站Agent 只需出站调试便利性高zabbix_get直接测需要看 Server 日志首次取数延迟一个间隔需要先下发配置约一分钟我的一般原则主机数超过 50 台或者单个主机监控项超过 200 个优先用主动模式。因为被动模式下每个监控项都是一次独立的 TCP 连接虽然有连接复用Server 侧要维护大量并发 poller一台 Server 撑不了太多主机。主动模式下 Agent 会批量打包上报Server 压力小得多。主动模式的坑也有Agent 必须知道自己的 Hostname 并且和前端一致Server 的StartTrappers要够大否则上报请求排队另外 Server 侧改了监控项后要等配置同步周期默认 60 秒才会下发给 Agent调试时会感觉改了没反应。4.3 监控项、触发器、图形三件套的串联在 Web 前端建自定义监控的完整流程是新建监控项 → 建触发器 → 建图形。监控项配置里几个关键字段字段取值建议说明类型Zabbix 客户端被动或主动式与上面的模式选择对应键值custom.tcp.conn[ESTABLISHED]必须和 UserParameter 完全一致信息类型数字无正负连接数是整数更新间隔30s 到 60s越短数据越密压力越大历史数据保留7d 到 30d和 housekeeping 联动趋势数据保留365d长期趋势图的依据触发器用表达式描述什么时候算问题{app-node-01:custom.tcp.conn[TIME_WAIT].last()} 5000或者更稳一点的写法用连续几个采样点判断避免瞬时抖动误报{app-node-01:custom.tcp.conn[TIME_WAIT].min(5m)} 5000这个min(5m)是我强烈推荐的写法。TIME_WAIT 数量的瞬时波动很大一次压测或者批量操作就能冲到几千用last()会疯狂误报。改成 5 分钟最小值超过阈值噪声立刻降下来。同理CPU、内存这类指标用avg(5m)网络抖动用min(3)加max(3)组合判断。图形这边Zabbix 5.0 支持简单图形单个监控项和自定义图形多个监控项叠加。我一般把所有相关指标放进一个自定义图形里比如把 ESTABLISHED、TIME_WAIT、CLOSE_WAIT 三条线画在一起看流量形态比看三个独立图直观得多。4.4 用 zabbix_get 与 zabbix_sender 做联调前端配好之后第一件事不是等它变绿而是用zabbix_get从 Server 侧直接测zabbix_get -s 10.10.20.21 -p 10050 -k custom.tcp.conn[ESTABLISHED]返回一个数字就说明链路是通的。如果返回ZBX_NOTSUPPORTED按这个顺序排查Agent 是否在跑systemctl status zabbix-agentUserParameter 是否被加载zabbix_agentd -t custom.tcp.conn[ESTABLISHED]脚本单独执行是否正常su -s /bin/bash zabbix -c /etc/zabbix/scripts/tcp_conn.sh ESTABLISHEDSELinux 是否拦截ausearch -m avc -ts recentzabbix_agentd -t这个参数在调试期非常有用它直接在 Agent 本地测试 key绕过网络能快速区分是脚本问题还是网络问题。对于主动模式或者需要手动推送数据的场景用zabbix_senderzabbix_sender -z 10.10.20.15 -s app-node-01 -k custom.batch.result -o 1这个命令常用于处理定时任务跑完上报结果这种场景监控项类型配成Zabbix 采集器业务脚本跑完调用zabbix_sender上报比让 Zabbix 主动去轮询要灵活得多。注意-s后面的主机名同样要和前端一致。5. 短信报警链路从触发器打到运营商自定义监控把数据采上来了触发器也能正确判断异常了最后一步是让报警从系统里走出来落到值班人的手机上。这一步的链路比前面都长触发器 → 动作Action→ 报警媒介类型 → 脚本 → 短信网关 API → 运营商。任何一环断了前端看起来一切正常就是收不到短信。5.1 报警链路的分层设计先把角色的职责分清楚这张表建议贴在工位上层组件职责出问题时的表现1触发器判定是不是问题没有事件产生2动作Action决定发给谁、发几次有事件但没操作记录3报警媒介类型用什么通道发操作记录里显示失败4报警脚本实际执行发送有日志但网关没收到5用户媒体绑定收件人手机号找不到收件人6短信网关投递到运营商网关侧有错误码这六层自下而上排查能覆盖九成以上的收不到短信。动作Action的配置要点条件比如触发器严重性 警告、操作发送到用户/用户组、发送到媒体类型、恢复操作问题恢复时也发一条。很多人只配了操作忘了恢复操作结果值班人收到报警后一直在等恢复通知只能自己上系统看。发送到用户和发送到用户组的区别也值得说前者是固定几个人后者按用户组动态取新加入的人自动接收。我一般用用户组配合用户组 → 主机组的权限控制人员变动时不用改动作。5.2 报警脚本的写法与超时控制Zabbix 5.0 里脚本类报警媒介类型的脚本放在AlertScriptsPath指定的目录默认是/usr/lib/zabbix/alertscripts。这个目录安装时可能不存在需要手动建mkdir -p /usr/lib/zabbix/alertscripts mkdir -p /var/log/zabbix chown -R zabbix:zabbix /usr/lib/zabbix/alertscripts /var/log/zabbix chmod 750 /usr/lib/zabbix/alertscripts脚本本身#!/bin/bash # /usr/lib/zabbix/alertscripts/sms_alert.sh # 参数: $1手机号 $2标题 $3内容 SENDTO$1 SUBJECT$2 MESSAGE$3 LOG/var/log/zabbix/sms_alert.log GWhttp://10.10.30.50:8080/api/sms/send TOKENBearer **************** TS$(date %Y-%m-%d %H:%M:%S) # 拼接内容并做 URL 编码 CONTENT$(printf %s\n%s $SUBJECT $MESSAGE | tr -d \r) ENC$(printf %s $CONTENT | od -An -tx1 | tr % | tr -d \n) RESP$(curl -s -m 8 -X POST $GW \ -H Authorization: $TOKEN \ -H Content-Type: application/x-www-form-urlencoded \ -d mobile${SENDTO}content${ENC}) RC$? echo ${TS} | to${SENDTO} | rc${RC} | resp${RESP} $LOG if [ $RC -ne 0 ]; then echo SMS gateway unreachable, curl rc$RC exit 1 fi case $RESP in *code:0*|*success:true*) exit 0 ;; *) echo SMS gateway returned error: $RESP; exit 1 ;; esac几个关键设计点超时必须由脚本自己控制。Zabbix 5.0 的脚本类媒介类型没有内置超时参数如果网关不通脚本会一直挂着直到 Server 的Timeout生效默认 3 秒很多部署会调到 10 秒。用curl -m 8主动限制在 8 秒内返回比让 Zabbix 来砍更干净。退出码决定成败。exit 0表示发送成功非 0 表示失败Zabbix 会在动作日志里标红同时把 stdout 和 stderr 记进日志。这个机制很适合做分层——脚本内部先用退出码表达结果再配合日志排查。URL 编码是必须的。Zabbix 传给脚本的{ALERT.MESSAGE}默认带换行和中文直接拼进表单会截断请求。上面用od做十六进制编码是个取巧办法离线环境里可能没装 python 的 urllib也没有jq更规范的做法是本地装个 python 用urllib.parse.quote。注意od方案的输出末尾会多一个%严谨点要再处理一下ENC$(printf %s $CONTENT | od -An -tx1 | tr % | sed s/%$// | tr -d \n)收件人格式要约定好。手机号在用户 → 媒体里配置格式建议统一成 11 位纯数字不要带86或者空格。网关侧对接时如果不一致报错往往只是号码格式错误这种模糊信息。注意脚本必须以 zabbix 用户可执行且不能是全局可写。chmod 750加chown zabbix:zabbix是最小权限组合。别用 777那是给自己挖坑。5.3 告警分级、收敛与值班视角的设计脚本能发出去只是起点真正影响体验的是发多少、发给谁。内网监控最常见的反面教材是一个人被 200 条短信淹没最后干脆把手机静音等到真出事反而没人看。这就是告警疲劳。我的做法是三层控制。第一层用严重性分流。Zabbix 触发器有六个严重性等级未分类、信息、警告、一般严重、严重、灾难。动作里按严重性设条件只有严重和灾难才走短信通道警告级别走邮件或者内部消息。这样短信通道天然稀缺收到就是真事。第二层用依赖关系抑制。一台机器上如果服务器不可达已经触发了那么它上面的 CPU、磁盘、进程数这些触发器还在报就是噪声。配置触发器依赖Trigger Dependency让细粒度触发器依赖主机不可达这个粗粒度触发器主机一断下面全部抑制只发一条。第三层用升级策略控制频率。动作的操作里可以配多个步骤比如步骤延迟发送对象说明1立即一线值班组首次通知210 分钟一线值班组未恢复重发330 分钟运维负责人升级460 分钟运维负责人 主管二次升级默认情况下动作会在问题持续期间按升级间隔重复发送间隔设得太短就是刷屏。我一般把默认升级间隔设成 60 分钟紧急场景在步骤里单独缩短。还有一个小细节消息模板。默认模板发出来的内容是Problem started at 14:23:05 on 2024.05.12 Problem name: TCP TIME_WAIT 连接数过高 Host: app-node-01 Severity: Average短信有字数限制这一长串能占两条。我一般自定义成单行紧凑格式【生产告警】app-node-01 TIME_WAIT超5000 当前6832 时间14:23在动作的操作里改默认信息和默认主题即可用宏拼{HOST.NAME}、{TRIGGER.NAME}、{ITEM.LASTVALUE}、{EVENT.TIME}。5.4 报警不通时的排查顺序收不到短信时不要一通乱查按链路顺序走。第一步看动作日志。前端报表 → 动作日志筛选最近一小时。这里能看到每个事件有没有匹配到动作、有没有执行发送、执行结果是什么。完全没记录触发器没触发或者动作条件不匹配。回去看触发器的最新数据以及动作条件里的严重性、主机组、时间段。有记录但状态是失败问题出在脚本层去/var/log/zabbix/zabbix_server.log找对应时间点的报错常见的是Cannot execute script: Permission denied或Timeout while executing a shell script。有记录、状态是已发送但手机没收到问题在网关或运营商侧去看脚本日志/var/log/zabbix/sms_alert.log里的网关返回。第二步手工测脚本。绕过 Zabbix直接用 zabbix 用户身份跑su -s /bin/bash zabbix -c /usr/lib/zabbix/alertscripts/sms_alert.sh 13800000000 测试 这是一条测试短信 echo exit code: $?如果手工能发但不自动发那就是环境差异比如 cron 的 PATH 不同、SELinux 上下文不同如果手工也发不出那就是脚本或网关问题。第三步用媒介类型的测试按钮。Zabbix 5.0 的报警媒介类型配置页有个测试按钮可以填收件人、主题、内容直接调脚本。这个比手工跑脚本更接近真实调用路径能暴露参数格式问题。第四步检查用户媒体。前端管理 → 用户点开对应账号看报警媒介里有没有配手机号有没有勾选已启用以及媒介类型的使用范围是不是覆盖了这个场景。我遇到过最隐蔽的一次是脚本没问题、网关没问题、日志也正常就是收不到。最后发现是用户被加到了一个用户组里那个用户组对触发报警的主机组没有读写权限动作匹配不到这个用户。这类权限问题在前端不会有任何明显报错只能靠对照检查。6. 上线之后几个容易被忽略的稳定性细节部署完成不等于结束。Zabbix 这套东西的特点是能跑和能长期跑之间差得很远很多问题在头一个月不会暴露。6.1 数据库膨胀与 housekeepingZabbix 的数据增长速度经常被低估。以 100 台主机、每台 200 个监控项、更新间隔 60 秒计算每天产生的 history 数据在千万行级别。默认保留 90 天一个月后数据库就是几十 GB。先看 housekeeping 配置在zabbix_server.conf里HousekeepingFrequency1 MaxHousekeeperDelete5000然后在 Web 端管理 → 一般 → 管家里调保留期限历史数据 7 到 30 天趋势数据 365 天。趋势数据是小时聚合体积小得多可以留长。当数据量上到几十 GBhousekeeping 的 DELETE 操作会锁表导致前端卡顿。这时候必须上分区表。Zabbix 自带分区脚本在/usr/share/doc/zabbix-sql-scripts/mysql/partitioning/下。原理是把 history 表按天/月分区过期数据直接 DROP PARTITION比 DELETE 快几个数量级。这个改动需要停服务、改表结构建议在业务低峰期做并提前完整备份。6.2 离线环境下的备份与打补丁难题离线环境没有yum update这意味着安全补丁和版本升级都得手工搬包。我的做法是备份策略分三层用脚本每天跑一次失败就发告警正好用上整套短信通道#!/bin/bash # /opt/zabbix-deploy/backup.sh DATE$(date %Y%m%d) BACKUP/data/backup/zabbix mkdir -p $BACKUP mysqldump --single-transaction --routines --triggers zabbix \ | gzip $BACKUP/zabbix-$DATE.sql.gz tar czf $BACKUP/zabbix-conf-$DATE.tar.gz \ /etc/zabbix /etc/httpd/conf.d/zabbix.conf \ /usr/lib/zabbix/alertscripts /etc/zabbix/scripts find $BACKUP -name *.gz -mtime 14 -delete--single-transaction是关键它让 mysqldump 在 InnoDB 上做一致性快照而不锁表备份期间监控照常运行。数据量大的话这个参数能救命。升级时先在测试环境把 rpm 补丁装一遍验证zabbix_server -V版本、前端能打开、自定义监控项还在、短信能发出去再推到生产。离线环境的升级风险比在线高因为没法快速回滚依赖。真要回滚得靠前面那份配置备份加数据库快照。6.3 一份日常巡检清单最后给一份我实际在用的巡检清单每周过一遍大部分事故都能提前发现。检查项位置判断标准Server 队列积压管理 → 队列每个队列项不超过 100不可达主机监控 → 最新数据筛选不可达无长期不可达主机无数据监控项监控 → 最新数据数量不增长Server 进程数ps -ef | grep zabbix_server | wc -l与配置的 Start* 之和匹配数据库体积du -sh /var/lib/mysql/zabbix环比增长在预期内housekeeping 耗时grep housekeeper /var/log/zabbix/zabbix_server.log单次不超过 60 秒报警脚本日志/var/log/zabbix/sms_alert.log无连续失败磁盘空间df -h根分区剩余 20% 以上时间偏差chronyc sources或ntpq -p偏差小于 1 秒我个人在实际操作中的体会是Zabbix 这套系统真正难的不是安装是装完之后你还得养它。离线环境把养护的难度又拔高了一层因为所有外部的便利——在线源、一键升级、云端告警通道——都没有了什么都得自己搭。但只要把依赖清单、配置文件、脚本、备份这四样东西管住它会成为内网里最稳的那套基础设施之一。顺便说一句如果后期想把报警同时推到内部消息平台比如群机器人 webhook链路是一样的只是在报警脚本里多发一个 HTTP 请求而已成本很低值得加上作为短信的补充通道。