jmx概念
JMX(Java Management Extensions,即Java管理扩展)是Java平台上为应用程序、设备、系统等植入管理功能的框架。
通过JMX,我们可以监控Java应用程序运行状态,如内存、线程等信息。常用的运维监控如jconsole,jvisualvm,Zabbix、prometheus(通过jmx_exporter采集)等工具对JVM本身的监控都是通过JMX获取的信息。
MBean
托管Bean(Managed Bean,MBean)是一种通过依赖注入创建的JavaBean。托管Bean主要用在Java管理扩展技术中。在Java EE 6的规范中,对托管Bean做了更具体的规定。
MBean代表了运行在Java虚拟机上的资源,例如应用程序或Java EE服务(事务监控、JDBC驱动程序等)。其可以用于收集如性能、资源使用率、问题信息等关键的统计信息(通过拉取),获取或设置应用程序的配置或属性(通过推送或拉取),以及对故障或状态变化等的通知事件(通过推送)。
JVM自带MBean如下:
除了以上这些MBean,还可以自定义MBean,此处略。
本地监控
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
OperatingSystemMXBean osbean = ManagementFactory.getOperatingSystemMXBean();
ManagementFactory.getClassLoadingMXBean();
ManagementFactory.getMemoryMXBean();
……
远程监控
启动参数
-Dcom.sun.management.jmxremote=true
-Djava.rmi.server.hostname=127.0.0.1
-Dcom.sun.management.jmxremote.port=10057
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
注意:此处配置的端口号需与服务本身端口号不同,否则将导致服务无法正常启动。
远程访问
private GarbageCollectorMXBean youngGC;
private GarbageCollectorMXBean fullGC;
private RuntimeMXBean runtimeMXBean;
private ThreadMXBean threadMXBean;
private OperatingSystemMXBean osbean;
private ClassLoadingMXBean classLoadingMXBean;
private MemoryMXBean memoryMXBean;
#无需访问权限
JMXServiceURL serviceURL = new JMXServiceURL(url);
#JMXConnector 实现了Closeable接口,可以使用try-with-resource语法来方便关闭资源
try(JMXConnector connector = JMXConnectorFactory.connect(serviceURL )) {
MBeanServerConnection mbs = connector.getMBeanServerConnection();
this.runtimeMXBean = ManagementFactory.newPlatformMXBeanProxy(mbs,"java.lang:type=Runtime",
RuntimeMXBean.class);
this.threadMXBean = ManagementFactory.newPlatformMXBeanProxy(mbs,"java.lang:type=Threading",
ThreadMXBean.class);
this.osbean = ManagementFactory.newPlatformMXBeanProxy(mbs,"java.lang:type=OperatingSystem",
OperatingSystemMXBean.class);
this.classLoadingMXBean = ManagementFactory.newPlatformMXBeanProxy(mbs,"java.lang:type=ClassLoading",
ClassLoadingMXBean.class);
this.memoryMXBean = ManagementFactory.newPlatformMXBeanProxy(mbs,"java.lang:type=Memory",
MemoryMXBean.class);
List<GarbageCollectorMXBean> gcList = ManagementFactory.getPlatformMXBeans(mbs, GarbageCollectorMXBean.class);
……
……
}
安全性与加密访问
JMX 作为 Java的一种Bean管理机制,如果JMX服务端口暴露,那么远程攻击者可以让该服务器远程加载恶意的Bean文件(如MLet),随着Bean的滥用导致远程代码执行。
因此需要通过启用 Solr JMX 服务身份验证来保证安全性。
2019年11月18日,360CERT检测到 Solr 官方发布了安全更新,而该版本中就出现了jmx相关安全漏洞。
配置文件
如设置加密访问,需设置以下文件
密码(jmxremote.password)及权限(jmxremote.access)
#若不存在jmxremote.access文件,则复制jmxremote.password.template一份为jmxremote.password
#若存在则不执行
cp jmxremote.password.template jmxremote.password
#给密码文件加上写权限
chmod +w jmxremote.password
#给密码文件增加用户密码
echo "user password" >> jmxremote.password
#修改jmxremote.*的权限,只允许启动用户对该文件拥有读写权限:
chmod 600 jmxremote.password
以追加文件方式往jmx权限控制文件中加入一个只读权限的帐号
echo "user readonly" >> jmxremote.access
启动参数
-Dcom.sun.management.jmxremote=true
-Djava.rmi.server.hostname=127.0.0.1
-Dcom.sun.management.jmxremote.rmi.port=10057
-Dcom.sun.management.jmxremote.local.only=false
-Dcom.sun.management.jmxremote.port=10057
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.password.file=${JAVA_HOME}/jre/lib/management/jmxremote.password
-Dcom.sun.management.jmxremote.access.file=${JAVA_HOME}/jre/lib/management/jmxremote.access
远程访问
#需访问权限
JMXServiceURL serviceURL = new JMXServiceURL(url);
Map<String, Object> environment = new HashMap<>();
environment.put(JMXConnector.CREDENTIALS, new String[]{"user", "password"});
JMXConnector connector = JMXConnectorFactory.connect(serviceURL, environment);
可能的异常信息
若权限未配置出现的异常如下
2020-07-10 17:11:37 | ERROR | http-nio-10057-exec-1 |
org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/project].
[dispatcherServlet]| 175 | Servlet.service() for servlet [dispatcherServlet] in context
with path [/project] threw exception [Request processing failed;
nested exception is java.lang.SecurityException:
Authentication failed! Credentials required] with root cause
java.lang.SecurityException: Authentication failed! Credentials required
at com.sun.jmx.remote.security.JMXPluggableAuthenticator.authenticationFailure(JMXPluggableAuthenticator.java:211)
at com.sun.jmx.remote.security.JMXPluggableAuthenticator.authenticate(JMXPluggableAuthenticator.java:163)
at sun.management.jmxremote.ConnectorBootstrap$AccessFileCheckerAuthenticator.authenticate(ConnectorBootstrap.java:227)
docker环境部署
Dockerfile
FROM openjdk:8-jre-slim
MAINTAINER user
ENV JAVA_OPTS='-Dcom.sun.management.jmxremote=true -Djava.rmi.server.hostname=ip -Dcom.sun.management.jmxremote.port=10057 -Dcom.sun.management.jmxremote.rmi.port=10057 -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.password.file=/usr/local/openjdk-8/lib/management/jmxremote.password -Dcom.sun.management.jmxremote.access.file=/usr/local/openjdk-8/lib/management/jmxremote.access'
RUN cp /usr/local/openjdk-8/lib/management/jmxremote.password.template /usr/local/openjdk-8/lib/management/jmxremote.password && chmod +w /usr/local/openjdk-8/lib/management/jmxremote.password && echo "user password" >> /usr/local/openjdk-8/lib/management/jmxremote.password && chmod 600 /usr/local/openjdk-8/lib/management/jmxremote.password && echo "user readonly" >> /usr/local/openjdk-8/lib/management/jmxremote.access
ENV TZ=PRC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ADD target/project-*.jar /app.jar
ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /app.jar"]
docker build -t com/project:1.0 .
docker run -d --restart=always -p 8080:880 -p 10054:10054 --name project images-docker.com.cn/common/project:0.0.1
参考文档
1、https://zh.wikipedia.org/zh-hans/JMX
2、https://www.anquanke.com/post/id/193208
3、https://stackoverflow.com/questions/970666/accessing-a-remote-garbagecollectormxbean