Zabbix监控JVM(微服务进程)

ZabbixServer端配置

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

1
./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/ 选择合适的版本。
安装

1
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端。
首先java版本为jdk1.8

1
2
3
4
# java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 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

监控脚本

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# 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|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'])

脚本使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# ./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端的值,文件的权限为:

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

如果文件权限不是646,最好重新授权。
文件内key值的解释为:

NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:元数据空间大小
MU:元数据空间使用大小
M:元数据空间使用百分比
EC:Eden区大小
EU:Eden区使用大小
E:Eden区使用百分比
S0C:第一个幸存区大小
S0U:第一个幸存区使用大小
S0:第一个幸存区使用百分比
S1C:第二个幸存区大小
S1U:第二个幸存区使用大小
S1:第二个幸存区使用百分比
OC:老年代大小
OU:老年代使用大小
O:老年代使用百分比
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
CCS:压缩类空间使用百分比
YGCT:年轻代GC消耗时间
YGC:年轻代GC次数
FGCT:老年代GC消耗时间
FGC:老年代GC次数
GCT:GC消耗总时间
Thread:线程数

userparameter配置

路径及内容如下

1
2
3
4
5
# 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配置如下

1
2
3
4
5
6
7
8
9
10
11
# 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=3Timeout=10
AllowRoot=1是给Agent服务提升权限,并且会以root用户启动(默认为zabbix用户),这样是不安全的。
在生产环境中不建议配置AllowRoot=1,使用以下方式替代:

1
2
3
4
5
6
7
8
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用户启动。

模板配置

模板地址:github地址Templates目录下 Template Jvm Process Dicovery.xml。
导入模板关联主机即可。
不再截图展示。
最后可以通过命令获取值计算结果与zabbix获取结果进行校准。

本文标题:Zabbix监控JVM(微服务进程)

文章作者:Francis

原始链接:http://www.cnops.com/posts/748ad64f.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。