感谢作者方傅皓敏、傅皓樑供稿!欢迎投稿分享你的使用经验。
傅皓樑
- Zabbix5.0中文手册译者
- 就职于某知名金融公司,从事于运维架构领域相关工作,负责公司AIOPS架构方面工作。
- 对于开源监控、数据库、分部署存储等方面有丰富的实践经验。
【背景】由于公司业务平台的网络环境苛刻,以Zabbix server为核心开发设计一套适应性强的监控运维境更强的方案,不仅能满足当下的需求还能方便后续扩展。写这篇文档的初衷希望遇到类似环境的同学有一个参考,在碰到严格、复杂的网络环境,数量庞大机器管理,如何能够利用 Zabbix 的特性做到深度监控。
需要面对的挑战
- 端口受限,我们的网络环境非常苛刻,节点系统对外只开放访问 22、80(部分),服务端有 80、443 端口。
- 带宽受限,服务端、节点系统分布在不同区域,连接靠一根多个系统公用千 M 光纤,在保证监控完整的同时不能影响业务。
- 数量庞大,目前近万台节点,还在不断在增加,监控项 30W 。
- 如何统一管理,如何批量部署客户端、更新监控项配置。
- 监控数据如何写入 Zabbix,zabbix server 是否扛得住。
- 什么数据库才能抗住这么大数据量,后面如何扩展。
终版配置环境
- Zabbix5.4 server*1。
- TiDB 分布式数据库,TiDB 采用 TiDB3、tikv3、pd*3。
- RabbitMQ*1。
- Jenkins server1, Jenkins node3。 脚本和客户端采用 python 和 go 编写。
终版监控架构图
架构说明
- 监控模块,数据从右往左,agent->server->rabbitmq->脚本处理->ZabbixServer(ZabbixApi)->Zabbix 数据库(Tidb)。
- Agent 部署模块,从 server 提取当前 agent 注册清单->并入 agent 总清单->对比 agent 和总清单的差异,对于那些没有安装 agent 主机通过 ssh 协议远程连接上进行执行署脚本。
解决问题
第一阻力:端口受限,只能用 22、80,那么如何部署 zabbix agent 和 zabbix server,为什么不用的 zabbix 分布式让 zabbix agent 主动上报,而采用当前的架构?
解决方案:
目前用的最多 zabbix 分布式如下图,那为什么我们不用呢?zabbix agent 上报数据需要访问 zabbix proxy 10051 端口,通常整个环境也是在一个局域网内亦或者整个网络环境可以调控。
而我们的网络环境如下,环境限制问题有:
- 客户端只能和网关进行相连,中心开放的端口只有 80,客户端只开放 80,443,22。
- 因为中心机房网关是单线光前通往 agent 所在地,同时网关也用于其他业务,对于带宽的要求也要进一步压缩。
- Zabbix agent 包都比较大,远程下发包的流量也会影响带宽的使用。
- 从这几方面考虑无法通过 zabbix agent 进行上报数据方式。
通过 go 编写 ws agent 主动上报,这样只要 ws server 端开 80 端口就能解决此问题,go 的 agent 压缩后只有 3M,可以解决包过大传输的问题和 3、4 问题,演变的数据上报架构如下。
- Server 是使用 go 写的 websocket 服务端简称 ws,agent 上部署了 2 个服务(webscoket 客户端和监控服务都是用 go 自己写的)。
- 客户端上的监控服务通过本地的配置文件会不断异步执行监控项存储至内存中,客户端上的 ws agent 也可以执行 ws server 下发的命令。
- 通过 jenkins 定时任务发送给 ws server 让其转发送给所有 ws agent,上报自身监控数据给 ws server,后者将数据存放至 rabbitmq 的监控项数据队列中,这里要说明一下,rabbitmq 队列要设置 TTL,不然一旦消费者挂了数据囤积后会把 mq 内存给打满了,这里我们对 ws server、消费者都通过脚本进行监测使其能够自愈。
第二阻力:数据在 mq 中如何消费到 zabbix 中的近万台主机的监控项中呢,采用什么方式?
解决方案:
- RabbitMQ 和 zabbix server 都在都在同一网络中,这样网络限制就会小很多,这当中第一个要解决的是如何创建监控项,因为监控项都是不定式的,例如多个磁盘,多个服务等,这样只能采用自动发现,常用的自动发现 2 种:一种通过自制脚本创建自动发现监控项,这种一般都是定式的,已经知道有哪些监控项,如知道目前主机上有 3 个磁盘,通过自动发现就可以节省每个主机上手动创建 3 个监控项,还有一种通过在 zabbix agent 主机执行脚本获取待创建监控项队列,
- 我们使用的是第三种通过 zabbix sender 发送给 zabbix server 数据的方式创建监控项例如:
zbx_sender -z zabbix_server的ip -s hostip -key 自动发现的key -o {“data”:[“#{ITEM_NAME}”:“监控项名字”,”#{ITEM_KEY}”:“监控项key”]}
这个是 zabbix 自带的命令,可以使用 python、go、java 等语言自己写一个,我们是用 python 写的。第一个问题解决了,下面就是第二个自动发现的监控项都有存活周期,这个周期如何更新,例如:自动发现设置 7 天后删除,那 7 天后失效了怎么办,经过测试上面创建监控项命令对存在的监控项再次创建就会更新监控项存活时间,例如昨天创建的监控项到今天还有 6 天就要被删除了,那今天再次创建监控项就会从今天开始计时往后延长 7 天。最后一个问题就是插入数据了,这个比较容易解决,命令提供在下面,在这里我们发现一个坑不知道大家有没有注意,使用 zbx_sender 时发送的数据时间是执行命令时的,而监控项数据在客户端采集到执行命令会有时间差,这个问题在自制 zab_sender 中已经解决,里面有个参数是 value_clock,脚本网上可以找到,看似问题都解决了其实不然。插入监控项数据命令。
代码语言:javascript复制zbx_sender -z zabbix_server的ip -s hostip -key 监控项key -o 监控项值
我们的监控项数量有30W ,相信大家的数量也不会少,我们 agent 完成后大约有 100W 的监控项,那如果监控项间隔为 5 分钟,相当于每次创建监控项任务要 5 分钟发送 100W 次,zabbix server 的 lld 进程最多只能开启 100 个,发送命令可以分布式多线程、多进程来发送,但是接收只有 1 个 zabbix server,我们运行到后面 zabbix server 就出现不会执行队列(创建监控项、更新监控项、插入数据)了,队列全部溢出。
对于这个问题我们尝试了 以下2 种方式,虽然全部失败但是也积累了经验。
1. 双 zabbix server 模式
这个模式查了相关文档没人用过,使用后会产生 2 个告警,查了源码和相关文档后找出原因,监控项数据发送给 zabbix server 后,zabbix server 会匹配对应监控项的触发器规则,如果匹配后发送给数据库,这样就不难理解会出现 2 个告警了,几个 zabbix server 就会有几个 zabbix server,这样问题还难不倒我们,一个 zabbix server 接收监控项数据,一个 zabbix server 接收创建监控项及更新监控项,但是就是这样对于大量更新监控项也扛不住,通过分析 lld 大部分工作在于更新监控项存活时间,从日志跟踪中找到下面的 sql,而创建监控项通常就创建一次,所以只要解决了这个问题就完美了。
代码语言:javascript复制update item_discovery set lastcheck=1624511695 where itemid=1674491
通过分析 zabbix server log 每一个触发更新监控项会输出 133 行日志,至少有 50 个以上的动作,那 100W 监控项的工作量可想而知了,如果采用批量 update 直接对数据库进行操作就可以解决这个问题了。解决方法如下,在消费监控项数据时把对应节点监控项输出一份到监控项需更新队列中,通过脚本汇聚这些监控项与 zabbix 数据库 items 表进行关联取差集得到需要更新的监控项 id,然后使用 update 批量更新,sql 如下,item_free 表为关联后取得 itemid 写入表中,不然不好操作:
代码语言:javascript复制Update item_discovery set lastcheck=UNIX_TIMESTAMP(NOW()),ts_delete=UNIX_TIMESTAMP(NOW()) (7*24*3600) where itemid in (select itemid from item_free)
2.zabbix proxy 模式
zabbix proxy 不支持 lld,所以发送创建监控项数据后,不会创建监控项,查看配置文件就没有 lld 的进程,查了下原理也释然,zabbix proxy 是定时读取 zabbix server 的配置数据保存在 zabbix proxy 数据库中,如果能够创建监控项信息,那多个 zabbix proxy 数据上传时会产生数据冲突。
第三阻力:什么数据库才能抗住这么大数据量,后面如何扩展?
我们数据库从 mysql->postgresql->tidb 不断进行迭代,从 mysql 说起吧,现在还在 mysql 的通常都是分表分库的方式,单标 1000W 已经影响性能了,当初 mysql 优化到极限单标 9000W 数据,查询 3 秒,面对现在动不动几亿的数据量实在扛不住,postgresql 是我们上一代的 zabbix server 数据库,单表性能没得说,目前各个 zabbix 场景下用的最多的就属他了,但是 pg 是单点的不能用于扩展,看着每天增加的指标量,数据阶梯式增长,在这种情况下分布式数据库是必选的方案,tidb 是开源的分布式数据库,想要扩展性能可以通过横向扩节点的方式,简单容易上手、社区活跃、官方解答问题非常积极,对于我们运维人员是一个福音,再更新数据库后虽然问题很多,但是大势所趋目的办法。
碰到的问题
问题 1:zabbix server 日志排查问题?
我们的架构核心是通过 zabbix_sender 发送 zabbix_server 创建监控项、更新监控项、更新监控项值,在更新自动发现监控项存活和创建监控项时遇到 zabbix_server 处理不过来,具体的排查通过查看 zabbix server 的 lld 日志来判断,这里如果直接打开 debug 日志满屏刷无法定位问题,通过设置日志文档来打开 lld 日志输出,具体参考:https://www.zabbix.com/documentation/5.0/manpages/zabbix_server
问题 2:如何快速更新自动发现监控项存活?
监控项数据增加监控项更新操作就会非常多,统计了下有 133 行日志,创建 lld 监控项多好几倍,自动发现(lld)都压在 zabbix_server 上,通过日志发现一个监控项的更新执行逻辑步骤非常多,那在多并发的情况下 zabbix_server lld 进程数量就 100,大量的 update 可能还会触发相互锁的问题,而 zabbix proxy 还不支持 lld 进程,通过日志发现更新最终就是执行一个 updatesql,如下:
代码语言:javascript复制update item_discovery set lastcheck=1624511695 where itemid=1674491
思索后通过把需要更新监控项聚合后通过 sql 批量更新,直接对数据库操作不仅速度更快,还大大减少了 zabbix server 的压力,sql 操作如下,对统计后的 itemid 批量更新。
代码语言:javascript复制Update item_discovery set lastcheck=UNIX_TIMESTAMP(NOW()),ts_delete=UNIX_TIMESTAMP(NOW()) (7*24*3600) where itemid in (select itemid from item_free)
参考资料
- zabbix 5.4 编译安装
# 前置库安装
yum install unixODBC-devel mysql-devel net-snmp-devel libxml2-devel libcurl-devel libevent-devel gcc -y
useradd zabbix
#安装zabbix5.4
cd /opt/zabbix-5.4/zabbix-5.4.0/
./configure --prefix=/usr/local/zabbix --sysconfdir=/etc/zabbix/ --enable-server --enable-agent --enable-ipv6 --with-net-snmp --with-libcurl --with-mysql --with-libxml2
make && make install