Zabbix本身就能监控系统的内存利用率和CPU利用率,但是系统内存并不能反应JVM内存情况,我经常碰到JVM内存满掉而系统内存大量空余的情况,因为我们在启动Tomcat这样的中间件时,一般会使用-Xmx 4096m这样命令来指定JVM可用内存的大小,默认才512M大小实在是很小,但最大也不应该超过5-6G,因为太大的内存会导致GC过程非常缓慢。所以即使你插了64GB的内存条,但JVM一旦到达4G你的应用就会变慢变卡,而zabbix不会发现也不会报警的情况。
我们平时监控JVM内存是否有溢出或者泄露问题一般都采用jstat的命令,同理使用Zabbix也可以使用这个方法,其实现方式类似与ss监控TCP连接数。但是由于权限问题会比监控TCP连接数要复杂一些。
有几个点需要注意一下
1、JDK 1.8和之前版本的区别:JDK 1.8新增了Metiaspace区,替换了原来的Perm区,所以jstat出来的结果也会不一样。同样在使用jstat命令的时候,如果应用是JDK 1.8的那么jstat也要是JDK1.8里面的jstat。而对于1.7和1.6版本,则都可以使用。甚至可以使用OpenJDK。
2、不同用户的权限问题:如果你的应用是使用alex账号启动的,那么只能在alex用户或者root用户下执行jstat才能获得JVM的内存信息,所以zabbix用户一般是看不到的。所以我们在执行jstat命令的时候需要先将zabbix用户提升到root权限,就可以看到所有用户的JVM状态了。
3、selinux的问题:对于CentOS和红帽系统,系统默认开启了selinux,它会阻止zabbix通过shell脚本也就是jstat命令获取电脑的信息,显示出来的效果就是会提示permission denied。所以我们需要通过setenforce 0来暂停selinux才能正常执行jstat命令。其原理同nc探测TCP连接的问题类似。
首先我们要使用root用户关闭selinux,这一步很多使用CentOS和红帽的运维人员都会装机完就关闭selinux,并不是多新鲜的事情了
setenforce 0
然后我们需要赋予zabbix用户sudo到root的权限,这波操作非常神奇,下面是在RHEL 7下面的操作,RHEL6也类似。
在/etc/sudoers.d下面新建一个名为zabbix的文件,里面填写在何种情况下允许zabbix提升到root权限,内容如下
zabbix ALL=(ALL) NOPASSWD: /usr/local/bin/jstat.sh *
上面那句话就是zabbix可以在执行/usr/local/bin/jstat.sh这个脚本的时候sudo到root权限,而且不需要输入root的密码,这样一来其实是非常危险的,其他人只要修改上面脚本的内容,就可以轻松的通过黑进zabbix获取root权限。所以这个脚本要配置成只读的,起码对于root以外的用户要无法修改才行。要是你连这个脚本都不限定,直接允许zabbix直接获取root权限,那就更危险了。
由于zabbix用户是没有终端权限的,这一点可以在/etc/passwd中看到/sbin/nologin 的配置,所以我们还需要取消sudo必须有tty权限的系统默认设定,方法就是修改/etc/sudoers这个文件,但首先需要将这个文件添加可读写权限,否则即使是root也无法修改这个文件
chmod +w /etc/sudoers
然后vi编辑这个文件,找到 Defaults requiretty 这一行,改成,加一个叹号
Defaults !requiretty
然后我们的zabbix现在已经拥有了执行jstat的权限,可以开始编写相关脚本了。这个脚本接收两个参数
第一个参数是你要获取哪个内存区域的名称,比如Eden,Old,Perm等等
第二个参数是进程的名称的关键字,方便根据这个名称去获取这个进程的Pid,这个名称要有唯一性,可以通过ps -ef然后仔细观察Tomcat或者其他中间件建立的进程来获得。比如tomcat在启动的时候一般会把配置文件写在进程信息中,于是我就把这个配置文件的路径作为关键字
然后脚本就会自动获取这个进程所对应的Pid,然后分析是JDK 1.8还是1.7 1.6,执行jstat命令,然后通过awk获取相关内存区域的百分比,提供给zabbix server。脚本如下,脚本路径为/usr/local/bin/jstat.sh,注意下面的JAVA_HOME等参数要根据你服务器的实际路径去修改。
#!/bin/bash
JAVA8_HOME=/usr/jdk1.8.0_91/
JAVA7_HOME=/usr/jdk1.7.0_79/
JAVA6_HOME=/usr/jdk1.6.0_27/
JHOME=/usr/
function Survivor0 {
$JHOME/bin/jstat -gcutil $pid | awk 'NR==2 {print $1}'
}
function Survivor1 {
$JHOME/bin/jstat -gcutil $pid | awk 'NR==2 {print $2}'
}
function Eden {
$JHOME/bin/jstat -gcutil $pid | awk 'NR==2 {print $3}'
}
function Old {
$JHOME/bin/jstat -gcutil $pid | awk 'NR==2 {print $4}'
}
function Perm {
$JHOME/bin/jstat -gcutil $pid | awk 'NR==2 {print $5}'
}
function Metaspace {
$JHOME/bin/jstat -gcutil $pid | awk 'NR==2 {print $5}'
}
process=$(ps -ef | grep "$2" | grep -v 'jstat' | grep -v 'grep' | awk 'NR==1 {print $0}')
pid=$(echo $process | awk '{print $2}')
java8=$(echo $process | grep jdk1.8)
java7=$(echo $process | grep jdk1.7)
java6=$(echo $process | grep jdk1.6)
if [ ! -z "$java8" ];then
JHOME=$JAVA8_HOME
elif [ ! -z "$java7" ];then
JHOME=$JAVA7_HOME
elif [ ! -z "$java6" ];then
JHOME=$JAVA6_HOME
fi
$1 "$2"
注意,使用ps -ef | grep“进程名”的时候,由于zabbix脚本本身会带有一个进程名参数,grep命令也会带有一个进程名参数,所以这条命令执行出来至少有三个进程符合条件,所以上面使用了两遍 grep -v命令来去除干扰项,然后如果还有多个进程满足该关键字,只会去结果里的第一行。
然后我们就把我们的这个自定义脚本增加到被监控服务器的zabbix客户端配置文件里,方法就是新建一个/etc/zabbix/zabbix_agentd.d/userparameter_jstat.conf文件,内容如下
UserParameter=custom.jstat[*],sudo /usr/local/bin/jstat.sh $1 $2
注意上面是通过sudo提升了权限
然后使我们刚加的自定义key在zabbix客户机上生效
service zabbix-agent restart
然后我们就可以到zabbix server上来探测脚本是否可以正常运行
[alex@localhost ~]$ zabbix_get -s 192.168.1.2 -k custom.jstat[Old,tomcat]
35.86
如果能正常出现一个百分数,那么就说明成功了,下面我们可以到zabbix网页端添加相关的监控项和图像。其中主要就是把key填写正确就好
然后重复上面的步骤,将你需要观察的项目都添加相应的监控项,注意JDK 1.8之后Perm区变成了MetaSpace区
然后再建立图形,上面建立的五个监控项都添加到图形中去
然后等待一会就可以看见效果啦
JDK 1.7效果
JDK 1.8效果