Java 如何从外部访问docker中的JMX接口?
我试图远程监视docker中运行的JVM。配置如下所示:Java 如何从外部访问docker中的JMX接口?,java,docker,jmx,jmxtrans,Java,Docker,Jmx,Jmxtrans,我试图远程监视docker中运行的JVM。配置如下所示: 机器1:在ubuntu机器上的docker中运行JVM(在我的例子中,运行kafka);本机IP为10.0.1.201;在docker中运行的应用程序为172.17.0.85 机器2:运行JMX监控 请注意,当我从机器2运行JMX监视时,它会失败,并出现以下错误(注意:当我运行jconsole、jvisualvm、jmxtrans和节点JMX/npm:JMX时,也会出现相同的错误): 对于每个JMX监视工具,失败时的堆栈跟踪看起来类似
- 机器1:在ubuntu机器上的docker中运行JVM(在我的例子中,运行kafka);本机IP为10.0.1.201;在docker中运行的应用程序为172.17.0.85
- 机器2:运行JMX监控
java.rmi.ConnectException: Connection refused to host: 172.17.0.85; nested exception is
java.net.ConnectException: Operation timed out
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:619)
(followed by a large stack trace)
现在有趣的部分是,当我在运行docker的同一台机器(上面的机器1)上运行相同的工具(jconsole、jvisualvm、jmxtrans和节点jmx/npm:jmx)时,jmx监控工作正常
我认为这表明我的JMX端口处于活动状态并且工作正常,但是当我远程(从机器2)执行JMX监控时,JMX工具似乎无法识别内部docker IP(172.17.0.85)
下面是JMX监控工作的机器1上的相关(我认为)网络配置元素(注意docker ip,172.17.42.1):
这是远程机器(机器2)上的相关网络配置元素,我从中获得JMX错误:
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
options=3<RXCSUM,TXCSUM>
inet6 ::1 prefixlen 128
inet 127.0.0.1 netmask 0xff000000
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
nd6 options=1<PERFORMNUD>
en1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
ether ....
inet6 ....%en1 prefixlen 64 scopeid 0x5
inet 10.0.1.203 netmask 0xffffff00 broadcast 10.0.1.255
nd6 options=1<PERFORMNUD>
media: autoselect
status: active
lo0:flags=8049 mtu 16384
选项=3
inet6::1预桥128
inet 127.0.0.1网络掩码0xff000000
inet6 fe80::1%lo0预桥64作用域ID 0x1
nd6选项=1
en1:标志=8863 mtu 1500
乙醚。。。。
inet6….%en1预桥64作用域ID 0x5
inet 10.0.1.203网络掩码0xffffff00广播10.0.1.255
nd6选项=1
媒体:自动选择
状态:活动
为了完整起见,以下解决方案有效。JVM运行时应设置特定参数,以启用远程docker JMX监控,如下所示:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.port=<PORT>
-Dcom.sun.management.jmxremote.rmi.port=<PORT>
-Djava.rmi.server.hostname=<IP>
where:
<IP> is the IP address of the host that where you executed 'docker run'
<PORT> is the port that must be published from docker where the JVM's JMX port is configured (docker run --publish 7203:7203, for example where PORT is 7203). Both `port` and `rmi.port` can be the same.
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.port=
-Dcom.sun.management.jmxremote.rmi.port=
-Djava.rmi.server.hostname=
哪里:
是执行“docker run”的主机的IP地址
必须从配置JVM的JMX端口的docker发布的端口(docker run--publish 7203:7203,例如,其中端口为7203)。'port'和'rmi.port'可以相同。
完成后,您应该能够从本地或远程计算机执行JMX监视(jmxtrans、节点JMX、jconsole等)
感谢您使这成为一个非常快速和简单的修复 我发现尝试在RMI上设置JMX是一件痛苦的事情,特别是因为启动时必须指定
-Djava.RMI.server.hostname=
。我们在Kubernetes运行docker图像,在那里一切都是动态的
我最终使用JMXMP而不是RMI,因为这只需要打开一个TCP端口,而不需要主机名
我当前的项目使用Spring,可以通过添加以下内容对其进行配置:
<bean id="serverConnector"
class="org.springframework.jmx.support.ConnectorServerFactoryBean"/>
(在Spring之外,您需要设置自己的JMXConncetorServer以使其正常工作)
与此依赖关系一起(因为JMXMP是可选扩展,而不是JDK的一部分):
org.glassfish.main.external
jmxremote_可选-重新打包
4.1.1
在启动JVisualVM时,您需要在类路径中添加相同的jar,以便通过JMXMP进行连接:
jvisualvm -cp "$JAVA_HOME/lib/tools.jar:<your_path>/jmxremote_optional-repackaged-4.1.1.jar"
jvisualvm-cp“$JAVA_HOME/lib/tools.jar:/jmxremote_optional-repacked-4.1.1.jar”
然后使用以下连接字符串进行连接:
service:jmx:jmxmp://<url:port>
服务:jmx:jmxmp://
(默认端口为9875)对于开发环境,您可以将
java.rmi.server.hostname
设置为0.0.0.0
例如:
经过相当多的挖掘,我发现了这个配置
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.port=1098
-Dcom.sun.management.jmxremote.rmi.port=1098
-Djava.rmi.server.hostname=localhost
-Dcom.sun.management.jmxremote.local.only=false
与上面的另一个不同之处在于,
java.rmi.server.hostname
被设置为localhost
,而不是0.0.0
为了增加一些额外的见解,我使用了一些Docker端口映射,之前的答案都没有直接适用于我。经过调查,我在这里找到了答案:提供所需的见解
我相信会发生这样的事情:
我按照其他答案中的建议设置了JMX:
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.port=1098
-Dcom.sun.management.jmxremote.rmi.port=1098
-Djava.rmi.server.hostname=localhost
-Dcom.sun.management.jmxremote.local.only=false
程序流程:
- 我运行Docker容器并将端口从主机公开/映射到容器。假设我在Docker中映射端口主机:1099->容器:1098
- 我在docker中使用上述JMX设置运行JVM
- Docker容器中的JMX代理现在侦听给定的端口1098
- 我使用URL localhost:1099在主机(Docker外部)上启动JConsole。我使用1099,因为我使用了1099:1098的
端口映射host:docker
- JConsole很好地连接到Docker内部的JMX代理
- JConsole询问JMX从何处读取监控数据
- JMX代理使用配置的信息和地址进行响应:
localhost:1098
- JConsole现在尝试连接到给定的地址
localhost:1098
- 此操作失败,因为本地主机(Docker外部)上的端口1098未侦听。端口1099已映射到
。JMX应该告诉JConsole从Docker:1098
读取监控信息,而不是localhost:1099
,因为1099是Docker容器内从主机映射到1098的端口localhost:1098
host:docker
端口映射从1099:1098
更改为1098:1098
。现在,JMX仍然告诉JConsole连接到localhost:1098
,以获取监控信息。但现在它可以工作了,因为外部端口与JMX inside Docker发布的相同
我希望同样的情况也适用于SSH隧道和类似场景。您必须将配置的JMX与ad匹配
-Djava.rmi.server.hostname=0.0.0.0 \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=${JMX_PORT} \
-Dcom.sun.management.jmxremote.rmi.port=${JMX_PORT} \
-Dcom.sun.management.jmxremote.local.only=false \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.port=1098
-Dcom.sun.management.jmxremote.rmi.port=1098
-Djava.rmi.server.hostname=localhost
-Dcom.sun.management.jmxremote.local.only=false
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.port=1098
-Dcom.sun.management.jmxremote.rmi.port=1098
-Djava.rmi.server.hostname=localhost
-Dcom.sun.management.jmxremote.local.only=false