API部分
參考文檔
https://py-zabbix.readthedocs.io/en/latest/
https://github.com/adubkov/py-zabbix
https://www.zabbix.com/documentation/3.2/manual
接口示例:
curl -H 'Content-Type: application/json' -X POST -d '{"jsonrpc":"2.0","method":"user.login","params":{"user":"Admin","password":"zabbix"},"id":1,"auth":null}' http://10.0.0.218:8888/api_jsonrpc.php
> {"jsonrpc":"2.0","result":"9aed9414bc61fd93021bdb6d7d2372ce","id":1}
使用py-zabbix模塊
安裝
pip install py-zabbix
使用py-zabbix模塊操作zabbix的接口
from zabbix.api import ZabbixAPI
# Create ZabbixAPI class instance
zapi = ZabbixAPI(url='http://localhost:8888/', user='admin', password='zabbix')
# Get all monitored hosts
result1 = zapi.host.get(monitored_hosts=1, output='extend')
# Get all disabled hosts
result2 = zapi.do_request('host.get',
{
'filter': {'status': 1},
'output': 'extend'
})
# Filter results
hostnames1 = [host['host'] for host in result1]
hostnames2 = [host['host'] for host in result2['result']]
獲取主機id
getHostid = zapi.do_request('host.get',
{
'filter': {'host':['yumOS']},
'output': 'extend',
})
print getHostid['result'][0]['hostid']
10105
獲取監控項
getItem = zapi.do_request('item.get',
{
'hostids': 10105,
'search': {'key_':'trap'},
'output': 'extend',
})
getItem['result'] == []
[{u'itemid': u'41054', u'username': u'', u'snmpv3_contextname': u'', u'inventory_link': u'0', u'multiplier': u'0', u'authtype': u'0', u'trends': u'365', u'snmpv3_authpassphrase': u'', u'snmp_oid': u'', u'templateid': u'0', u'snmpv3_securitylevel': u'0', u'port': u'', u'lastns': u'306656356', u'password': u'', u'logtimefmt': u'', u'mtime': u'0', u'delay': u'0', u'publickey': u'', u'state': u'0', u'params': u'', u'snmpv3_securityname': u'', u'formula': u'1', u'type': u'2', u'snmpv3_authprotocol': u'0', u'prevvalue': u'123', u'status': u'0', u'lastlogsize': u'0', u'lastclock': u'1511777835', u'snmp_community': u'', u'description': u'', u'data_type': u'0', u'evaltype': u'0', u'trapper_hosts': u'', u'lastvalue': u'3', u'units': u'', u'value_type': u'3', u'delta': u'0', u'snmpv3_privprotocol': u'0', u'delay_flex': u'', u'interfaceid': u'0', u'snmpv3_privpassphrase': u'', u'hostid': u'10105', u'key_': u'trap', u'name': u'my_sender', u'privatekey': u'', u'lifetime': u'30', u'valuemapid': u'0', u'flags': u'0', u'error': u'', u'ipmi_sensor': u'', u'history': u'90'}]
創建監控項
createItem = zapi.do_request('item.create',
{
'hostid': 10105,
'name': 'bm_sql_check $1',
'key_': 'bm.sql.check[sql_name_col1]',
'type': 2,
'value_type': 3,
'applications': ["2932"],
'delay': 10,
})
print createItem
{u'jsonrpc': u'2.0', u'result': {u'itemids': [u'41055']}, u'id': u'1'}
獲取應用集
getApplication = zapi.do_request('application.get',
{
'hostid': 10105,
'search': {'name': 'business_monitor'},
})
print getApplication
{u'jsonrpc': u'2.0', u'result': [{u'flags': u'0', u'hostid': u'10105', u'applicationid': u'2932', u'name': u'business_monitor', u'templateids': []}], u'id': u'1'}
創建應用集
createApplication = zapi.do_request('application.create',
{
'hostid': 10105,
'name': 'business_monitor',
})
print createApplication
{u'jsonrpc': u'2.0', u'result': {u'applicationids': [u'2932']}, u'id': u'1'}
獲取觸發器
getTriger = zapi.do_request('trigger.get',
{
'filter': {'hostid':10105,'description':'bm_sql_trigger'},
})
print getTriger
{u'jsonrpc': u'2.0', u'result': [{u'status': u'0', u'recovery_mode': u'0', u'description': u'bm_sql_trigger', u'state': u'0', u'url': u'', u'type': u'0', u'templateid': u'0', u'correlation_tag': u'', u'lastchange': u'1511794116', u'value': u'1', u'priority': u'5', u'triggerid': u'14021', u'flags': u'0', u'comments': u'', u'error': u'', u'correlation_mode': u'0', u'expression': u'{13688}=0\r\nor\r\n{13689}>10', u'recovery_expression': u'', u'manual_close': u'0'}], u'id': u'1'}
創建觸發器
通過zabbix_sender上傳數據
使用場景:通過客戶端主動推送數據到服務器(10051端口),數據類型可以是數字也可以是字符串等,避免一些超時的查詢。但是需要注意在觸發器中添加:
{yumOS:bm.sql.check[sql_name_col1].nodata(3)}=0 # 最后3次未獲取到數據(3.5.2有bug,不能觸發)
or
{yumOS:bm.sql.check[sql_name_col1].last(3)}>10 # 設置閾值
方法1:zabbix-api協議
通過zabbix_sender命令來測試上傳監控項和監控值
/usr/local/zabbix-agent/bin/zabbix_sender -c /usr/local/zabbix-agent/etc/zabbix_agentd.conf -k zookeeper.status[zk_followers] -o 1 info from server: "processed: 1; failed: 0; total: 1; seconds spent: 0.000114" sent: 1; skipped: 0; total: 1
遇到一個怪問題,雖然sconds spent很快,但是整個zabbix_sender過程非常慢。后來發現是其中一個dns(nameserver)不通導致的,但是用host或者dig及nslookup命令貌似沒問題,暫時沒找到原因。
方法2:pyzabbix封裝后
from pyzabbix import ZabbixMetric, ZabbixSender
packet = [
ZabbixMetric('test-1', 'trap[use]',2),
ZabbixMetric('test-1', 'trap[use2]',6)
]
result = ZabbixSender(zabbix_server='10.0.0.28',zabbix_port=10051).send(packet)
print result
使用pyzabbix模塊
安裝
pip install pyzabbix
使用方法基本和py-zabbix是一樣的,但是方法更優美
創建監控項
r = zapi.item.create(
name='THREAD_POOL_STATUS.%s' %(i+"."+res), # item name
key_='app_Check[THREAD_POOL_STATUS,%s]'% (i+"."+res), # key name
hostid='10432', # 這邊可以主機也可以模塊的 id
type='0',
value_type='3',
delay='30s')
總結
py-zabbix 和pyzabbix都是需要使用的包,我的做法是把他們集中到一個文件里面,然后自己導入
sys.path.insert(0,'./zabbix_package') from pyzabbix import ZabbixAPI,ZabbixAPIException from pzabbix import ZabbixMetric,ZabbixSender,ZabbixResponse
自定義監控項目
zabbix自動發現
可以在模板也可以在主機添加自動發現規則,這個自動發現和主機的自動發現不是一樣的!
采用客戶端方式

創建一個bmt.discovery的鍵值,加在客戶端UserParameter自定義監控項中執行腳本
UserParameter=bmt.discovery,source /etc/profile && python /usr/local/zabbix-agent/shell/business_monitor.py
#!/usr/bin/env python
# set coding: utf-8
# :output:
# {
# "data":[
# {
# "{#SQL_NAME_COL}":"TEST1_T1",
# },
# {
# "{#SQL_NAME_COL}":"TEST1_T2",
# },
# {
# "{#SQL_NAME_COL}":"TEST2_T1"
# }
# ]
# }
# TempFile:
#SQL_NAME_COL VALUE
# TEST1_T1 1
# TEST1_T2 0
# TEST2_T1 10
__author__ = "richardzgt"
import cx_Oracle as cx
import ConfigParser
import os,sys
import re
import datetime,time
import logging
import logging.handlers
import multiprocessing
import json
"""logging info"""
LOG_FILE = '/tmp/bm.log'
logger = logging.getLogger('bm') # 獲取名為Ldap的logger
logger.setLevel(logging.INFO)
handler = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes = 1024*1024) # 實例化handler
formatter = logging.Formatter('%(asctime)s- %(levelname)s - %(message)s') # 實例化formatter
handler.setFormatter(formatter) # 為handler添加formatter
logger.addHandler(handler)
""" :parameter """
# 建立數據庫連接
ip = ''
port = 1521
db = ''
username = ''
pwd = ''
runtime = 1
tempfile = "/tmp/tmp.zabbix.bm"
class datetimeToJson(json.JSONEncoder):
"""docstring for datatimeToJson"""
def default(self, obj):
if isinstance(obj, (datetime.datetime,)):
return obj.isoformat()
else:
return super().default(obj)
def intToString(vdata):
if isinstance(vdata,(int,float)):
return str(vdata)
return vdata
def checksql(sqlstate):
newsqlstate = sqlstate.rstrip(';')
return newsqlstate
def do_db_query(item,q):
# output: {'DATA': {'COL1': 345}, {'COL1': 52}, 'SQL_NAME': 'test'}
conn = cx.connect(item['USERNAME'],
item['PASSWORD'],
item['DB_URL'],
)
cursor = conn.cursor()
try:
c = cursor.execute(checksql(item['SQL_TEXT']))
rows = rows_to_dict_list(c)
except Exception,e:
rows = [{'error':0}]
logger.warning( "[%s]%s" % (item['SQL_NAME'],str(e)))
SQL_NAME = item['NAME'] if item['NAME'] else item['ID']
sql_name_data = {"DATA":rows[0],"SQL_NAME":SQL_NAME}
q.put(sql_name_data)
def rows_to_dict_list(cursor):
columns = [i[0] for i in cursor.description]
dict_with_columns = [dict(zip(columns, row)) for row in cursor]
return dict_with_columns
def get_bm_config():
sql = 'select * from bm_config where status=1;'
dsn = cx.makedsn(ip, port, db)
conn = cx.connect(username, pwd, dsn)
cursor = conn.cursor()
c = cursor.execute(checksql(sql))
rows = rows_to_dict_list(c)
return rows
def updateTempFile(for_data):
# param: [('test_COL2', '66'), ('test_COL1', 345), ('test2_K1', 3742)]
_list = []
with open(tempfile,'w') as fobj:
_list = [" ".join(i) for i in for_data ]
content = "\n".join(_list)
fobj.write(content+"\n")
fobj.close()
def strft_json(for_data):
# param: [{'DATA': {'COL2': '66', 'COL1': 345}, 'SQL_NAME': 'test'}, {'DATA': {'K1': 3742}, 'SQL_NAME': 'test2'}]
# output :
#格式化成適合zabbix lld的json數據
format_data = []
file_data = []
for one_result in for_data:
for k in one_result['DATA']:
SQL_NAME_COL = "%s_%s" % (one_result['SQL_NAME'],k)
file_data += [(SQL_NAME_COL,intToString(one_result['DATA'][k]))]
format_data += [{'{#SQL_NAME_COL}':SQL_NAME_COL}]
updateTempFile(file_data)
return json.dumps({'data':format_data},sort_keys=True,indent=4,separators=(',',':'))
def main():
logger.info("buisness check starting")
db_query_result = []
bm_items = get_bm_config()
p = multiprocessing.Pool(50)
q = multiprocessing.Queue()
for bm_item in bm_items:
mp = multiprocessing.Process(target=do_db_query,args=(bm_item,q))
mp.start()
time.sleep(0.5)
if mp.is_alive():
# 等待查詢時間
time.sleep(runtime)
if mp.is_alive():
mp.terminate()
logger.error("%s: sql[%s],runtime[%s] exceed!" % (bm_item['NAME'],bm_item['SQL_TEXT'],runtime))
return ""
else:
logger.warning("run_sql name: <"+bm_item['NAME']+"> is ok,but test query db is too slow!") ##alarm
db_query_result.append(q.get())
# print q.get()
print strft_json(db_query_result)
if __name__ == '__main__':
main()
這個腳本的作用是提取數據庫sql並在目標數據庫執行,將執行結果打到/tmp/tmp.zabbix.bm。
格式如下:
SQL_NAME_COL VALUE xxx_xxxx_message_RECVS 0
將key是唯一性的,由sql_name+column拼接成的為了查詢出來的鍵值不重復
然后再通過監控項原型根據key提取此文件value值,而監控項原型的意義就在於利用自動發現的key去查詢value,所以必須在原型項中定義后續的查詢條件
{#SQL_NAME_COL}是根據bm.discovery上傳的json中的key定義的

在客戶端userParameter配置bmt原型項:
UserParameter=bmt.[*],python /usr/local/zabbix-agent/shell/bm_get_values.py $1
注意 commandType[MEMORY_STATUS, heap.max] zabbix參數用默認,分割
$1 ==> MEMORY_STATUS
$2 ==> heap.max

采用采集器方式

后面應用監控可以通過監控原型擴展預設值來判斷業務歷史預測值和實際值的偏差
使用采集器可以減輕proxy壓力
# 上傳監控值
def metric_sender(hostname,itemKeysList):
# args[0]
# 組裝成zbx的 item
# sys.exit()
try:
for item in itemKeysList:
packet = [ ZabbixMetric(hostname, "commandType[%s]" % item.keys()[0],item.values()[0]) ]
result = ZabbixSender(zabbix_server=ZABBIX_ENDPOINT,zabbix_port=10051).send(packet)
if result.failed:
logger.error(u"主機上傳[%s]上傳監控值失敗,上傳數據[%s],結果[%s]" % (hostname, packet, result))
except IndexError as e:
logger.error(u"IndexError %s [%s]" % e)
except Exception as e:
logger.error(u"%s [%s]" % (hostname,e))
else:
logger.info(u"主機[%s]上傳采集值成功" % hostname)
自定義觸發器
創建觸發器, 告警時會將$1替換成告警閾值

3分鍾平均
{test-cif.base-98-13:commandType[MEMORY_STATUS,Usage/Max].avg(3m)}>50
Problem: 最近5分鍾剩余磁盤空間小於10GB。(異常)
Recovery: 最近10分鍾磁盤空間大於40GB。(恢復)
簡單說便是一旦剩余空間小於10G就觸發異常,然后接下來剩余空間必須大於40G才能解除這個異常(注意這個表達式,不是>40G哦),就算你剩余空間達到了39G(不在報警條件里)那也是沒用的.
TRIGGER.VALUE的意義就在於連接前后的觸發器,之后的業務告警也可以嘗試用這個方法,可以大大減少告警反復
({TRIGGER.VALUE}=0&{server:vfs.fs.size[/,free].max(5m)}<10G) |
({TRIGGER.VALUE}=1&{server:vfs.fs.size[/,free].min(10m)}<40G)
6次不等於0 的事件,出現4次以上就告警
{88lm-webpd-1-1.server.dt:xmty_balance_acctrans_account.count(#6,0,"ne")}>4
6分鍾內,如果最后一次出現strlen >4 那么就成立
{88lm-webpd-1-1.server.dt:bmt.[xmty_message_greater_1k_ACTIVEMQ_MSGS].strlen(,6m)}>4
3個周期內匹配不到N,就報警
{88lm-webpd-1-1.server.dt:bmt.[xmty_message_greater_1k_ACTIVEMQ_MSGS].str(N,#3)}=0
count計數部分
參數:秒或#num 支持類型:float,int,str,text,log 作用:返回指定時間間隔內數值的統計, 舉例: count(600)最近10分鍾得到值的個數 count(600,12)最近10分鍾得到值的個數等於12 count(600,12,"gt")最近10分鍾得到值大於12的個數 count(#10,12,"gt")最近10個值中,值大於12的個數 count(600,12,"gt",86400)24小時之前的10分鍾內值大於12的個數 count(600,,,86400)24小時之前的10分鍾數據值的個數 第一個參數:指定時間段 第二個參數:樣本數據 第三個參數:操作參數 第四個參數:漂移參數
支持比較符操作
eq: 相等 ne: 不相等 gt: 大於 ge: 大於等於 lt: 小於 le: 小於等於 like: 內容匹配
日常使用
上行流量最近兩次都大於50M告警
{zabbix:net.if.out[em1].count(#2,50M,"gt")}=2
最近30分鍾zabbix這個主機超過5次不可到達。
{zabbix:icmpping.count(30m,0)}>5

