夜雨聆风学习资料网

ARTICLE · 1196625

IP 台账在 Excel 里躺了三年我把它搬进了 Zabbix

IP 台账在 Excel 里躺了三年我把它搬进了 Zabbix
周一早上,同事在群里问了一句:「10.100.100.20 这个地址谁在用?」

我先翻共享目录里那份《IP 分配表.xlsx》,上次更新是去年十月;又去 Zabbix 里按主机一个个对接口,最后在交换机的配置里搜了一遍 MAC。前后二十分钟,答案还是「不确定」。

这事让人别扭的地方在于:Zabbix 明明知道每一台机器的每一个接口地址,可它就是不告诉你「哪个网段还剩多少、哪个地址归谁、有没有人重复用了」。采集能力没得说,唯独缺一本 IP 台账。

监控工具知道机器活着,却不知道它住在哪个网段。

于是我在「资产记录」下面加了一个 IP 地址管理(IPAM)页面。这篇文章拆给你看:功能怎么定的、数据落在哪、具体改了哪些文件,以及两个让我多花一小时的坑。截图全部来自改造后的真实环境,数据也都是真机上跑出来的。

1先想清楚:IPAM 到底该有什么

动手前我先把 NetBox、phpIPAM 这几套主流 IPAM 的功能翻了一遍,抄它们的模型,而不是自己拍脑袋。最后定下来七块:

① 网段 / 子网:CIDR、网关、VLAN、VRF、机房、用途,使用率超过 80% 预警、90% 告警。
② IP 地址:状态(空闲 / 已分配 / 保留 / 网关 / 动态 / 冲突)、绑定的 Zabbix 主机、MAC、负责人、用途。
③ VLAN:VLAN ID、名称、机房,网段可关联。
④ 变更审计:分配、回收、新建、删除全部留痕,含操作人和时间。
⑤ 异常检测:IP 冲突、未登记(监控在用但没台账)、网段外、幽灵登记(台账写着在用,监控里早就没了)。
⑥ 导出:IP 清单、网段清单 CSV,带 BOM,Excel 双击直接开。
⑦ 从 Zabbix 同步:一键把主机接口上真实在用的 IP 拉进来登记。

先抄成熟产品的数据模型,再动手写第一行代码——这一步省下的返工,比什么都值。
2数据落在哪:4 张表,一行都不碰 Zabbix 原表

这是二开里我特别在意的一条:绝不改 Zabbix 自带的表结构。新增的资产数据全部放进自己的扩展表,Zabbix 升级时互不干扰,回滚也就是一条 DROP。四张表对应四个概念:

ipam_subnet 网段 / 子网 ipam_address IP 地址
ipam_vlan VLAN / VRF ipam_log 变更审计日志

CREATE TABLE IF NOT EXISTS ipam_address (   addressid   integer      NOT NULL AUTO_INCREMENT,   subnetid    integer      NOT NULL DEFAULT 0,   -- 0 = 未归属任何网段   ip          varchar(64)  NOT NULL DEFAULT '',   status      integer      NOT NULL DEFAULT 0,   -- 空闲/已分配/保留/网关/冲突/动态   hostid      integer      NOT NULL DEFAULT 0,   -- 绑定的 Zabbix 主机   hostname    varchar(128) NOT NULL DEFAULT '',  -- 冗余主机名,便于检索   mac         varchar(64)  NOT NULL DEFAULT '',   owner       varchar(64)  NOT NULL DEFAULT '',  -- 负责人 / 使用方   purpose     varchar(64)  NOT NULL DEFAULT '',   source      integer      NOT NULL DEFAULT 0,   -- 0 手工登记 / 1 Zabbix 同步   updated     integer      NOT NULL DEFAULT 0,   PRIMARY KEY (addressid),   UNIQUE KEY  ipam_address_1 (ip) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

三个设计细节值得一提:IP 列加唯一键,重复登记直接覆盖,从根上杜绝同址多条;subnetid = 0 表示还没归入任何网段,删除网段时只把它置 0,不删地址;source 字段区分手工登记和同步来的,这样「从 Zabbix 同步」才知道哪些能自动回收、哪些是人辛辛苦苦填的不能动。建表全部用 CREATE TABLE IF NOT EXISTS,脚本可以反复执行。

3后端:一个控制器,参数驱动十种动作

页面没有做成一堆独立入口,而是一个 action 承载全部操作:zabbix.php?action=ipam.view,靠 URL 参数区分动作。控制器里按顺序判断,处理完再统一渲染页面:

protected function doAction(): void {   if ($this->hasInput('export'))      { $this->exportCsv(...); }   if ($this->hasInput('subnet_save')) { $this->saveSubnet(); }   if ($this->hasInput('subnet_del'))  { $this->deleteSubnet(); }   if ($this->hasInput('vlan_save'))   { $this->saveVlan(); }   if ($this->hasInput('vlan_del'))    { $this->deleteVlan(); }   if ($this->hasInput('ip_save'))     { $this->saveAddress(); }   if ($this->hasInput('ip_release'))  { $this->releaseAddress(); }   if ($this->hasInput('ip_del'))      { $this->deleteAddress(); }   if ($this->hasInput('sync'))        { $this->syncFromZabbix(); }   if ($this->hasInput('plan'))        { $this->planSubnets(); }   // ... 组装数据 → getResponse(new CControllerResponseData($data)) }

权限也在这里收口:读操作所有登录用户可见,凡是写操作(建网段、登记 IP、同步、删除)统一要求管理员以上,只读的值班账号在页面上根本看不到那些按钮。

两个能力是这个模块真正的价值所在。一是 syncFromZabbix():直接读 Zabbix 的主机接口表,把真实在用的 IP 批量登记进来,按 CIDR 自动归入网段,监控里已经消失的地址自动回收为空闲,同一个地址被多台主机占用的直接标成「冲突」。二是 planSubnets():网段一条都没有的时候,按监控中真实在用的地址反推出 /24 网段,一键补建——不需要我先去把网段表填一遍。

台账的价值不在「记下来」,在于它会自己跟真实情况对账。
4怎么挂进 Zabbix:路由、菜单、视图

Zabbix 7.x 是标准 MVC,挂一个新页面要做三件事:注册路由、加菜单、放视图。路由在 CRouter.php 的映射表里加一行,用正则锚定已有的 hostinventories.view 行,插在它后面:

'ipam.view'  =>  [CControllerIpam::class,  'layout.htmlpage',  'inventory.ipam.view'],

菜单在 CMenuHelper.php 里,往「资产记录」子菜单的「主机」下方塞一个菜单项,可见性交给已有的权限常量控制,没权限的角色连入口都看不到:

CWebUser::checkAccess(CRoleHelper::UI_INVENTORY_HOSTS)   ? (new CMenuItem(_('IP 地址管理')))       ->setAction('ipam.view')   : null,

这两处都得改官方文件,所以我把它们写成可回滚的补丁脚本:改之前先留 .ipam.bak,脚本支持 php patch_xxx.php restore 一键还原,重复执行会自己检测到已打过补丁直接跳过。

5两个让我多花一小时的坑

坑一:视图文件名里不能有数字。路由写的是 ipam.view,我顺手把视图文件命名成 monitoring.idc3d.view.php 那一路的写法,结果页面直接报 Invalid view name——Zabbix 校验视图名的正则是 /^[a-z]+(\/[a-z]+)*(\.[a-z]+)*$/,数字不在允许范围内。最后 action 名保持 ipam.view(action 名不受这个限制),视图文件老老实实叫 inventory.ipam.view.php。

坑二:菜单补丁插错了地方。我第一版补丁的逻辑是「找到锚点,再往下找下一个 : null, 插进去」,执行完脚本报 PATCHED,看着挺顺利。可打开页面一看,菜单里根本没有新入口——直接输 URL 却能打开。

原因很隐蔽:「主机」是资产记录这个菜单数组的最后一项,它结尾的 : null 后面没有逗号,于是往下找到的第一个 : null, 已经跑到报表子菜单里去了,菜单项被插到了不该在的位置。改成正则精确匹配「锚点 + 数组结束的 ];」才插对。教训是:打补丁不能只看脚本输出,一定要打开页面点一遍。

6改了哪些文件

这就是这次改造的完整清单——5 个新增文件,2 个官方文件打补丁(均带备份),没有动过一行 Zabbix 核心逻辑:

zabbix-ipam/sql/ipam_schema.sql

建表脚本。4 张扩展表 ipam_subnet / ipam_address / ipam_vlan / ipam_log,幂等可重复执行,回滚只需一条 DROP。

zabbix-ipam/CControllerIpam.php → ui/app/controllers/

控制器,约 1240 行。取数、使用率统计、异常检测、十种写动作、CSV 导出、从 Zabbix 主机接口同步、按 /24 补建网段。

zabbix-ipam/ui/inventory.ipam.view.php → ui/app/views/

视图,约 540 行。KPI 仪表条、网段卡片墙、IP 地址表、登记表单、异常检测面板、VLAN、变更记录(文件名不含数字,原因见上一节)。

zabbix-ipam/ipam.css → ui/assets/styles/

样式。含浅色 / 暗色两套主题适配。

zabbix-ipam/patch_router_ipam.php

改 include/classes/mvc/CRouter.php,注册 ipam.view 路由。备份 CRouter.php.ipam.bak,支持 restore 回滚。

zabbix-ipam/patch_menu_ipam.php

改 include/classes/helpers/CMenuHelper.php,在「资产记录 → 主机」下方插入入口。备份 CMenuHelper.php.ipam.bak,支持 restore 回滚。

scripts/deploy_ipam.sh

一键部署:建表 → 上传文件 → 打两个补丁 → php -l 逐个校验 → 重启 php-fpm。

二开的第一原则:能新增就别修改,非改不可就留好备份和一条回滚命令。
7实测:拿真机跑一遍

部署完第一次打开页面,网段和地址都是空的——可异常检测那一栏已经自己报出一条:10.100.100.3(Zabbix server)监控在用,但没有任何登记记录。这一条正好说明了这个模块的意义:它从一开始就拿真实数据在对账。

然后按真实操作顺序走了一遍:点「按 /24 补建网段」,系统根据监控中在用的地址自动建出 10.100.100.0/24;点「从 Zabbix 同步」,真实 IP 全部登记入库;新建一个 VLAN 100 并关联到网段;手工登记 10.100.100.20 为「保留」(负责人填的张三);再把它回收、重新同步恢复;最后导出 CSV,Excel 直接打开无乱码。全程在浏览器里点,没有碰过命令行。

▲ 改造后的 IPAM 页面:KPI 仪表条 + 网段卡片墙 + IP 地址表

▲ 主区:网段卡片、IP 地址表(含状态徽章与来源标记)、登记表单、异常检测

▲ 侧栏:新建网段表单、VLAN 管理、变更记录(每一次操作都留痕)

暗色主题也一起适配了——值班的同学夜里开着暗色模式,不至于被一块白底卡片晃到眼睛。

▲ 暗色主题下的同一套页面

8写在结尾

这次改造前后大概一天,回头看,真正花时间的不是功能代码,而是让这套东西融进 Zabbix 本身:菜单入口要在对的位置、权限要跟着角色走、视图名要守它的规矩、改过的官方文件要能一键还原。

监控负责告诉我们谁活着,台账负责告诉我们谁住哪——两者接起来,CMDB 才真的能用。
你们的 IP 台账现在记在哪?Excel、Wiki,还是干脆没记?评论区聊聊,下篇写 CMDB 资产台账是怎么搬进 Zabbix 的。

相关学习资料