Nginx 监控这件事,很多人一开始想复杂了。Zabbix 要拿到 Nginx 的运行状态,并不需要装额外探针,也不一定非要解析访问日志,最稳定的入口其实是 Nginx 自带的 stub_status 状态页。只要把它打开,再用 Zabbix Agent 把里面的几个数字读回来,就能完成请求量、并发连接、读写状态的监控,再配上触发器和图形,连接数异常、请求速率突变这些情况都能直接看到。
这篇内容按实际落地顺序来拆:先讲 Nginx 状态页每个字段是什么意思,再讲怎么让 Zabbix Agent 把数据采回来,然后是 Zabbix 前端里的监控项、触发器和图形怎么配,最后做一轮实测,把请求打起来,再去看统计结果怎么解读。适合刚接触 Zabbix 监控 Nginx 的人,也适合已经在用 shell 脚本采集、但想转成 Zabbix 规范化监控的人。
1. 先搞清楚 nginx 状态页:Zabbix 监控 Nginx 的数据从哪来
1.1 stub_status 输出字段对照
Zabbix 监控 Nginx,底层数据源不是日志,而是 Nginx 自己维护的计数器。只要在 Nginx 配置里启用 stub_status,访问状态页就能看到一小段纯文本:
Active connections: 291 server accepts handled requests 16630948 16630948 31070465 Reading: 6 Writing: 179 Waiting: 106这段文本虽然短,但每个字段都有明确含义。
| 字段 | 含义 | 类型 |
|---|---|---|
| Active connections | 当前活动连接数 | 瞬时值 |
| accepts | Nginx 启动以来累计接受的客户端连接总数 | 累计值 |
| handled | 成功处理的连接总数 | 累计值 |
| requests | 累计处理的客户端请求总数 | 累计值 |
| Reading | 正在读取请求头的连接数 | 瞬时值 |
| Writing | 正在读取请求体、处理请求或写响应的连接数 | 瞬时值 |
| Waiting | 空闲的 keep-alive 连接数 | 瞬时值 |
第一行Active connections是当前活动连接数。注意它包含后面 Reading、Writing、Waiting 三类的总和,所以只要 Nginx 活着,这个数值通常不会是 0。
第二行是固定表头:server accepts handled requests。第三行的三个数字分别对应表头,都是只增不减的累计计数器,只有 Nginx 重启才会归零。
第四行的 Reading、Writing、Waiting 是三个瞬时值。一个请求从进入到结束,通常会依次经过 Reading、Writing 两个阶段,最后进入 Waiting,或者直接关闭连接。
这几个字段之间的换算关系是:Active connections = Reading + Writing + Waiting。理解这个关系,后面看图形和触发器才不会被数字骗到。
1.2 哪些指标值得接进 Zabbix
不是每个字段都必须接进来,但下面这几个我建议至少都要有。
请求速率是最核心的指标,对应 requests 计数器,看的是单位时间内处理了多少请求,也就是平时说的 QPS。只看累计值没有意义,必须换算成速率。
新建连接速率对应 accepts 计数器,看的是单位时间内来了多少新连接。这个值和 QPS 的区别在于,keep-alive 生效的情况下,一个连接上可以连续发多个请求,所以请求速率通常远大于连接速率。
当前并发连接数对应 Active connections,是最直观的负载指标。但它不能单独说明问题,必须结合 Reading、Writing、Waiting 一起看。
Reading 如果持续偏高,说明有一些客户端请求头发得很慢,或者请求头一直没发完。Writing 持续偏高,说明 Nginx 正在大量往客户端写响应,这时候要关注带宽和上游服务能不能跟上。Waiting 高反而是正常现象,说明大量连接是空闲 keep-alive,对静态资源或高并发 Web 服务很常见。
把这些字段全部接进 Zabbix,才算真正把 Nginx 的运行状态看完整。
2. 环境准备与最小配置:让 Nginx 先吐出状态数据
2.1 确认 Nginx 是否支持 stub_status
stub_status 不是所有 Nginx 都默认包含。编译 Nginx 时如果没有加--with-http_stub_status_module,这个功能就不存在,配置写了也会报错。
先检查当前 Nginx 有没有这个模块:
nginx -V 2>&1 | grep -o "http_stub_status_module"如果输出里有http_stub_status_module,说明支持。如果没有,有两个方向:重新编译 Nginx 时加上这个模块,或者换成已经默认包含该模块的发行版软件包。
很多发行版和官方仓库自带的 Nginx 都已经开启了这个模块,但自编译版本一定要单独确认。
这里提醒一下:执行nginx -V时,编译参数是在标准错误输出里,所以我加了2>&1把错误输出重定向到同一路。很多人第一次只看第一行 version,以为不支持,其实是没看全。
2.2 Nginx 状态页配置示例与安全限制
确认模块存在后,在 Nginx 的 server 配置里加一个 location。以下是一份最小配置:
server { listen 80; server_name _; location /nginx_status { stub_status on; access_log off; allow 127.0.0.1; deny all; } }这里最关键的是allow和deny。stub_status 本身没有任何认证机制,任何人能访问就能看到你的连接数、请求量和连接状态。如果把状态页暴露到公网,内部运行信息等于公开了。所以默认只允许本机访问是最稳妥的做法。
如果你的 Zabbix Agent 和 Nginx 不在同一台机器上,不要直接把deny all删掉,应该改成只允许采集机 IP 访问:
location /nginx_status { stub_status on; access_log off; allow 192.168.1.100; deny all; }改完配置先检查再重载:
nginx -t nginx -s reloadnginx -t这一步不能省。stub_status 的 location 如果配置在错误的上下文里,或者和已有配置冲突,reload 会直接失败。
2.3 用 curl 验证状态页是否生效
配置完成之后,先在 Nginx 所在机器上验证:
curl -s http://127.0.0.1/nginx_status如果能看到开头那段文本,说明状态页已经可以访问了。如果返回 404,说明 location 没生效或者配置在了错误的 server 块里。如果返回 403,大概率是deny all生效了,而你的请求来源 IP 不在allow列表里。
这里有个容易忽略的点:如果你的 Nginx 不是监听 80 端口,或者同时有 HTTPS,URL 里要带上实际协议和端口,例如http://127.0.0.1:8080/nginx_status。先花一分钟把 curl 这步跑通,后面会省很多时间。
3. Zabbix Agent 接入:一条 UserParameter 和一个监控项的关系
3.1 安装与基础配置 Zabbix Agent
Zabbix Agent 是部署在 Nginx 所在机器上的采集程序。它的作用和 Nginx 状态页互补:状态页负责提供数据,Agent 负责把数据读出来,再交给 Zabbix Server。
安装方式要看你的系统和 Zabbix 版本,直接使用官方仓库安装 zabbix-agent 即可。装完需要修改配置文件,常见要确认三个参数:
Server=192.168.1.10 ServerActive=192.168.1.10 Hostname=web01Server是允许给这台 Agent 下发被动采集请求的 Zabbix Server 地址。ServerActive是主动上报要连接的 Server 地址。Hostname是 Agent 上报时使用的名字,在 Zabbix 前端添加主机时要保持一致,特别是主动检查时,Hostname 对不上会直接导致数据丢失。
这里解释一下被动检查和主动检查的区别。被动检查是 Zabbix Server 主动连接 Agent 的 10050 端口要数据,主动检查是 Agent 主动连接 Server 的 10051 端口上报数据。自定义 UserParameter 两种方式都支持,但排查问题时的思路不一样,后面会详细说。
改完配置启动服务:
systemctl restart zabbix-agent systemctl status zabbix-agent3.2 通过 UserParameter 把 nginx_status 转成监控项
Zabbix Agent 默认不认识 nginx 的 key,需要通过 UserParameter 来定义。UserParameter 的格式很简单:一个 key,一个命令,Agent 执行命令,把标准输出作为监控值返回。
在/etc/zabbix/zabbix_agentd.d/目录下创建一个 nginx.conf,写入以下内容:
UserParameter=nginx.active,/usr/bin/curl -s --max-time 3 http://127.0.0.1/nginx_status | awk '/Active connections/{print $3}' UserParameter=nginx.accepts,/usr/bin/curl -s --max-time 3 http://127.0.0.1/nginx_status | awk 'NR==3{print $1}' UserParameter=nginx.handled,/usr/bin/curl -s --max-time 3 http://127.0.0.1/nginx_status | awk 'NR==3{print $2}' UserParameter=nginx.requests,/usr/bin/curl -s --max-time 3 http://127.0.0.1/nginx_status | awk 'NR==3{print $3}' UserParameter=nginx.reading,/usr/bin/curl -s --max-time 3 http://127.0.0.1/nginx_status | awk '/Reading/{print $2}' UserParameter=nginx.writing,/usr/bin/curl -s --max-time 3 http://127.0.0.1/nginx_status | awk '/Writing/{print $4}' UserParameter=nginx.waiting,/usr/bin/curl -s --max-time 3 http://127.0.0.1/nginx_status | awk '/Waiting/{print $6}'逐个拆一下。每行都是一个 key,例如nginx.active。后面的命令先用 curl 抓取状态页,再用 awk 提取对应字段。
awk 的写法要特别注意。第三行的 accepts、handled、requests 是三个连续数字,所以分别用NR==3定位到第三行,再打印第 1、2、3 列。Reading、Writing、Waiting 在同一行里,所以要先用正则匹配到这一行,再打印对应列。Writing 是第 4 列,Waiting 是第 6 列,这个顺序很多人会弄错。
命令里的--max-time 3不是随便加的。如果状态页异常导致 curl 一直挂住,Agent 执行这个 UserParameter 就会卡住,严重时会占满 Agent 的检查线程,影响这台机器上的其他监控项。加上超时时间,最坏 3 秒也会退出。
改完配置后重启 Agent:
systemctl restart zabbix-agent3.3 先用 zabbix_get 还是前端测试功能
配置完 UserParameter,不要急着去 Zabbix 前端创建主机,先在命令行验证一遍 key 能不能查到值。
在有 zabbix-get 工具的机器上执行,它通常在 Zabbix Server 或 Proxy 上:
zabbix_get -s 192.168.1.20 -k nginx.requests返回一个数字说明这条链路通了。如果提示 Not supported 或者没有输出,先不要怀疑 Zabbix 配置,回到 Nginx 机器上直接执行那条 UserParameter 命令,看有没有结果。
这里有个容易踩的坑:你手动执行命令时用的是 root 用户,而 Zabbix Agent 运行时的用户通常是 zabbix,两个用户的环境变量、权限、可执行路径可能都不一样。比如 zabbix 用户没有权限访问某个目录,或者 SELinux 拦截了 zabbix 用户执行 curl,都会导致手动执行正常、Agent 执行失败。
新版 Zabbix 前端在监控项配置里也有一个 Test 按钮,可以直接测试当前主机上的 key,返回结果和报错更直观。实际排查时,我一般按三个地方顺序看:Agent 所在机器手动执行命令、zabbix_get 从 Server 端测试、前端 Test 按钮看报错。
4. 创建主机、监控项、触发器:数据到告警的完整链路
4.1 在 Zabbix 前端添加主机与监控项
命令行验证通过后,进入 Zabbix Web 界面,按照 配置 → 主机 → 创建主机 的路径添加一台主机。
填写主机名时要注意,Agent 配置文件里的 Hostname 和这里的主机名必须一致,否则主动检查会失败。接口配置里选择 Agent,填写 Nginx 所在机器的 IP 和默认端口 10050。
添加监控项有两条路线。一条是直接链接模板,Zabbix 模板库里有 Nginx by HTTP、Nginx by Zabbix agent 2 这类现成模板,不同版本的模板数量和名称会有差异,底层采集方式也是去读状态页,适合不想自己维护 key 的人。另一条是手动创建监控项,用刚才定义的 UserParameter 键值,自由度更高,也更容易理解每个数字怎么来的。
如果你是第一次做,我更建议先手动创建。原因很简单:自动模板会隐藏很多细节,一旦出了问题,你很难判断是状态页的问题、Agent 的问题还是模板映射的问题。手动创建一遍,等于把链路里的每个环节都摸清楚了。
手动创建监控项时,以nginx.requests为例:
- 名称:Nginx Requests
- 类型:Zabbix Agent
- 键值:nginx.requests
- 信息类型:数字(无正负)
- 更新间隔:1m 或 30s 都可以
- 历史数据保留:7d
- 趋势数据保留:365d
其他几个 key 按同样方法创建。
4.2 请求速率、连接数怎么存储和计算单位
这里要解决一个关键问题:accepts、handled、requests 这三个字段是累计值,直接存进 Zabbix 只能看到一根一直向上的线,很难判断当前请求量到底是多少。所以必须让 Zabbix 把累计值换算成速率。
在 Zabbix 监控项配置里有一个字段叫“存储值”,可以选“原样存储”“增量(简单变化)”和“增量(每秒速率)”。
| 存储方式 | 适用场景 | 示例 |
|---|---|---|
| 原样存储 | 瞬时值,当前是多少就存多少 | active、reading、writing、waiting |
| 增量(简单变化) | 累计值的两次差值 | accepts 每次变化量 |
| 增量(每秒速率) | 累计值换算成每秒速率 | requests、accepts、handled |
对 requests、accepts、handled 这三个累计计数器,应该选择“增量(每秒速率)”,单位写成 req/s 或 conn/s。这样 Zabbix 会在后台自动计算两次采集之间的差值,再除以时间间隔,得到每秒速率。
如果你不想在监控项里改存储方式,也可以用预处理步骤里的“每秒更改量”来实现同样效果。两者本质上是一样的,看你习惯哪种。
唯一要注意的是,如果 Nginx 在两次采集之间重启过,计数器归零,速率可能会出现一次很大的负值或异常值,统计图形上会出现明显的断点,这是正常现象,不是系统崩溃。
active、reading、writing、waiting 这四个瞬时值就保持“原样存储”,它们的数值本身就是当前状态,不需要做速率换算。
4.3 触发器和告警阈值怎么定
监控项建好后,数据会不断进入 Zabbix,但没人盯着图形的话,异常还是发现不了。触发器的意义就是自动判断数值是否超过预期。
触发器配置路径是 配置 → 主机 → 触发器 → 创建触发器。以活跃连接数为例,一个简单表达式:
last(/web01/nginx.active)>10000含义是当前活跃连接数大于 10000 时触发警告。再比如请求速率:
min(/web01/nginx.requests,5m)>5000含义是最近 5 分钟请求速率平均值超过 5000 req/s 时触发。
阈值定多少没有标准答案,取决于你的业务。我建议先让 Zabbix 跑两三天,积累一段基线数据,再根据图形里的峰值来定警告和严重阈值,不要凭感觉拍脑袋。第一次设置可以把阈值放宽一点,确认告警链路能正常触发后,再往回收。
触发器还可以设置恢复表达式,也就是数值降到多少以下时自动恢复为正常。这个最好一起配好,否则每次数值震荡都会反复触发、恢复,告警会很吵。
要测试告警链路的完整效果,除了触发器,还需要配置好动作和通知方式。如果暂时没配邮件或其他通知,也可以先只做触发器,通过 Zabbix 首页的告警记录来确认触发是否成功。
5. 实测一轮:用请求压一压,再去看统计结果和图形
5.1 制造流量:curl 循环或 ab 简单压测
配置完成之后,最关键的一步就是实测。没有流量,监控图形是一条平线,你很难判断数据到底有没有采集成功。
最简单的测试方法是用 curl 发一批请求。在 Nginx 机器上执行:
for i in $(seq 1 1000); do curl -s -o /dev/null http://127.0.0.1/; done这个循环会向本机 Nginx 发送 1000 次请求,每次丢弃响应内容。优点是环境干净,不需要额外工具。缺点是速度不够快,产生的请求速率不会特别高。
如果你机器上有 ab,也就是 ApacheBench,可以压得更明显一些:
ab -n 5000 -c 100 -k http://127.0.0.1/这条命令的含义是:总共发送 5000 个请求,同时保持 100 个并发连接,-k表示启用 keep-alive。ab 在测试时会持续请求,能把 Nginx 的请求速率和活跃连接数明显拉起来。
执行的时候建议一次只跑一种,跑完等一分钟,让 Zabbix 完成几个采集周期,再去前端看数据。如果立刻去看,采集周期还没到,可能什么都看不到。
5.2 分析 Latest data 和 Graphs
进入 Zabbix 前端的“监测 → 最新数据”,选择刚才创建的主机,就能看到每条监控项的当前值。这时候你看到的应该是有具体数字的绿色状态,而不是“不支持”或“无数据”。
以刚才 ab 压测为例,压测过程中可以看到 requests 数值冲上去,压测结束后又会回落。如果配上图形,把请求速率、活跃连接、Reading、Writing、Waiting 放在同一张图里观察,能很直观地看到整个请求生命周期的变化。
如果你没有在监控项里单独创建图形,也可以在仪表盘中添加一个图表组件,数据源选择对应主机的监控项。Zabbix 的图表组件支持把多个监控项叠加到同一张图上,非常适合对比 Active connections、Reading、Writing、Waiting 这几个相关指标。
5.3 从数据反向判断 Nginx 状态是否健康
数据有了,接下来的重点是怎么读这些数字。
先看 active connections 和 waiting 的关系。如果活跃连接数很高,但 waiting 也很高,说明大量连接是空闲 keep-alive,Nginx 实际压力不大,只是在维持长连接。如果活跃连接数高,waiting 却很低,说明连接都在真正干活,这时候负载才是真的高。
再看 accepts 和 handled 的对比。正常情况下,这两个数值应该非常接近。如果 handled 长期明显小于 accepts,说明有一部分连接没有被 Nginx 成功处理,可能原因是 worker_connections 配置过低,连接数被系统层面拒绝了。通过 Zabbix 监控这两个值的曲线差值,能比看错误日志更早发现问题。
再看请求速率和连接速率的关系。用 requests 速率除以 accepts 速率,可以得到平均每个连接上的请求数。如果这个值接近 1,说明 keep-alive 基本没起作用,客户端每次请求都新建连接,连接效率很低。如果明显大于 1,说明 keep-alive 生效良好,一个连接上处理了多个请求。
Reading 和 Writing 是更细的观察点。静态资源站点在压测时,Writing 会明显升高,因为 Nginx 正在往客户端输出响应。如果 Writing 持续高位但 requests 并不高,可能是网络带宽打满了,响应写不出去。Reading 持续高位则要小心,可能是客户端请求头发送缓慢,也可能是存在慢连接,这种情况即使活跃连接数不高,worker 也可能被拖住。
这些判断不能只看一两个采样点,最好结合 5 分钟、1 小时的时间窗口来看趋势。这也是为什么前面建议把趋势数据保留时间设长一些。
6. 常见问题排查与生产环境落地的边界
6.1 Agent 显示 Not supported 时按什么顺序查
Zabbix Agent 返回 Not supported 是监控 Nginx 时最常遇到的问题。很多人第一反应是去改 Zabbix 配置,其实大多数时候问题出在 Agent 侧。
我的排查顺序是固定的。
第一步,直接手动执行那条 UserParameter 命令,看有没有输出。如果命令本身就没输出,说明状态页、curl、awk 里有一环断了。
第二步,确认执行用户。Zabbix Agent 通常以 zabbix 用户运行,手动用 root 执行成功不代表 zabbix 用户能执行。可以用su - zabbix -s /bin/bash -c "命令"来模拟 Agent 的执行环境。
第三步,看 Agent 日志,默认在 /var/log/zabbix/zabbix_agentd.log。日志里会记录 key 对应的错误信息,比如命令找不到、执行超时、权限不足等。
第四步,改完 UserParameter 后确认重启了 Agent。UserParameter 是在 Agent 启动时读取的,不重启不会生效。
第五步,从 Server 端用 zabbix_get 测试连通性。这一步能区分问题是出在 Agent 执行命令,还是 Server 与 Agent 之间的网络、端口、密钥配置上。
还有一个常见问题:Linux 有 SELinux 或 AppArmor 环境时,zabbix 用户执行 curl 连接本机端口可能会被拦截。手动执行正常,Agent 执行就是失败,排查到这一步时,要看安全审计日志,确认是不是被策略拦截了。
6.2 状态页不能直接暴露在公网
前面配置 Nginx 状态页时写了 allow 和 deny,这个限制在正式环境同样重要。stub_status 只有数据输出,没有认证、没有访问控制,任何人拿到状态页地址,就能看到你所有虚拟主机的连接数、请求量等信息。
这些数据单独看不算机密,但结合业务时序,可能暴露促销活动、低谷期等运营信息。所以生产环境要遵守几个原则:
- 状态页只监听内网地址,或只允许内网访问。
- Zabbix Agent 与 Nginx 部署在一起时,直接限制为 127.0.0.1。
- 如果需要远程采集,优先让 Agent 本机读取,不要让其他机器直接 HTTP 访问状态页。
- Nginx 服务本身就开启了公网监听时,更要检查 allow 列表,不要把公网网段放进去。
另外,状态页建议使用独立 location,不要放在一个会匹配大量路径的通用规则里,避免被扫描工具顺带发现。access_log off也要保留,否则每个状态页请求都会写一条访问日志,白白增加磁盘 IO。
6.3 多实例和规范化:模板、宏、命名规则
如果只有一台机器,手动创建监控项没什么问题。一旦机器多起来,比如七八台 Nginx 都要监控,逐台手动创建就很痛苦,而且容易漏配。
这时候应该把监控项、触发器、图形整理成模板。在模板里创建好 nginx.active、nginx.requests 这些监控项和对应的触发器,再把模板链接到主机上,所有主机自动继承配置。后续要加阈值、加图形,只需要改模板一处,所有主机同步生效。
模板里的阈值可以做成宏,比如{$NGINX_ACTIVE_WARN}、{$NGINX_REQ_HIGH}。这样在不同业务机器上可以差异化覆盖,不用复制整个模板。比如 A 机器请求量本来就大,警告阈值定 8000;B 机器是小业务,阈值定 1000。用宏就能在同一套模板下按主机单独调整。
key 的命名也要规范。前面用的 nginx.active、nginx.requests 就是比较清晰的命名方式。如果一台机器上有多个 Nginx 实例,建议在 key 里带实例编号,比如 nginx1.active、nginx2.active,避免数据串台。
6.4 历史数据保留和监控频率
最后聊一下采集频率和数据保留。
对 Nginx 监控来说,30 秒到 1 分钟的采集间隔已经足够。不要看到别人用 1 秒间隔就觉得越快越好,越短的间隔意味着越多的 Agent 执行、越多的 Server 轮询和越大的存储开销。
尤其是自定义 UserParameter 每次都执行 curl 命令,如果一台机器上有 7 个 nginx key,每个采集周期就有 7 次 curl。机器数量一多,这个开销会被明显放大。
如果想降低开销,有两个方向。
一个方向是把采集间隔统一调大成 1 分钟。Nginx 的指标本来就是慢变量,1 分钟内的尖峰对告警影响不大。
另一个方向是写一个单独的采集脚本,一次抓取状态页,把结果写到临时文件,再由多个 UserParameter 去读文件,避免每次都重新 curl。后一种方案适合机器多、又不愿意放宽采集间隔的场景,但脚本逻辑和错误处理要写得更严谨,否则一个脚本崩了,所有 nginx key 都会失效。
历史数据保留方面,原始监控值保留 7 到 14 天足够,趋势数据保留 365 天用于回顾容量。如果你发现自己经常要回溯更久之前的 Nginx 指标,建议单独导出到外部存储,不要无限拉长 Zabbix 的保留周期,否则数据库膨胀之后,整个监控系统的查询速度都会受影响。
我个人更建议按这个顺序落地:先把状态页和 curl 验证跑通,再用 zabbix_get 确认 key 有数据,然后创建主机和监控项,最后才是触发器和图形。看起来流程更长,但每一步都有明确的成功标准,出了问题也知道在哪一段排查。真正跑过一轮测试之后,你会发现 Zabbix 监控 Nginx 并不复杂,难点反而不是工具,而是你愿不愿意把一个累计计数器、一个状态页字段吃透。