目录

监控方案概述

我们使用 zabbix-agent 的方式来监控 多个 tomcat 8.5.51 ,由于我们需要监控的是 Docker 容器里的 Tomcat ,而 zabbix 官方模板并不支持,而且官方提供的第三方方案也不支持多实例监控,所以只能参考很多互联网上的一些解决方案,最终形成了适合我们自己的应用场景的解决方案(监控虚拟机里的多个 tomcat 实例方案见文末的参考资料)。

本文详细描述了整个方案的详细过程和原理,如果只是想监控 tomcat 多个 Docker 实例,请参考:tomcat 监控实际操作

准备工作

配置 tomcat 容器的 LABEL 标签:JMX_PORT 和 JMX_MONITOR_UUID 用于定义使用的 jmx 端口和 tomcat 的 UUID 标记。

tomcat 配置 jmx,实际上 java 应用的 jmx 监控中原理都相同,开启并配置 jmx 的远程监控配置即可,这个是监控的前提条件。

tomcat 自动发现脚本

tomcat_jmx 监控数据源脚本

cmdline-jmxclient-0.10.3.jar 来监控数据

处理 zabbix-server 获取不到 zabbix-agent 收集的数据问题

处理 zabbix-agent 镜像挂载容器外的 docker 进程时候报错:permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock

tomcat 容器镜像启动

拉取 tomcat 镜像

docker pull develop-harbor.geostar.com.cn/base/apache-tomcat:8.5.51-8u231

启动 tomcat 多实例

docker run -p 8088:8080 -p 10057:10057 --name tomcat-test1 -l JMX_MONITOR_UUID=bc47dcd484724fb48fe81bc9f0e3d802 -l JMX_PORT=10057 -d develop-harbor.geostar.com.cn/base/apache-tomcat:8.5.51-8u231

docker run -p 8089:8080 -p 10058:10058 --name tomcat-test2 -l JMX_MONITOR_UUID=bc47dcd484724fb48fe81bc9f0e3d803 -l JMX_PORT=10058 -d develop-harbor.geostar.com.cn/base/apache-tomcat:8.5.51-8u231

注意:这里启动了 2 个 tomcat 容器实例,第一个容器内部 8080 映射到外部为 8088,jmx使用 10057 端口,第一个容器内部 8080 映射到外部为8089,jmx 使用 10058 端口。这里定义的我们约定的标签有 JMX_PORT 和 JMX_MONITOR_UUID ,JMX_PORT 用于定义使用的 jmx 端口,JMX_MONITOR_UUID 用于定义 tomcat 实例的 UUID 标记,区分各个 tomcat实例。

定义这两个标签是为了后续使用 Docker 的 api 来获取 tomcat 的监控信息(jmx 的端口、容器内部 ip、)

进入每个tomcat容器实例内部(以tomcat-test1为例子)

docker exec -it tomcat-test1 bash

配置 jmx 监听

vi /srv/tomcat8/bin/catalina.sh

加入以下配置

CATALINA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=10057 -Dcom.sun.management.jmxremote.rmi.port=10057 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=172.16.101.245"

其中-Djava.rmi.server.hostname=配置为当前服务器 ip,请自行修改,

-Dcom.sun.management.jmxremote.port=10057

-Dcom.sun.management.jmxremote.rmi.port=10057

这两个 jmx 的端口需要与容器启动时候用的 jmx 端口保持一致。

退出 tomcat 容器,重启 tomcat 容器

exit

docker restart tomcat-test1

测试 jmx 能否获取到数据,打开 jdk 目录下面的 jconsole 工具,输入 jmx 远程连接 ip 和 jmx 连接端口

点连接后正常连接就证明 jmx 已正常启用

在这个过程中我们发现一些主要的注意事项,请您在结合自己的监控场景的时候也一定注意:

带密码的 jmx 配置

我们在监控的时候因为会有很多个 tomcat 的 docker 实例,为了简化监控,所以使用 jmx 连接并没有设置密码,如果需要密码访问 jmx ,那么需要设置如下的:

CATALINA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=10057 -Dcom.sun.management.jmxremote.rmi.port=10057 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=172.16.101.245

-Dcom.sun.management.jmxremote.access.file=/srv/tomcat8/conf/jmx.access

-Dcom.sun.management.jmxremote.password.file=/srv/tomcat8/conf/jmx.password"

authenticate:改为 true

access.file:设置访问权限,readonly:只能读取 MBean 的属性和接受通知。

readonly:只能读取 MBean 的属性和接受通知。

readwrite:还允许设置属性,调用方法,创建和删除 MBean。

# cat /srv/tomcat8/conf/jmx.access 如果不存在这个文件请自行创建

admin readwrite

password.file:访问密码

# cat /srv/tomcat8/conf/jmx.password 如果不存在这个文件请自行创建

admin tomcat

创建完成上述文件后,修改文件权限(这一步很关键,因为这些属于敏感信息,所以需要限制访问权限,不修改的话会在监控的时候报错)

# chmod 400 jmx.*

注意:这个时候需要重启 tomcat 容器让配置生效

端口的映射问题

采用 tomcat 的 jmx 监控有一个限制,容器对外映射 jmx 端口,容器内部 jmx 端口,配置文件里的 jmx 端口,三者必须严格保持一致,不然无法正常获取到监控数据,所以,必须是 -p 10057:10057 这种形式来启动 tomcat 容器,如果是 10058:10057这种形式会失败,或者在 dockerfile 里用 expose 10057,外部映射随机端口,同样会失败,这个坑一定要注意。虽然 oracle 的工程师通过别的解决方案解决了这个问题,但是会引入 oracle 的一些商业软件进来,规避了这个问题本身,所以不采取他们的方案:http://thegridman.com/coherence/oracle-coherence-on-docker/#jmx

ps ef | grep tomcat 过滤 tomcat jmx 端口方案

相对于用 docker api 的方式获取 tomcat 容器 jmx 端口的方案,还有另外一种方案,仅供参考。

jmx_tomcat_discovery.sh,执行下面这个脚本就可以获取 tomcat jmx 监控的端口号,如果是在 docker 版的 zabbix-agent 容器内执行,还需要在启动这个 agent 容器的时候加上 --pid=host 这个启动参数。

#!/bin/bash

# this is the server ip

serverip=127.0.0.1

# serverObj example:"{#SERVER_IP}": "172.16.101.181", "{#TOMCAT_NAME}": "tomcat_10053"

serverObj=""

# this cmd returns the jmx port the tomcat instances using

tomcat_jxm_ports_res=`ps -aux | grep "tomcat" | awk '{for(i=1;i<=NF;i++){print $i;}}' | grep jmxremote.port | cut -d'=' -f 2`

for tomcat_jmx_port in $tomcat_jxm_ports_res

do

tmp=\{\"{#SERVER_IP}\":\"$serverip\",\"{#TOMCAT_NAME}\":\"tomcat_$tomcat_jmx_port\",\"{#TOMCAT_PORT}\":\"$tomcat_jmx_port\"\},

serverObj="$serverObj$tmp"

done

# subString the last comma of the serverObj string

if [ $serverObj ] ; then

serverObj=${serverObj:0:-1}

fi

# the jsonResult is like {"data": [{"{#SERVER_IP}": "172.16.101.181", "{#TOMCAT_NAME}": "tomcat_10053", "{#TOMCAT_PORT}": "10053"}, {"{#SERVER_IP}": "172.16.101.181", "{#TOMCAT_NAME}": "tomcat_10053", "{#TOMCAT_PORT}": "10054"}]}

if [ $serverObj ] ; then

jsonResult=\{\"data\":[$serverObj]\}

echo $jsonResult

else

echo ""

fi

验证是否能获取 jmx 监控数据

除了用 jconsole 的本地方式连接测试外,更推荐一种在 zabbix-server上通过 cmdline-jmxclient 验证的方式,因为这样能确保服务端可以连接上客户端。

服务端下载 cmdline-jmxclient

wget http://crawler.archive.org/cmdline-jmxclient/cmdline-jmxclient-0.10.3.jar

测试

[root@host-172-16-102-253 ~]# java -jar cmdline-jmxclient-0.10.3.jar - 172.16.101.245:10057 java.lang:type=Memory NonHeapMemoryUsage

05/04/2020 14:53:19 +0800 org.archive.jmx.Client NonHeapMemoryUsage:

committed: 36372480

init: 2555904

max: -1

used: 33912184

注意:如果是有用户名/密码的 jmx 监控,那么需要把 ip 前面的 - 替换为我们设置的 admin:tomcat

tomcat自动发现脚本

获取 tomcat 多个容器实例的 容器 ip、tomcat JMX 端口,uuid,输出为 json 格式提供给 zabbix-server 获取使用(没办法,只能自己写脚本咯,:)。

jmx_tomcat_discovery.py

#!/usr/bin/python

# -*- encoding: utf-8 -*-

import urllib

import xml.etree.ElementTree as ET

import json

import os

import commands

import subprocess

def main():

data = []

(status, docker_ps_output) = commands.getstatusoutput('docker ps -q')

docker_ps_output_text = docker_ps_output.decode('utf-8')

if docker_ps_output_text:

container_id_list=docker_ps_output_text.split('\n')

for container_id in container_id_list:

out_bytes = subprocess.check_output(['docker','inspect',container_id])

out_text = out_bytes.decode('utf-8')

result=json.loads(out_text)

jmx=result[0]['Config']['Labels']

if('JMX_PORT' in jmx):

jmx_port=result[0]['Config']['Labels']['JMX_PORT']

jmx_monitor_uuid=result[0]['Config']['Labels']['JMX_MONITOR_UUID']

ip=result[0]['NetworkSettings']['Networks']['bridge']['IPAddress']

tomcat_instance={"{#CONTAINER_IP}":ip,"{#JMX_PORT}":jmx_port,"{#JMX_MONITOR_UUID}":jmx_monitor_uuid}

data.append(tomcat_instance)

print json.dumps({"data": data})

else:

print "empty result of docker ps -q"

if __name__ == "__main__":

main()

这个脚本我们直接放到了自制的 zabbix-agent 容器镜像内,供我们的自定义 UserParameter 来调用。你也可以根据你的情况放到你认为合适的位置。

tomcat_jmx监控数据源脚本

监控模板参考zabbix监控自动发现监控tomcat(V1)修改而来,定义了我需要的CONTAINER_IP,JMX_PORT,JMX_MONITOR_UUID 三个自定义占位符。

tomcat_monitor.sh,这个脚本跟上面的 python 脚本一样放到 zabbix-agent 的容器镜像里,供我们的自定义 UserParameter 来调用。这里我针对自己的环境和需要的东西做了优化,跟你环境不合适的位置请自行修改。

#!/bin/bash

source /etc/profile

[ $# -ne 3 ] && echo 'The scripts need 3 parameters' && exit 1

CONTAINER_IP=$1

JMX_PORT=$2

ITEM=$3

authenticate="-"

# if no authenticate,use "-",otherwise,add authenticate jmx user and password code here

# jmx_user="akiya"

# jmx_password="akiya_password"

# if [ -n "$jmx_user" ] && [ -n "$jmx_password" ]; then

# authenticate="$jmx_user:$jmx_password"

# fi

# The PORT means the tomcat service default port in the server.xml

PORT=8080

# The cmd means the directory of cmdline-jmxclient jar

cmd=/etc/zabbix/scripts/tomcat/cmdline-jmxclient-0.10.3.jar

#logdir=/tmp/zabbix_tmp

#[ ! -d "$logdir" ] && mkdir -p $logdir && chmod 644 $logdir

#cd $logdir

LOGDIR=/etc/zabbix/scripts/tomcat/logs

function HeapMemoryUsage() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=Memory HeapMemoryUsage 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function EdenSpaceUsage() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=MemoryPool,name=PS\ Eden\ Space Usage 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function SurvivorSpaceUsage() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=MemoryPool,name=PS\ Survivor\ Space Usage 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function TenuredGenUsage() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=MemoryPool,name=PS\ Old\ Gen Usage 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function NonHeapMemoryUsage() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=Memory NonHeapMemoryUsage 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function MetaspaceUsage() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=MemoryPool,name=Metaspace Usage 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function CodeCacheUsage() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=MemoryPool,name=Code\ Cache Usage 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function CompressedClassSpaceUsage() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=MemoryPool,name=Compressed\ Class\ Space Usage 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function TotalLoadedClassCount() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=ClassLoading TotalLoadedClassCount 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function LoadedClassCount() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=ClassLoading LoadedClassCount 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function UnloadedClassCount() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=ClassLoading UnloadedClassCount 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function TotalStartedThreadCount() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=Threading TotalStartedThreadCount 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function ThreadCount() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=Threading ThreadCount 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function PeakThreadCount() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=Threading PeakThreadCount 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function maxThreads() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT Catalina:name=\"http-nio-$PORT\",type=ThreadPool maxThreads 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function currentThreadCount() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT Catalina:name=\"http-nio-$PORT\",type=ThreadPool currentThreadCount 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function currentThreadsBusy() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT Catalina:name=\"http-nio-$PORT\",type=ThreadPool currentThreadsBusy 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function GlobalRequestProcessor_bytesReceived() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT Catalina:name=\"http-nio-$PORT\",type=GlobalRequestProcessor bytesReceived 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function GlobalRequestProcessor_bytesSent() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT Catalina:name=\"http-nio-$PORT\",type=GlobalRequestProcessor bytesSent 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function requestCount() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT Catalina:name=\"http-nio-$PORT\",type=GlobalRequestProcessor requestCount 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function errorCount() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT Catalina:name=\"http-nio-$PORT\",type=GlobalRequestProcessor errorCount 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

function jvmUptime() {

java -jar $cmd $authenticate $CONTAINER_IP:$JMX_PORT java.lang:type=Runtime Uptime 2>$LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

}

case $ITEM in

HeapMemoryUsage.max)

HeapMemoryUsage

sed -n '4p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

HeapMemoryUsage.used)

HeapMemoryUsage

sed -n '5p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

HeapMemoryUsage.committed)

HeapMemoryUsage

sed -n '2p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

EdenSpaceUsage.max)

EdenSpaceUsage

sed -n '4p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

EdenSpaceUsage.used)

EdenSpaceUsage

sed -n '5p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

EdenSpaceUsage.committed)

EdenSpaceUsage

sed -n '2p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

SurvivorSpaceUsage.max)

SurvivorSpaceUsage

sed -n '4p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

SurvivorSpaceUsage.used)

SurvivorSpaceUsage

sed -n '5p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

SurvivorSpaceUsage.committed)

SurvivorSpaceUsage

sed -n '2p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

TenuredGenUsage.max)

TenuredGenUsage

sed -n '4p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

TenuredGenUsage.used)

TenuredGenUsage

sed -n '5p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

TenuredGenUsage.committed)

TenuredGenUsage

sed -n '2p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

NonHeapMemoryUsage.used)

NonHeapMemoryUsage

sed -n '5p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

NonHeapMemoryUsage.committed)

NonHeapMemoryUsage

sed -n '2p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

MetaspaceUsage.used)

MetaspaceUsage

sed -n '5p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

MetaspaceUsage.committed)

MetaspaceUsage

sed -n '2p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

CodeCacheUsage.max)

CodeCacheUsage

sed -n '4p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

CodeCacheUsage.used)

CodeCacheUsage

sed -n '5p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

CodeCacheUsage.committed)

CodeCacheUsage

sed -n '2p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

CompressedClassSpaceUsage.max)

CompressedClassSpaceUsage

sed -n '4p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

CompressedClassSpaceUsage.used)

CompressedClassSpaceUsage

sed -n '5p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

CompressedClassSpaceUsage.committed)

CompressedClassSpaceUsage

sed -n '2p' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT | awk '{print $2}'

;;

ClassLoading.TotalLoadedClassCount)

TotalLoadedClassCount

awk '{print $6}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

;;

ClassLoading.LoadedClassCount)

LoadedClassCount

awk '{print $6}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

;;

ClassLoading.UnloadedClassCount)

UnloadedClassCount

awk '{print $6}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

;;

Threading.TotalStartedThreadCount)

TotalStartedThreadCount

awk '{print $6}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

;;

ThreadCount)

ThreadCount

awk '{print $6}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

;;

PeakThreadCount)

PeakThreadCount

awk '{print $6}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

;;

maxThreads)

maxThreads

awk '{print $6}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

;;

currentThreadCount)

currentThreadCount

awk '{print $6}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

;;

currentThreadsBusy)

currentThreadsBusy

awk '{print $6}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

;;

bytesReceived)

GlobalRequestProcessor_bytesReceived

awk '{print $6}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

;;

bytesSent)

GlobalRequestProcessor_bytesSent

awk '{print $6}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

;;

requestCount)

requestCount

awk '{print $6}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

;;

errorCount)

errorCount

awk '{print $6}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT

;;

jvmUptime)

jvmUptime

[ $? -eq 0 ] && awk '{print $6/1000}' $LOGDIR/$CONTAINER_IP.$ITEM.$JMX_PORT || echo 0

;;

esac

我们的 userparameter_tomcat.conf 里配置如下:

UserParameter=tomcat.discovery,/usr/bin/python /etc/zabbix/scripts/tomcat/jmx_tomcat_discovery.py

UserParameter=tomcat.status[*],/bin/bash /etc/zabbix/scripts/tomcat/tomcat_monitor.sh $1 $2 $3

处理 zabbix-server 获取不到 zabbix-agent 收集的数据问题

在 zabbix_server 端上使用 zabbix_get 测试的时候,会出现权限错误无数据,这个时候应该修改 zabbix-agent 容器里这些脚本的所属用户。

chown -R zabbix:zabbix /etc/zabbix/scripts/tomcat

chmod 775 /etc/zabbix/scripts/tomcat/cmdline-jmxclient-0.10.3.jar /etc/zabbix/scripts/tomcat/jmx_tomcat_discovery.py /etc/zabbix/scripts/tomcat/tomcat_monitor.sh

zabbix-agent 镜像挂载容器外的 docker 进程时候报错

因为,我们的脚本里用到 docker ps 、 docker inspect 等命令,所以需要在 zabbix-agent 容器里挂载宿主的 docker 进程,即 -v /usr/bin/docker:/usr/bin/docker -v /var/run/docker.sock:/var/run/docker.sock 这个挂载参数,会出现这个错误:

permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock

这个时候需要修改权限解决,在容器启动的时候修改权限(这个已经预制到我的 zabbix-agent 镜像里了)

chmod 777 /var/run/docker.sock

测试

在 zabbix-server 可以用下面的命令来测试 jmx 的所有 Bean(只到 ip:port部分,不包括后面部分) 和 Bean 的值(包括具体的 Bean 名字和属性)

# java -jar cmdline-jmxclient-0.10.3.jar - 172.16.101.245:10057

# java -jar cmdline-jmxclient-0.10.3.jar - 172.16.101.245:10057 java.lang:type=Memory NonHeapMemoryUsage

在 zabbix-server 的容器镜像内,可以使用下面的命令来获取用户自定义参数 UserParameter 对应的 key 的值执行后获取的结果,比如:下面的例子会执行 jmx_tomcat_discovery.py 脚本,获取返回值。

# zabbix_get -s 172.16.102.96 -k tomcat.discovery

UserParameter=tomcat.discovery,/usr/bin/python /etc/zabbix/scripts/tomcat/jmx_tomcat_discovery.py

tomcat 监控

zabbix 主机管理页面关联 Templates App Tomcat 模版即可获得多个 tomcat 的 docker 实例的监控数据

监控到的数据

监控原理

现在,是时候总结下 多个 docker 的 tomcat 实例监控的原理了。

开启 tomcat 容器镜像的 jmx 监控配置并配置对外映射端口

用我们的自定义用户脚本,执行 jmx_tomcat_discovery.py 获取到多个 tomcat 容器的端口号,ip 地址, uuid 等信息,供 zabbix-server 端的自动发现规则使用

我们用发现出来的 ip ,port ,uuid 填充我们监控模板中的键,这样就保证了监控的每一个 key 都不一致(这个限制由 zabbix 本身决定了,也是为什么很多方案做不到监控多实例的原因),通过这个 key 去请求我们的 tomcat_monitor.sh 脚本,获取到监控项的值。

主要参考资料

docker tomcat 多开 实例_Docker zabbix-agent 监控 docker tomcat 多实例相关推荐

  1. ZABBIX Agent2监控docker

    首先我们先来看一下zabbix agent2监控docker插件的实现原理,其实就是通过调用docker的API来获取数据,插件目录位于zabbix-agent2/src/go/plugins/doc ...

  2. zabbix企业应用之监控docker容器资源情况

    关于docker的监控,无论开源的CAdvisor.Data Dog还是我自己写的监控(http://dl528888.blog.51cto.com/2382721/1635951),不是通过dock ...

  3. linux下构建Zabbix网络监控平台

    linux下构建Zabbix网络监控平台 由于图片过多,本人不想一张一张上传,请下载我的详细文章: linux下构建zabbix网络监控平台[技术文档](河南-清小小)-下载地址: http://do ...

  4. zabbix常用监控项解读

    CPU来源模板:Template Module Linux CPU by Zabbix agent 内存(memory)来源模板:Template Module Linux memory by Zab ...

  5. zabbix日志监控:操作系统、业务系统、文件大小、多行日志

    zabbix日志监控:操作系统.业务系统.文件大小.多行日志 目录 1 监控操作系统日志 2 监控业务系统日志 具体要求: 分析: 操作: 3 监控日志文件大小 (1)在被管主机当中安装agent ( ...

  6. docker tomcat 多开 实例_给妈妈讲什么是docker

    上周对象突然心血来潮说想养个小宠物,我问想养啥她又说随便,你看着办!!!这我真的比较难办啊!但是咱们程序员能有个对象就不错了,还不赶紧宠着,我只能照办咯!   我去到了一家宠物店,半天也没有找到合适的 ...

  7. 使用docker安装部署Spark集群来训练CNN(含Python实例)

    使用docker安装部署Spark集群来训练CNN(含Python实例) 本博客仅为作者记录笔记之用,不免有很多细节不对之处. 还望各位看官能够见谅,欢迎批评指正. 博客虽水,然亦博主之苦劳也. 如需 ...

  8. CentOS 7.5基于Docker部署4.2 版本的zabbix监控平台

    两台VMware 虚拟机 一台充当zabbix server(安装docker)ip:192.168.73.133 一台充当zabbix agent(安装docker)ip:192.168.73.13 ...

  9. docker history显示完整信息_Docker使用

    背景 docker是什么? docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱 ...

最新文章

  1. 输入命令导出oracle
  2. 创建型设计模式对比总结 设计模式(八)
  3. matlab寻找函数对应的工具箱
  4. SAP WebClient UI drop down list(下拉列表)的一个故障和解决方法
  5. 从键盘上录入学生人数和每个学生的分数,按分数降序输出所有的分数,java冒泡排序应用
  6. ADO Entities Framework不对多表查询进行优化?
  7. win系统mysql找回密码
  8. MySql数据类型分析(银行家舍入法) Part3
  9. 加动画喽。。有一次我设定动画的时间不管用。就把设置时间的代码位置调整到最开始。然后就好了。...
  10. IT 工作者如何写交接文档
  11. 03.NopCommerce功能与特点介绍
  12. Access violation at address 77106D4E in module 'ntdll.dll'. Write of address 004051A5.
  13. sip php 来电,php - 如何在Twilio上传递原来的来电显示? (Sip域语音URL配置) - SO中文参考 - www.soinside.com...
  14. python中怎么统计英文字符的个数_Python之每日一练统计英文文本单词出现的个数、行数、字符数...
  15. SQL:查找某个班级的人数并按班级人数多少进行排列,查找各班年龄最小的女生的班级号,学号,姓名,出生日期并按班级号升序排列
  16. 北京铁警的春运日记本:“手机、爷爷、救护车”
  17. 【机器学习】一张机器学习算法的思维导图
  18. bugku 细心的大象
  19. REST API 最佳实践 – REST 端点设计示例
  20. 怎么把桌面计算机放到快速启动栏,如何设置电脑快速启动?

热门文章

  1. 去哪编辑html5页面,h5页面 判断网页在哪打开
  2. 安装 PyCharm
  3. 用faster-rcnn训练自己的数据集(VOC2007格式,python版)
  4. 行为型模式:中介者模式
  5. SQLServer之创建唯一聚集索引
  6. Emscripten教程之入门指导
  7. 《Windows Server 2012 Hyper-V虚拟化管理实践》——3.2 Hyper-V主机日常管理
  8. 搭建Python+Eclipse开发环境
  9. Javascript基础系列之(五)条件语句(比较操作符)
  10. Windows Server 2012:服务器虚拟化 学习笔记