2905 字
15 分钟
为什么我把 Zabbix Server 放进独立 VM 的 Docker,而把 agent2 留在宿主机上

前阵子打算在自己的环境里上 Zabbix 时,我最开始纠结的其实不是“会不会装”,而是:

监控中心到底应该放在哪里,应该怎么放,才能既顺手又不容易把自己坑到。

我的环境不算大,但也已经不是“纯空白新机”那种状态了:

  • 一台 PVE 宿主机,i7-8700 / 64G RAM
  • PVE 上已经跑着其他业务和实验环境
  • 宿主机本身也已经承担了一些 Docker 服务
  • 另外还有几台需要一起纳管的机器

所以问题的重点并不是 Zabbix 能不能跑,而是:

  • 要不要直接装在 PVE 宿主机上
  • 还是单独开一台 VM
  • 如果单独开 VM,Server 是原生装,还是用 Docker 编排
  • 宿主机本身又该怎么被监控

最后我的选择是:

  • Zabbix Server 放在一台独立 VM 里
  • Server / Web / DB 用 Docker 部署
  • PVE 宿主机本体安装 zabbix-agent2

这篇文章就记录一下这套选择背后的考虑,以及这套架构后来真实踩到的一个坑:

Linux: Zabbix agent is not available (for 3m)

为什么我没有把 Zabbix Server 直接装在 PVE 宿主机上#

从“能不能运行”这个角度说,把 Zabbix Server 直接装在 PVE 宿主机上当然是可行的。PVE 本质上就是 Debian 系统,技术上没有障碍。

但我最后还是没有这么做,原因主要有三点。

1. 宿主机应该尽量少承担额外耦合#

PVE 宿主机最核心的职责是:

  • 跑虚拟化平台
  • 托管 VM / LXC
  • 维护网络、存储、宿主层稳定性

一旦把 Zabbix Server 直接塞进宿主机,就会额外把这些东西也绑上去:

  • Web 前端
  • PHP / Nginx
  • 数据库
  • Server 自身守护进程
  • 历史数据写盘和维护

这样做不是不能用,而是会让宿主机的角色变得更混杂。

对于个人实验环境来说,偶尔混一点业务也不是世界末日;但如果目标是长期把监控系统保留下来,那么“少耦合一点”通常会更舒服。

2. Zabbix 真正重的部分,不在界面,而在数据库和持续写入#

很多人一开始会把 Zabbix 想成“一个带网页的监控工具”,但真正会慢慢变重的部分,其实是:

  • 历史数据
  • 趋势数据
  • 事件记录
  • 数据库持续写入
  • 长期保留带来的磁盘与 I/O 压力

哪怕我现在监控规模不大,这些东西的增长趋势也是确定的。

如果一开始就把它和宿主机绑死,后面一旦要:

  • 调整保留策略
  • 迁移数据库
  • 升级版本
  • 做回滚或快照

都会比单独放在 VM 里更麻烦。

3. 监控中心和被监控核心宿主最好不要完全同生共死#

这点是我最后最看重的。

我当然要监控 PVE 宿主机本身,但如果 Zabbix Server 也直接跑在宿主机上,那么一旦宿主机出现:

  • 网络异常
  • 磁盘爆满
  • Docker 抢资源
  • 升级失败
  • 某些宿主层服务问题

监控中心本身也可能跟着一起受影响。

这样一来,“我在监控谁”和“谁在负责监控”就重叠得太厉害了。

从架构上看,把 Zabbix Server 放进独立 VM,至少隔离性会更好一点:

  • 可以单独快照
  • 可以单独备份
  • 可以单独迁移
  • 可以单独回滚
  • 不会因为宿主机上某个额外服务折腾而直接把监控中心一起带崩

那为什么我最后又选了 Docker,而不是在 VM 里原生装#

确定“单独 VM”这件事之后,第二个问题就是:

这台 VM 里是原生装 Zabbix,还是继续用 Docker。

我最后选了 Docker,理由其实很现实。

1. 组件天然适合拆分#

Zabbix 这一套本来就不是一个单进程小工具,而是至少包含:

  • zabbix-server
  • Web 前端
  • 数据库
  • 可能还有 Java Gateway 等组件

这种结构用 Docker 编排起来,本身就比较顺手:

  • 每个组件边界清楚
  • 配置文件和环境变量更集中
  • 容器重建成本低
  • 升级路径也更清晰

对我这种本来就已经习惯 Docker 工作流的人来说,这种部署方式明显更自然。

2. 更符合我自己的维护习惯#

比起“尽量原生装所有东西”,我平时其实更习惯:

  • 服务组件容器化
  • 配置收敛到 compose / 环境变量
  • 出问题优先看容器状态、日志、网络

这种习惯不是绝对更正确,但它和我的日常工作流更一致。

如果一个工具最终是要长期留在环境里的,那我会更偏向:

用我自己最容易维护、最容易理解、最容易回溯的方式去部署它。

3. 方便把监控中心作为一个独立可迁移单元保留下来#

把 Zabbix Server 放进一台独立 VM,再在 VM 里用 Docker 跑组件,最后得到的效果就是:

  • VM 是隔离边界
  • Docker 是服务编排边界

这样后续无论是:

  • 做备份
  • 做迁移
  • 调整资源
  • 把这套监控中心搬到别的宿主

操作上都会比较清楚。

那为什么 agent2 不一起塞进容器,而是留在宿主机本体#

如果说前面是在解决“监控中心怎么放”,那这里解决的就是:

宿主机本身应该怎么被监控。

我最后没有把 agent 也一起塞进容器,而是直接在宿主机本体安装了 zabbix-agent2

这个选择的理由也很明确。

1. 我想看的是宿主机本体,不是某个容器视角里的宿主机投影#

如果监控的是 PVE 宿主机,那我更希望采到的是:

  • 宿主机自己的 CPU / Load
  • 宿主机自己的内存和磁盘状态
  • 宿主机自己的网络与系统资源
  • 宿主机上的真实进程和服务可达性

而不是经过容器隔一层之后的某种不完整视角。

对于“被监控对象就是宿主机本体”这种场景,把 agent 放在宿主机上会更直接,也更自然。

2. agent2 本身足够轻,没必要为了形式统一强行容器化#

zabbix-agent2 本身不是什么特别重的组件,跑在宿主机上对资源压力很小。

既然它的职责就是:

  • 采集本地数据
  • 响应检查请求
  • 或主动回传数据

那直接贴着宿主机跑,反而更贴合它的角色。

3. 这样更容易把“监控中心”和“被监控对象”分开#

最终这套结构就变成了:

  • 独立 VM:作为监控中心
  • Docker:承载 Zabbix Server / Web / DB
  • 宿主机 agent2:负责把宿主机本体暴露给 Zabbix 监控

这套结构在逻辑上很清晰:

  • 监控中心是一层
  • 被监控宿主机是另一层
  • 两者通过 agent 通信

从架构上看,这比“所有东西全堆在宿主机里”要更像一个可以长期维护的方案。

但这套方案后来踩了一个很典型的坑#

这也是我后来真正觉得值得写出来的部分。

部署完成后,前端、登录、仪表盘都正常,结果问题页里却一直躺着一条 一般严重 告警:

Linux: Zabbix agent is not available (for 3m)

出问题的主机正是:

  • Zabbix server

也就是我那台跑监控中心的 VM 对应的监控对象。

一开始这种告警很容易让人往错误方向想,比如:

  • 是不是前端坏了
  • 是不是 Zabbix server 本身有问题
  • 是不是 agent2 没启动
  • 是不是数据库连接炸了

但实际查下来,都不是。

先确认 agent2 自己没死#

在宿主机上检查:

Terminal window
systemctl status zabbix-agent2
ss -lntp | grep 10050

结果都正常:

  • zabbix-agent2 处于 active (running)
  • 10050 端口正在监听

这说明至少从宿主机本地视角看,agent2 是活着的。

真正的问题在访问来源控制#

继续看 zabbix_agent2.conf

Server=127.0.0.1
ServerActive=127.0.0.1:10051
Hostname=Zabbix server

这里的问题一下就出来了。

Server= 的含义不是“agent 要去连谁”,而是:

哪些 Zabbix server / proxy 被允许来请求这个 agent。

而当时我的 zabbix-server 并不在宿主机本体上,它跑在 Docker 里。

继续查容器 IP:

Terminal window
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}} {{end}}' zabbix-server

结果是:

172.18.0.4

于是问题就完全坐实了:

  • zabbix-agent2 只允许 127.0.0.1
  • zabbix-server 实际来源是 172.18.0.4
  • 所以 server 发起的被动检查会被 agent2 拒绝
  • 最终触发:
Linux: Zabbix agent is not available (for 3m)

这其实就是这类“容器里的 server + 宿主机上的 agent”架构最常见的坑之一:

从宿主机本地看,agent2 没问题;但真正访问它的不是 localhost,而是 Docker 网络里的容器。

最终修复方式#

最直接的修法是先把当前容器 IP 放进 Server=

Server=127.0.0.1,172.18.0.4

然后重启 agent2:

Terminal window
systemctl restart zabbix-agent2

这样可以立刻恢复。

但这种写法不够稳,因为 Docker 容器 IP 以后可能会变。

所以最后我改成了更合理的网段方式:

Server=127.0.0.1,172.18.0.0/16
ServerActive=127.0.0.1:10051
Hostname=Zabbix server

这样做的好处是:

  • 当前容器来源被放行
  • 以后容器重建、IP 变化,只要还在这个 Docker 网段里,就不需要再改一次

这次经历真正让我确认了什么#

回过头看,这次部署和排障让我更确认了一件事:

把 Zabbix Server 放进独立 VM,再用 Docker 跑组件,本身是一个合理选择;但一旦监控对象跨越了“容器”和“宿主机”两个层次,就一定要认真处理好两者的网络关系。

换句话说,这套架构的问题不在于“选错了”,而在于:

  • 架构是对的
  • 但细节必须跟上

尤其是 agent 侧的:

  • Server=
  • ServerActive=
  • Hostname
  • 容器来源 IP / 网段

这些配置如果还按“全部都在同一台裸机上”的思路去写,就很容易踩坑。

最后的结论#

如果现在让我重新总结这次部署选择,我会这么说:

为什么我没把 Zabbix Server 直接放在 PVE 宿主机上#

因为我更看重:

  • 宿主机少耦合
  • 监控中心可隔离
  • 数据库和写盘负担不要直接压在宿主层
  • 后续升级、备份、迁移、回滚都更清楚

为什么我最后选择“独立 VM + Docker”#

因为这符合我的维护习惯,也适合 Zabbix 本身的组件结构:

  • VM 提供隔离边界
  • Docker 提供服务编排边界
  • 整套监控中心更容易保留和迁移

为什么 agent2 仍然留在宿主机上#

因为我要监控的是宿主机本体,而不是某个容器视角下的宿主机投影。

这套方案真正需要注意什么#

只要 server 在容器里、agent 在宿主机上,就必须记住:

宿主机本地的 127.0.0.1,并不等于 Docker 里的 server 容器。

如果不把容器来源放进 agent 的允许列表里, 前端看上去一切正常,问题页里照样会给你躺一条:

Linux: Zabbix agent is not available (for 3m)

这类问题表面上像“agent 挂了”,实际上很多时候只是:

容器网络视角和宿主机本地视角,不是同一个世界。

为什么我把 Zabbix Server 放进独立 VM 的 Docker,而把 agent2 留在宿主机上
https://blog.yangyus8.top/posts/20260324-02/
作者
杨与S8
发布于
2026-03-24
许可协议
CC BY-NC-SA 4.0