check_mk深度剖析之C/S数据交互

2015年3月13日 发表评论 阅读评论

了解并使用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 模式下的采集数据的工作主要交给了被监控端,这里的被监控端是主动的 。监控端所做的工作就是接收数据,在接收到本地后,再对数据进行处理。




本站的发展离不开您的资助,金额随意,欢迎来赏!

You can donate through PayPal.
My paypal id: itybku@139.com
Paypal page: https://www.paypal.me/361way

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.