+135 410 16684Mon. - Fri. 10:00-22:00

使用zabbix_sender主动发送监控数据到zabbix server

【zabbix】使用zabbix_sender主动发送监控数据到zabbix server

使用zabbix_sender主动发送监控数据到zabbix server

通常来讲,大部分的zabbix key都是预先在server端定义好抓取数据的行为(比如抓取脚本,时间间隔,单位),然后由server端按照这个行为去向agent索要数据,或者 由agent按照这个行为往server端发送数据,但都逃不开“定时”这个概念,即抓取数据的时间间隔是固定的。有一种场景,需要监控的数据不是随时 (比如cpu,load)都能拿到的,而是由某些程序不定时的产生(比如crontab,某些用户触发的数据),这时候再用常规的zabbix key解决起来就不是那么的优雅,而且很难做到实时,理想的做法应该是程序产生监控数据的同时便将数据发送到zabbix server,这种做法类似于大名鼎鼎的newrelic,通过在程序端部署agent,然后发送数据到newrelic服务器。甚幸,zabbix提供类似的功能来帮助我们完成这种需求。
zabbix_sender:
zabbix提供的二进制程序,可以用来发送数据到zabbix server,具体用法如下:

>>> zabbix_sender -z 10.0.0.1 -p 10051 -s "rs_solrmaster" -k "get_number_of_solr_index_update" -o 4
需要注意的是get_number_of_solr_index_update这个key是需要事先创建在rs_solrmaster这台主机上的,并且是zabbix trapper类型的,并且是zabbix trapper类型的,并且是zabbix trapper类型的。
#usage: zabbix_sender [-Vhv] {[-zpsI] -ko | [-zpI] -T -i  -r} [-c ]
#Options:
#  -c --config                    配置文件的绝对路径
#  -z --zabbix-server             zabbix server或者zabbix proxy的ip地址
#  -p --port                      zabbix server或者zabbix proxy的端口,默认是10051
#  -s --host                      主机名,指的是zabbix里配制的主机名
#  -I --source-address            源ip,干嘛使得
#  -k --key                       zabbix trapper key名字
#  -o --value                     需要发送的监控项值
#  -i --input-file <input type="text" />   通过文件的方式批量发送数据,每行一个纪录,格式为hostname,key,value,使用空格分割,如果hostname有空格,使用双引号。
>>> cat trapper_key.txt
"zabbix server" key1 10
"zabbix proxy" key2 20
10.0.0.2 key3 30
>>> zabbix_sender -z 10.0.0.1 -i trapper_key.txt

如此,我们便可以随时随地的向zabbix发送监控数据了,由于zabbix只提供了zabbix_sender这个二进制程序来发送数据,如果要 在程序中调用,直接调用一个二进程程序是一种很丑陋的做法,那么有没有更优雅的办法呢(zabbix的api部分并没有trapper key的相关说明)。有,注意前方高能。
zabbix_sender跟zabbix_server的通信方式是tcp协议的,也就是说zabbix_sender发送给一个DATA段为固定格式 的tcp数据包给zabbix_server,然后server端解析并处理请求。所以这个DATA段的数据格式如果能确定,那我们就可以使用程序来模拟 zabbix_sender了。
先来看两篇文章:
zabbix sender协议的研究 | itnihao
zabbix sender 2.0 协议文档
从中可以整理出一个完整的zabbix_sender请求包的格式如下:

源端口 目标端口
32位序列号
32位确认号
4位首部长度 保留(6位) URG ACK PSH PST SYN FIN 16位窗口大小
16位校验和 16位紧急指针
可选项
Z B X D 协议版本 8位数据长度 请求或者响应的json数据

可以看到基本的zabbix_sender数据协议即为’Z”B”X”D’ + 1位的协议版本号 + 8位的数据长度 + json数据组成,如文档,请求的json数据格式为:

{
        "request":"sender data",
        "data":[
                {
                        "host":"Host name 1",
                        "key":"item_key",
                        "value":"33"},
                {
                        "host":"Host name 2",
                        "key":"item_key",
                        "value":"55"
                }
        ]
}

这样,基本的程序逻辑就出来了,即为:
♠ 构建符合zabbix_sender协议的数据包,格式为 ‘4sBqns’ (n为json串的长度)
♠ 发送数据包至zabbix_server的端口
♠ 处理响应
如下为一个python的实现示例:

#!/usr/bin/env python

import struct
import json
import socket


class zabbix_sender:
        def __init__(self,zbx_server_host,zbx_server_port):
                self.zbx_server_host=zbx_server_host
                self.zbx_server_port=zbx_server_port
                self.zbx_header='ZBXD'
                self.zbx_protocols_version=1
                self.zbx_send_value={'request':'sender data','data':[]}
        def adddata(self,host,key,value):
                add_data={'host':host,'key':key,'value':value}
                self.zbx_send_value['data'].append(add_data)
        #按照协议封装数据包
        def makesenddata(self):
                zbx_send_json=json.dumps(self.zbx_send_value)
                zbx_send_json_len=len(zbx_send_json)
                self.zbx_send_data=struct.pack("<4sBq"+str(zbx_send_json_len)+"s",'ZBXD',1,zbx_send_json_len,zbx_send_json)
        def send(self):
                self.makesenddata()
                zbx_server_socket=socket.socket()
                zbx_server_socket.connect((self.zbx_server_host,self.zbx_server_port))
                zbx_server_write_df=zbx_server_socket.makefile('wb')
                zbx_server_write_df.write(self.zbx_send_data)
                zbx_server_write_df.close()
                zbx_server_read_df=zbx_server_socket.makefile('rb')
                zbx_response_package=zbx_server_read_df.read()
                zbx_server_read_df.close()
                #按照协议解数据包
                zbx_response_data=struct.unpack("<4sBq"+str(len(zbx_response_package) - struct.calcsize("<4sBq"))+"s",zbx_response_package)
                return zbx_response_data[3]

if __name__ == '__main__':
        zabbix_sender=zabbix_sender('10.0.0.1',10051)
        zabbix_sender.adddata('solrmaster','get_number_of_solr_index_update',60)
        response=zabbix_sender.send()
        print response