马哥Linux运维原创作者投稿

zabbix 自定义监控--监控redis

2017-12-18  本文已影响34人  进击的胖达

环境:CentOS6.5
Python:2.7

1、添加redis监控脚本(端口自动发现脚本)
discovery_redis_port.py

#!/usr/bin/env python 
import os
import json 
import commands

[stat, rtv] = commands.getstatusoutput(r''' sudo netstat -tlpn |grep redis-serv|grep 0.0.0.0|awk '{print $4}'|awk -F: '{print $2}' ''')
ports = []
for port in  rtv.split('\n'):
        r = os.path.basename(port.strip())
        ports += [{'{#REDISPORT}':r}]
[stat, rtv] = commands.getstatusoutput(r''' /usr/bin/python discovery_redis_conf.py ''')
print json.dumps({'data':ports},sort_keys=True,indent=4,separators=(',',':'))

ps:由于涉及到有些redis设置有密码而有些没密码,所以这里写了一个自动搜索对应端口的密码的脚本,搜索目录为/usr/local下放置的所有redis目录,本脚本完成redis端口发现和redis端口对应的passwd的查找,代码生成的conf文件可以手工维护,如新增,不会被代码下次执行覆盖,但是如果key值冲突则会被代码以搜索到的内容覆盖。代码如下
discovery_redis_conf.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json 
import commands
import os
import pprint

def Get_File_Path():
    rootdir = "/usr/local"
    file_path_list = []
    for parent,dirnames,filenames in os.walk(rootdir):
        for filename in filenames:
            if filename == "redis.conf":
                file_path = parent + os.sep + filename
                file_path_list.append(file_path)
    return file_path_list

def Get_Dict(file_path):
    result = {}
    with open(file_path) as f:
        all_text = f.read()
        [ result.update({i.split(' ')[0]:i.split(' ')[1]}) for i in all_text.splitlines() if i.strip() and not i.strip().startswith('#') ]
    return result

def Get_Conf_Dict(file_path_list):
    dict = {}
    for file in file_path_list:
        result = Get_Dict(file)
        port = result.get('port', 'None')
        requirepass = result.get('requirepass', '')
        dict[port] = requirepass 
    return dict

def Get_Old_Conf(conf_file_path):
    if os.path.exists(conf_file_path):
        with open('redis_conf.json', 'r') as json_file:   
            dict = json.load(json_file)['data']
    else:
        dict = {} 
    return dict    

if __name__ == '__main__':
    old_conf_path = 'redis_conf.json'
    dict = Get_Old_Conf('redis_conf.json') 
    file_path_list = Get_File_Path()
    dict = Get_Conf_Dict(file_path_list)
    conf_dict = {'data':dict}
    pprint.pprint(conf_dict)
    with open('redis_conf.json', 'w') as json_file:   
        json.dump(conf_dict, json_file, ensure_ascii=False)
    [stat, rtv] = commands.getstatusoutput(r'''sudo chown zabbix:zabbix redis_conf.json ''')

两个脚本放置到同一个目录下,执行第一个脚本:

{
    "data":[
        {
            "{#REDISPORT}":"6379"
        },
        {
            "{#REDISPORT}":"6390"
        },
        {
            "{#REDISPORT}":"6500"
        }
    ]
}

ps:zabbix对自动发现脚本返回的数据类型有要求,必须是这种的json格式的数据
自动发现脚本添加之后就是添加处理方法,为了满足一台主机上安装有多个redis且密码各不相同的情况,不能简单用一句话搞定所有的情况,所以也需要一个脚本来搞定,如下:
redis_t.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import json 
import commands
import os
import string
import pprint
from sys import argv

def Get_Mb(str):
    str = str.upper()
    if "MB" in str:
        return int(string.atoi(str[0:-2]))
    elif "M" in str:
        return int(string.atoi(str[0:-1])*1000.0/1024)
    if "GB" in str:
        return int(string.atoi(str[0:-2])*1024)
    elif "G" in str:
        return int(string.atoi(str[0:-1])*1000)
    if "KB" in str:
        return int(string.atoi(str[0:-2])/1024)
    elif "K" in str:
        return int(string.atoi(str[0:-1])*1000.0/1024/1024)
    return int(string.atoi(str)/1024.0/1024)

def Get_Passwd():
    name,port,str = argv
    passwd = ''
    with open("/etc/zabbix/scripts/redis_conf.json", 'r') as load_f:
        load_dict = json.load(load_f)
    for i in load_dict['data'].keys():
        if i == port:
            passwd = load_dict['data'][i]
    if passwd == '':
        passwd = "passwd"
    return passwd

def Get_Info(port, passwd):
    result = {}
    comm = "sudo /usr/local/bin/redis-cli -h 127.0.0.1 -a \"" +  passwd + "\" -p " + port +" info"
    [stat, rtv] = commands.getstatusoutput(comm)
    [ result.update({i.split(':')[0]:i.split(':')[1]}) for i in rtv.splitlines() if i.strip() and not i.strip().startswith('#') ]
    return result
    
def Get_Count_Mem_Fconf():
    result = {}
    path = INFO.get('config_file', 'None')
    with open(path, 'r') as f:
        text = f.read()
    [ result.update({i.split(' ')[0]:i.split(' ')[1]}) for i in text.splitlines() if i.strip() and not i.strip().startswith('#')]
        tmp_cm = result.get('maxmemory', 'None')
    f_cm = Get_Mb(tmp_cm)
    return f_cm

def Get_Count_mem(port, passwd):
    comm = "sudo /usr/local/bin/redis-cli -h 127.0.0.1 -a \"" +  passwd + "\" -p " + port +" config get maxmemory"
    [stat, rtv] = commands.getstatusoutput(comm)
    tmpl = []
    for i in rtv.splitlines():
        tmpl.append(i)
    if tmpl[0] == 'maxmemory': 
        f_cm = Get_Mb(tmpl[1])
    else:
        f_cm = Get_Count_Mem_Fconf()
    return f_cm

if __name__ == '__main__':
    name,port,str = argv
    if str == "proportion" or str == "count_mem":
        passwd = Get_Passwd()
        INFO = Get_Info(port, passwd)
        conf_file = INFO.get('config_file', 'None')
        count_mem = Get_Count_mem(port, passwd)
        if str == "count_mem":
            print count_mem
        else:
            used_mem = string.atoi(INFO.get('used_memory'))/1024.0/1024
            used_mem = int(used_mem)
            mem_proportion = used_mem / float(count_mem)*100
            print mem_proportion
    else:
        passwd = ''
        with open("/etc/zabbix/scripts/redis_conf.json", 'r') as load_f:
            load_dict = json.load(load_f)
        for i in load_dict['data'].keys():
            if i == port:
                passwd = load_dict['data'][i]
        if passwd == '':
            passwd = "passwd"
        comm = "sudo /usr/local/bin/redis-cli -h 127.0.0.1 -a \"" +  passwd + "\" -p " + port +" info|grep " + str + "|cut -d : -f2"
        [stat, rtv] = commands.getstatusoutput(comm)
        print rtv

这里对可分配最大内存有个细节处理,因为redis可以使用命令动态修改内存大小,所以这里是优先选择命令获取redis的实时可分配最大内存,但测试中发现有些客户端连接成功之后执行命令时提示没有config命令,无法完成获取(貌似是redis3以上的都是这样),对于这种情况的redis,取其INFO中获取到的config_file中配置的最大可分配内存大小,如果没有设置,则默认使用128MB大小的内存。
ps:其实如果只是监控redis info中显示的信息,其实只用其中20%的代码就足够,剩下的80%都是为了监控redis可分配的最大内存以及已使用内存所占的百分比
测试我们的自定义脚本

[~~ scripts]# python redis_t.py 6500 memory_rss
8687616
[~~ scripts]# python redis_t.py 6500 count_mem
1024.0

ps:这里的设置的总内存我是把拿到的数据换算成了Mb为单位,而上面的那个数据是B为单位的,因为对于INFO中拿到的数据没做换算处理
到这里我们的自定义方法基本算测试通过了,都可以拿到我们想要的数据,然后就是告诉zabbix来调用我们的监控脚本--添加zabbix键值
键值可以添加在zabbix/zabbix_agentd.conf中,也可以在zabbix目录下新建zabbix_agentd.d和scripts目录来分别存放键值配置和脚本,建议对每个需要新加的键值单独新建一个.conf结尾的文件,如redis.conf等,文件内容如下:

UserParameter=redis.discovery,/etc/zabbix/scripts/discovery_redis_port.py
#这个是自动发现脚本
UserParameter=redis_stats[*], /etc/zabbix/scripts/redis_t.py $1 $2
#这个是监控的具体处理方法

到这里基本就只剩下调试了,先使用zabbix server端进行测试,如果你的zabbix是使用zabbix用户启动运行,不出意外会出现sudo 什么no tty之类的错误(忘记对错误做记录、~~),这个是因为普通用户调用sudo时需要tty窗口输入密码什么,可以设置为不需密码,需要修改/etc/sudoers文件,在文件末尾新增如下内容:

#zabbix ALL=(root) NOPASSWD:/bin/netstat,NOPASSWD: /home/admin/redisCS/redis/src/redis-cli
#上面这种方法是比较安全的做法,只设置监控中需要用到的命令,但是很容易有没发现的命令一直报错、、、
zabbix ALL=(ALL) NOPASSWD: ALL
#这个方法简单省事,相对没那么安全
#Defaults   requiretty  这行要注释掉

还有就是记得所有脚本记得chown到zabbix用户哦
然后重启zabbix-agent

CentOS6:/etc/init.d/zabbix-agent restart
CentOS7:systemctl restart zabbix-agent

登录zabbix server端主机进行测试
测试自动发现端口的脚本

[root@hz-zabbix ~]# zabbix_get -s 192.168.9.142 -k redis.discovery
{
    "data":[
        {
            "{#REDISPORT}":"6379"
        },
        {
            "{#REDISPORT}":"6390"
        },
        {
            "{#REDISPORT}":"6500"
        }
    ]
}

测试获取监控项

[root@hz-zabbix~]# zabbix_get -s 192.168.9.142 -k redis_stats[6500,count_mem]
1024.0

ps:这里如果有看不懂第二个参数的朋友可以仔细看下脚本不难发现这个参数是用来对INFO命令获取的结果进行筛选的,至于获取总内存和百分比的两个参数更是完全可以由我们的代码随便写成什么都可以,比如这里我把可分配总内存写成了count_mem,在代码中做相应的处理即可

对于python和zabbix我都是一个初学者,代码可能只是勉强可以运行,连异常捕获都没做,欢迎大家找茬指正,共勉。

下一篇介绍怎么添加监控模板。——添加监控模板

纯手打,如需转载,请注明出处:http://www.jianshu.com/p/f304cee5ca6e

上一篇下一篇

猜你喜欢

热点阅读