zabbix 监控Java 微服务进程

ZabbixServer端配置

Zabbix服务器需安装java,编译需添加启动参数–enable-java 本次安装的编译参数为:

./configure --prefix=/data/zabbix/ --enable-server --enable-agent --with-mysql --enable-ipv6 --with-net-snmp --with-libcurl --with-libxml2 --enable-java

ZabbixAgent端配置

ZabbixAgent端不仅需要安装zabbix_agentd,还需要安装zabbix_sender,可以通过地址http://repo.zabbix.com/zabbix/3.0/rhel/7/x86_64/ 选择合适的版本。 安装 rpm -ivh http://repo.zabbix.com/zabbix/3.0/rhel/7/x86_64/zabbix-sender-3.0.9-1.el7.x86_64.rpm

监控原理

微服务的特性:

每个进程是直接以java-jar service.jar的方式启动,并没有依赖于tomcat或者其他web应用。 每台服务器上的微服务并没有固定的数量,可以灵活的增加或者减少。 每个微服务的启动参数已有配置端口很多。 鉴于此种情况,传统的监控方法监控微服务,会造成经常的手动去增加删减web页面配置,服务器内的端口管理也会很混乱。 所以使用discovery自动发现的方式去监控微服务。并将每个微服务的信息通过zabbix_sender发送到ZabbixServer端。

关于微服务的信息主要通过jstat获取,如下

# ps -ef|grep java
root      1891     1  0 Apr02 ?        00:09:38 java -Xms500M -Xmx500M -Xmn400M -XX:+PrintGCDetails -jar /data/work/service_jar/orderService.jar --server.port=14000 --management.port=14001 --config.profile=test
root     17860     1  0 Mar26 ?        00:27:17 java -Xms500M -Xmx500M -Xmn400M -XX:+PrintGCDetails -jar /data/work/service_jar/systemService.jar --server.port=21000 --management.port=21001 --config.profile=test
root     18444     1  0 Mar26 ?        00:39:28 java -Xms500M -Xmx500M -Xmn400M -XX:+PrintGCDetails -jar /data/work/service_jar/resourceService.jar --server.port=18000 --management.port=18001 --config.profile=test
root     18619     1  0 Mar26 ?        00:27:06 java -Xms500M -Xmx500M -Xmn400M -XX:+PrintGCDetails -jar /data/work/service_jar/userService.jar --server.port=13000 --management.port=13001 --config.profile=test
root     19601     1  0 Mar26 ?        00:22:37 java -Xms1000M -Xmx1000M -Xmn800M -jar /data/work/service_jar/manageMiddle.jar --server.port=20000 --management.port=20001 --config.profile=test
root     31282 17046  0 17:00 pts/3    00:00:00 grep --color=auto java
# jstat -gc 1891
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
9216.0 8704.0 4320.0  0.0   391680.0 246746.7  102400.0   77473.4   83112.0 80407.4 9896.0 9400.6     94    1.742   3      0.762    2.504
# jstat -gcutil 1891
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
 46.88   0.00  63.00  75.66  96.75  94.99     94    1.742     3    0.762    2.504
# jstat -gccapacity 1891
 NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC 
409600.0 409600.0 409600.0 9216.0 8704.0 391680.0   102400.0   102400.0   102400.0   102400.0      0.0 1122304.0  83112.0      0.0 1048576.0   9896.0     94     3

Jdk1.8中取消了永久区Perm

监控脚本

脚本很多调试内容已注释,请忽略,脚本路径及内容:

# pwd
/etc/zabbix/scripts/java
[root@gbw_test_app03 java]# cat jstat.py 
#!/usr/bin/env python
#coding=utf-8
'''
功能: 调用jstat获取JVM的各项指标
说明: 用于zabbix自动发现告警
版本: V1.0 2019-04-01
特性: 1. 线程功能,提高脚本执行速度
'''
import datetime
import time
import sys
import os
import commands
import subprocess
import json
import argparse
import socket
import threading

jstat_cmd = commands.getoutput("which jstat")
jstack_cmd = commands.getoutput("which jstack")
jvmname_cmd = "jps -l |grep -Ev 'Jps|JStack|Jstat'|awk '{print $2,$1}'"
jvmport_cmd = "netstat -tpnl|grep -oP '(?<=:)\d+.*\d+(?=/java)'|awk '{print $1,$NF}'"

hostname = socket.gethostname()
zbx_sender='/usr/bin/zabbix_sender'
zbx_cfg='/etc/zabbix/zabbix_agentd.conf'
zbx_tmp_file='/etc/zabbix/scripts/java/.zabbix_jvm_status'

'''
output=sys.stdout
outputfile=open("/etc/zabbix/scripts/java/log.txt","a")
sys.stdout=outputfile

now = time.time()
t = time.localtime(int(now))
dt = time.strftime("%Y%m%d%H%M%S", t)
print dt
'''

jvm_threads = []

def get_status(cmd,opts,pid):
    value = commands.getoutput('%s -%s %s' % (cmd,opts,pid)).strip().split('\n')

    #print value[0].split(' ')
    #print value[1].split(' ')
    #print filter(None, value[0].split(' '))
    #print filter(None, value[1].split(' '))   

    if filter(None, value[0].split(' ')):
        if filter(None, value[1].split(' ')):
            kv = []
            for i in filter(None, value[0].split(' ')):
                if i != '':
                    kv.append(i)
            vv = []
            for i in filter(None, value[1].split(' ')):
                if i != '':
                    vv.append(i)
            data = dict(zip(kv,vv))
            return data    
         else:
             pass
      else:
          pass

'''     
    kv = []
    for i in filter(None, value[0].split(' ')):
        if i != '':
            kv.append(i)

    vv = []
    for i in filter(None, value[1].split(' ')):
        if i != '':
            vv.append(i)

    data = dict(zip(kv,vv))
    return data
'''

def get_thread(cmd,pid):
    value = commands.getoutput('sudo %s %s|grep http|wc -l' % (cmd,pid))
    data = {"Thread":value}
    return data

def get_jvm(jport,jprocess):
    '''
      使用jstat获取Java的性能指标
    '''

    file_truncate()    # 清空zabbix_data_tmp

    gcutil_data = get_status(jstat_cmd,"gcutil",jprocess)
    gccapacity_data = get_status(jstat_cmd,"gccapacity",jprocess)
    gc_data = get_status(jstat_cmd,"gc",jprocess)
    thread_data = get_thread(jstack_cmd,jprocess)
    data_dict = dict(gcutil_data.items()+gccapacity_data.items()+gc_data.items()+thread_data.items())

    for jvmkey in data_dict.keys():
        zbx_data = "%s jvm[%s,%s] %s" %(hostname,jport,jvmkey,data_dict[jvmkey])
        with open(zbx_tmp_file,'a') as file_obj: file_obj.write(zbx_data + '\n')

def jvm_name_discovery():
    output = subprocess.Popen(jvmname_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    jvm_name_lists = output.stdout.readlines()
    jvm_name_proce = []
    for jvm_name_tmp in jvm_name_lists:
         jvm_name_proce.append(jvm_name_tmp.split())
    return jvm_name_proce

def jvm_port_discovery():
    output = subprocess.Popen(jvmport_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    jvm_port_lists = output.stdout.readlines()
    jvm_port_proce = []
    for jvm_port_tmp in jvm_port_lists:
         jvm_port_proce.append(jvm_port_tmp.split())
    return jvm_port_proce


def file_truncate():
    '''
      用于清空zabbix_sender使用的临时文件
    '''
    with open(zbx_tmp_file,'w') as fn: fn.truncate()

def zbx_tmp_file_create():
    '''
      创建zabbix_sender发送的文件内容
    '''
    jvmname_list = jvm_name_discovery()
    for jvm_name_tmp in jvmname_list:
        jvmname = jvm_name_tmp[0]
        jvmprocess = jvm_name_tmp[1]
        th = threading.Thread(target=get_jvm,args=(jvmname,jvmprocess))
        th.start()
        jvm_threads.append(th)

def send_data_zabbix():
    '''
      调用zabbix_sender命令,将收集的key和value发送至zabbix server
    '''
    zbx_tmp_file_create()
    for get_jvmdata in jvm_threads:
        get_jvmdata.join()
    zbx_sender_cmd = "%s -c %s -i %s" %(zbx_sender,zbx_cfg,zbx_tmp_file)
    print zbx_sender_cmd
    zbx_sender_status,zbx_sender_result = commands.getstatusoutput(zbx_sender_cmd)
    #print zbx_sender_status
    print zbx_sender_result

def zbx_name_discovery():
    '''
      用于zabbix自动发现JVM名称
    '''
    jvm_zabbix = []
    jvmname_list = jvm_name_discovery()
    for jvm_tmp in jvmname_list:
        jvm_zabbix.append({'{#JNAME}' : jvm_tmp[0],
                           '{#JPROCESS}' : jvm_tmp[1],
                         })
    return json.dumps({'data': jvm_zabbix}, sort_keys=True, indent=7,separators=(',', ':'))

def zbx_port_discovery():
    '''
      用于zabbix自动发现JVM端口
    '''
    jvm_zabbix = []
    jvmport_list = jvm_port_discovery()
    for jvm_tmp in jvmport_list:
        jvm_zabbix.append({'{#JPORT}' : jvm_tmp[0],
                           '{#JPROCESS}' : jvm_tmp[1],
                         })
    return json.dumps({'data': jvm_zabbix}, sort_keys=True, indent=7,separators=(',', ':'))

def cmd_line_opts(arg=None):
    class ParseHelpFormat(argparse.HelpFormatter):
        def __init__(self, prog, indent_increment=5, max_help_position=50, width=200):
            super(ParseHelpFormat, self).__init__(prog, indent_increment, max_help_position, width)

    parse = argparse.ArgumentParser(description='JVM监控"',
                                    formatter_class=ParseHelpFormat)
    parse.add_argument('--version', '-v', action='version', version="1.0", help='查看版本')
    parse.add_argument('--jvmname', action='store_true', help='获取JVM名称')
    parse.add_argument('--jvmport', action='store_true', help='获取JVM端口')
    parse.add_argument('--data', action='store_true', help='发送JVM指标数据至zabbix')

    if arg:
        return parse.parse_args(arg)
    if not sys.argv[1:]:
        return parse.parse_args(['-h'])
    else:
        return parse.parse_args()


if __name__ == '__main__':
    opts = cmd_line_opts()
    if opts.jvmname:
  print zbx_name_discovery()
    elif opts.jvmport:
        print zbx_port_discovery()
    elif opts.data:
        send_data_zabbix()
    else:
        cmd_line_opts(arg=['-h'])

脚本使用方法

# ./jstat.py -h
usage: jstat.py [-h] [--version] [--jvmname] [--jvmport] [--data]

JVM监控"

optional arguments:
     -h, --help     show this help message and exit
     --version, -v  查看版本
     --jvmname      获取JVM名称
     --jvmport      获取JVM端口
     --data         发送JVM指标数据至zabbix
# ./jstat.py --version
1.0
# ./jstat.py --jvmname
{
       "data":[
              {
                     "{#JNAME}":"manageMiddle.jar",
                     "{#JPROCESS}":"19601"
              },
              {
                     "{#JNAME}":"orderService.jar",
                     "{#JPROCESS}":"1891"
              },
              {
                     "{#JNAME}":"systemService.jar",
                     "{#JPROCESS}":"17860"
              },
              {
                     "{#JNAME}":"userService.jar",
                     "{#JPROCESS}":"18619"
              },
              {
                     "{#JNAME}":"resourceService.jar",
                     "{#JPROCESS}":"18444"
              }
       ]
}
# ./jstat.py --jvmport
{
       "data":[
              {
                     "{#JPORT}":"14000",
                     "{#JPROCESS}":"1891"
              },
              {
                     "{#JPORT}":"18000",
                     "{#JPROCESS}":"18444"
              },
              {
                     "{#JPORT}":"14001",
                     "{#JPROCESS}":"1891"
              },
              {
                     "{#JPORT}":"18001",
                     "{#JPROCESS}":"18444"
              },
              {
                     "{#JPORT}":"20000",
                     "{#JPROCESS}":"19601"
              },
              {
                     "{#JPORT}":"20001",
                     "{#JPROCESS}":"19601"
              },
              {
                     "{#JPORT}":"13000",
                     "{#JPROCESS}":"18619"
              },
              {
                     "{#JPORT}":"21000",
                     "{#JPROCESS}":"17860"
              },
              {
                     "{#JPORT}":"13001",
                     "{#JPROCESS}":"18619"
              },
              {
                     "{#JPORT}":"21001",
                     "{#JPROCESS}":"17860"
              }
       ]
}
# ./jstat.py --data
/usr/bin/zabbix_sender -c /etc/zabbix/zabbix_agentd.conf -i /etc/zabbix/scripts/java/.zabbix_jvm_status
info from server: "processed: 170; failed: 0; total: 170; seconds spent: 0.001286"
sent: 170; skipped: 0; total: 170

.zabbix_jvm_status文件中存储要发送到server端的值,文件的权限为:

# ll .zabbix_jvm_status 
-rw-r--rw- 1 root root 686 Apr  4 15:15 .zabbix_jvm_status

如果文件权限不是646,最好重新授权。

监控项解释:

  1. NGCMN:新生代最小容量
  2. NGCMX:新生代最大容量
  3. NGC:当前新生代容量
  4. OGCMN:老年代最小容量
  5. OGCMX:老年代最大容量
  6. OGC:当前老年代大小
  7. MCMN:最小元数据容量
  8. MCMX:最大元数据容量
  9. MC:元数据空间大小
  10. MU:元数据空间使用大小
  11. M:元数据空间使用百分比
  12. EC:Eden区大小
  13. EU:Eden区使用大小
  14. E:Eden区使用百分比
  15. S0C:第一个幸存区大小
  16. S0U:第一个幸存区使用大小
  17. S0:第一个幸存区使用百分比
  18. S1C:第二个幸存区大小
  19. S1U:第二个幸存区使用大小
  20. S1:第二个幸存区使用百分比
  21. OC:老年代大小
  22. OU:老年代使用大小
  23. O:老年代使用百分比
  24. CCSMN:最小压缩类空间大小
  25. CCSMX:最大压缩类空间大小
  26. CCSC:压缩类空间大小
  27. CCSU:压缩类空间使用大小
  28. CCS:压缩类空间使用百分比
  29. YGCT:年轻代GC消耗时间
  30. YGC:年轻代GC次数
  31. FGCT:老年代GC消耗时间
  32. FGC:老年代GC次数
  33. GCT:GC消耗总时间
  34. Thread:线程数
  35. userparameter配置

路径及内容如下

# pwd
/etc/zabbix/zabbix_agentd.d
# cat userparameter_java_discovery_status.conf 
UserParameter=jvmname,/usr/bin/python /etc/zabbix/scripts/java/jstat.py --jvmname
UserParameter=jvmdata,/usr/bin/python /etc/zabbix/scripts/java/jstat.py --data
zabbix_agentd.conf配置如下

# cat zabbix_agentd.conf |grep -Ev '^$|^#'
PidFile=/var/run/zabbix/zabbix_agentd.pid
LogFile=/var/log/zabbix/zabbix_agentd.log
LogFileSize=0
Server=172.19.138.53
ListenPort=10050
ServerActive=172.19.138.53
Hostname=gbw_test_app03
Timeout=10
AllowRoot=1
Include=/etc/zabbix/zabbix_agentd.d/

为了防止脚本执行超时,出现报错ZBX_NOTSUPPORTED,修改默认Timeout=3为Timeout=10。 AllowRoot=1是给Agent服务提升权限,并且会以root用户启动(默认为zabbix用户),这样是不安全的。 在生产环境中不建议配置AllowRoot=1,使用以下方式替代:

ll /etc/sudoers
-r--r----- 1 root root 3938 Sep  6  2017 /etc/sudoers
# chmod 640 /etc/sudoers    #修改可写权限
# vim /etc/sudoers    #添加下面这一句
zabbix ALL=(ALL) NOPASSWD: ALL
# chmod 440 /etc/sudoers    #恢复权限
# ll /etc/sudoers
-r--r----- 1 root root 3937 Apr  3 17:20 /etc/sudoers

配置完成后重启agent客户端,确保是以zabbix用户启动。

模板配置

模板地址:https://download.csdn.net/download/u010571301/12610278。 导入模板关联主机即可。

转自 https://www.cnops.top/posts/748ad64f.html


发表评论

评论列表,共 0 条评论

    暂无评论