了解并使用check_mk 已经有一两年了,并未深扒过其代码。由于近一年开始会写一些python 代码,在研究socket模块时,想到之前的check_mk 模块,本篇着重分析C/S 之间是如何交互数据的。
一、check_mk-agent与server端
默认在被监控主机上,只需要安装一个check_mk-agent包,由于在当前的check_mk项目站点上未找到check_mk-agent 的rpm包下载地址,这里还是使用 13年写的check_mk及WATO配置 篇里的老的agent 。直接就一个rpm,安装即可。默认安装完成后,会有以下文件:
[root@361way ~]# rpm -ql check_mk-agent /etc/xinetd.d/check_mk /usr/bin/check_mk_agent /usr/bin/waitmax /usr/lib/check_mk_agent/local /usr/lib/check_mk_agent/plugins /usr/share/doc/check_mk_agent /usr/share/doc/check_mk_agent/AUTHORS /usr/share/doc/check_mk_agent/COPYING /usr/share/doc/check_mk_agent/ChangeLog
/usr/bin/check_mk_agent 程序是一个主程序为一个shell 脚本,主要作用就是收集信息,如常见的/proc下的几个目录下信息文件。将信息事先提取后,等待client 端连上来取数据 。
/etc/xinetd.d/check_mk 为xinetd 下的服务配置文件,用于守护check_mk_agent进程,并使其运行监听tcp 6556端口。
/usr/bin/waitmax 程序是做为check_mk_agent shell 脚本中调用到的一个程序,waitmax 程序用于处理shell 下的各命令在处理数据时,避免陷入僵死或长时间等待中,在其指定的最大超时时间内如果仍未结束 --- 即取回结果,就将该程序kill 掉。
/usr/lib/check_mk_agent/plugins 该目录用于存放mrpe 自定义插件。
所以单从check_mk-agent的设计上可以看出其是非常睿智的:
- 避免使用其他语言 ,仅仅使用系统自带指令即可完成基本数据的收集。
- 事先收集好数据,等待client 连接,避免监控主机长时间等待及减少监控主机的负荷。
二、python 实现的监控端
在安装完check_mk-agent 的主机上,通过telnet 127.0.0.1 6556 可以验证该插件是否正常运行,telnet指令获取的信息就是监控端需要获取的数据。如果想要实现这个功能,通过python下的socket模块可以通过如下代码实现:
[root@361way ~]# cat client.py import socket HOST = '192.168.0.110' PORT = 6556 ADDR = (HOST, PORT) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(ADDR) total_data = [] data='' i = 0 while (i<100): data = s.recv(1024) if data: total_data.append(data) i+=1 # print total_data print ''.join(total_data) #print 'the data received is',data s.close()
优化以下,使用如下代码:
[root@361way ~]# cat 2client.py import socket HOST = '192.168.0.110' PORT = 6556 ADDR = (HOST, PORT) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(ADDR) total_data = [] data='' while True: data = s.recv(1024) if data: total_data.append(data) else: break # print total_data print ''.join(total_data) #print 'the data received is',data s.close()
为避免长时间不响应,还可以给其加一个超时器,代码如下:
[root@361way ~]# cat socket_client.py import socket #for sockets import sys #for exit import struct import time #create an INET, STREAMing socket try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error: print 'Failed to create socket' sys.exit() print 'Socket Created' host = '192.168.0.110'; port = 6556; try: remote_ip = socket.gethostbyname( host ) except socket.gaierror: #could not resolve print 'Hostname could not be resolved. Exiting' sys.exit() #Connect to remote server s.connect((remote_ip , port)) print 'Socket Connected to ' + host + ' on ip ' + remote_ip #Send some data to remote server def recv_timeout(the_socket,timeout=2): #make socket non blocking the_socket.setblocking(0) #total data partwise in an array total_data=[]; data=''; #beginning time begin=time.time() while 1: #if you got some data, then break after timeout if total_data and time.time()-begin > timeout: break #if you got no data at all, wait a little longer, twice the timeout elif time.time()-begin > timeout*2: break #recv something try: data = the_socket.recv(1024) if data: total_data.append(data) #change the beginning time for measurement begin=time.time() else: #sleep for sometime to indicate a gap time.sleep(0.1) except: pass #join all parts to make final string return ''.join(total_data) #get reply and print print recv_timeout(s) #Close the socket s.close()
上面指定的host IP为check_mk-agent 主机的IP ,在操作时,可以换成实际的IP 。check_mk官方实现该功能的代码位于modules/check_mk_base.py 文件中750行左右,具体如下:
# Get data in case of TCP def get_agent_info_tcp(hostname, ipaddress, port = None): if not ipaddress: raise MKGeneralException("Cannot contact agent: host '%s' has no IP address." % hostname) if port is None: port = agent_port_of(hostname) try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: s.settimeout(tcp_connect_timeout) except: pass # some old Python versions lack settimeout(). Better ignore than fail vverbose("Connecting via TCP to %s:%d.\n" % (ipaddress, port)) s.connect((ipaddress, port)) try: s.setblocking(1) except: pass output = "" while True: out = s.recv(4096, socket.MSG_WAITALL) if out and len(out) > 0: output += out else: break s.close() if len(output) == 0: # may be caused by xinetd not allowing our address raise MKAgentError("Empty output from agent at TCP port %d" % port) return output except MKAgentError, e: raise except MKCheckTimeout: raise except Exception, e: raise MKAgentError("Cannot get data from TCP port %s:%d: %s" % (ipaddress, port, e))
可以看到官方接收tcp数据部分接近2client.py部分 ,不过每次读取1pagesize(4k)大小的数据,增加了socket.MSG_WAITALL ---在socket模块中socket.MSG_WAITALL 值默认为256 ,其是阻塞模式下尽量读全数据的参数。
三、总结
从代码中可以看出check_mk 在C/S交互部分处理的非常好。将nrpe 模式下的采集数据的工作主要交给了被监控端,这里的被监控端是主动的 。监控端所做的工作就是接收数据,在接收到本地后,再对数据进行处理。