如何从外部访问docker中的JMX接口?

时间:2015-07-07 00:40:16

标签: 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和node-jmx / npm:jmx时会出现相同的错误):< / p>

失败时的堆栈跟踪对于每个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和node-jmx / npm:jmx)时,JMX监控正常工作。

我认为这表明我的JMX端口处于活动状态且工作正常,但是当我远程执行JMX监控(从机器2)时,看起来JMX工具无法识别内部docker IP(172.17.0.85)

以下是机器1上JMX监控工作的相关(我认为)网络配置元素(请注意docker ip,172.17.42.1):

docker0   Link encap:Ethernet  HWaddr ...
      inet addr:172.17.42.1  Bcast:0.0.0.0  Mask:255.255.0.0
      inet6 addr:... Scope:Link
      UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
      RX packets:6787941 errors:0 dropped:0 overruns:0 frame:0
      TX packets:4875190 errors:0 dropped:0 overruns:0 carrier:0
      collisions:0 txqueuelen:0
      RX bytes:1907319636 (1.9 GB)  TX bytes:639691630 (639.6 MB)

wlan0     Link encap:Ethernet  HWaddr ... 
      inet addr:10.0.1.201  Bcast:10.0.1.255  Mask:255.255.255.0
      inet6 addr:... Scope:Link
      UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
      RX packets:4054252 errors:0 dropped:66 overruns:0 frame:0
      TX packets:2447230 errors:0 dropped:0 overruns:0 carrier:0
      collisions:0 txqueuelen:1000
      RX bytes:2421399498 (2.4 GB)  TX bytes:1672522315 (1.6 GB)

这是远程计算机(计算机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

5 个答案:

答案 0 :(得分:31)

为完整起见,以下解决方案有效。应该使用已建立的特定参数运行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. 

完成此操作后,您应该能够从本地或远程计算机执行JMX监视(jmxtrans,node-jmx,jconsole等)。

感谢logo option让我们快速简单地解决这个问题!

答案 1 :(得分:7)

对于开发环境,您可以将java.rmi.server.hostname设置为catch-all IP address 0.0.0.0

示例:

 -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

答案 2 :(得分:5)

我发现尝试在RMI上设置JMX是一件痛苦的事,特别是因为你必须在启动时指定-Djava.rmi.server.hostname=<IP>。我们在Kubernetes运行我们的码头图像,一切都是动态的。

我最终使用的是JMXMP而不是RMI,因为这只需要一个TCP端口打开,而且没有主机名。

我当前的项目使用Spring,可以通过添加以下内容进行配置:

<bean id="serverConnector"
    class="org.springframework.jmx.support.ConnectorServerFactoryBean"/>

(在Spring之外,您需要设置自己的JMXConncetorServer才能使其正常工作)

与此依赖关系一起(因为JMXMP是可选扩展,而不是JDK的一部分):

<dependency>
    <groupId>org.glassfish.main.external</groupId>
    <artifactId>jmxremote_optional-repackaged</artifactId>
    <version>4.1.1</version>
</dependency>

在启动JVisualVM时需要在类路径中添加相同的jar才能通过JMXMP进行连接:

jvisualvm -cp "$JAVA_HOME/lib/tools.jar:<your_path>/jmxremote_optional-repackaged-4.1.1.jar"

然后使用以下连接字符串连接:

service:jmx:jmxmp://<url:port>

(默认端口为9875)

答案 3 :(得分: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.0

答案 4 :(得分:0)

要添加一些其他见解,我使用了一些Docker端口映射,而先前的答案都没有直接对我起作用。经过调查,我在这里找到了答案:How to connect with JMX from host to Docker container in Docker machine?,以提供所需的见解。

这是我认为会发生的事情:

我按照此处其他答案中的建议设置了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中映射端口host:1099-> container:1098。
  • 我使用上述JMX设置在docker内部运行JVM。
  • Docker容器中的JMX代理现在侦听给定的端口1098。
  • 我使用URL localhost:1099在主机(Docker外部)上启动JConsole。我使用1099,因为我使用了{99.1098}的host:docker端口映射。
  • JConsole可以很好地连接到Docker内部的JMX代理。
  • JConsole询问JMX在哪里读取监视数据。
  • JMX代理以配置的信息和地址作为响应:localhost:1098
  • JConsole现在尝试连接到给定的地址localhost:1098
  • 此操作失败,因为localhost(外部Docker)上的端口1098没有监听。端口1099已映射到Docker:1098。 JMX应该告诉JConsole从localhost:1098读取监视信息,而不是localhost:1099,因为1099是Docker容器中从主机映射到1098的端口。

作为修复,我将host:docker端口映射从1099:1098更改为1098:1098。现在,JMX仍然告诉JConsole连接到localhost:1098来监视信息。但是现在它可以工作了,因为外部端口与Docker内部JMX宣告的端口相同。

我希望这同样适用于SSH隧道和类似情况。您必须匹配配置JMX来发布的内容以及JConsole视为运行它的主机上的地址空间的内容。

也许可以使用jmxremote.portjmxremove.rmi.porthostname属性来发挥作用,以使用不同的端口映射来完成这项工作。但是我有机会使用相同的端口,因此使用它们简化了端口,这对我来说有效。