如何使用JMX连接到在EC2上运行的Java实例

我们在连接到在Amazon的EC2集群中运行的Java应用程序时遇到问题。 我们肯定允许“JMX端口”(通常是RMI注册表端口) 服务器端口(完成大部分工作)到相关实例的安全组。 Jconsole连接但似乎挂起并且从不显示任何信息。

我们正在运行我们的java,如下所示:

java -server -jar foo.jar other parameters here > java.log 2>&1 

我们尝试过:

  • Telnet到端口连接但不显示任何信息。
  • 我们可以使用remote-X11 over ssh在实例上运行jconsole ,它连接并显示信息。 所以JRE在本地导出它。
  • 打开安全组中的所有端口。 Weeee。
  • 使用tcpdump确保流量不会转到其他端口。
  • 在本地模拟它。 我们总是可以使用相同的应用程序参数连接到我们的本地JRE或在我们网络上其他地方运行的JRE。

java -version输出:

 OpenJDK Runtime Environment (IcedTea6 1.11.5) (amazon-53.1.11.5.47.amzn1-x86_64) OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode) 

另外,我们使用的是Simple JMX软件包,它允许我们设置RMI注册表和服务器端口,这些端口通常由RMI注册表半随机选择。 您还可以使用以下JMX URI强制执行此操作:

 service:jmx:rmi://localhost:" + serverPort + "/jndi/rmi://:" + registryPort + "/jmxrmi" 

这些天我们为服务器和注册表使用相同的端口。 在过去,我们使用X作为注册表端口,使用X+1作为服务器端口,以使安全组规则变得容易。 您连接到jconsole的注册表端口或您正在使用的任何JMX客户端。

我们在连接到在Amazon的EC2集群中运行的Java应用程序时遇到问题。

事实certificate,问题是两个缺失设置的组合。 第一个强制JRE更喜欢ipv4而不是 v6。 这是必要的(我猜),因为我们试图通过v4地址连接到它:

 -Djava.net.preferIPv4Stack=true 

真正的阻止是JMX通过首先联系RMI端口来工作,该端口响应JMX客户端连接的主机名和端口。 如果没有其他设置,它将使用盒子的本地IP,这是远程客户端无法路由到的10.XXX虚拟地址。 我们需要添加以下设置,即服务器的外部主机名或IP – 在这种情况下,它是服务器的弹性主机名。

 -Djava.rmi.server.hostname=ec2-107-XXX.compute-1.amazonaws.com 

诀窍,如果你试图自动化你的EC2实例(以及你为什么不这样做),是如何在运行时找到这个地址。 为此,您需要在我们的应用程序启动脚本中添加以下内容:

 # get our _external_ hostname RMI_HOST=`wget -q -O - http://169.254.169.254/latest/meta-data/public-hostname` ... java -server \ -Djava.net.preferIPv4Stack=true -Djava.rmi.server.hostname=$RMI_HOST \ -jar foo.jar other parameters here > java.log 2>&1 

上面wget命令中的神秘169.254.169.254 IP提供了EC2实例可以请求自身的信息。 我很失望,这不包括仅在经过身份validation的呼叫中可用的标签。

我最初使用的是extern ipv4地址,但看起来JDK在启动时尝试建立与服务器端口的连接。 如果它使用外部IP,那么这会减慢我们的应用程序启动时间,直到超时。 public-hostname在本地解析为10-net地址,从外部解析为public-ipv4。 因此,应用程序现在正在快速启动,JMX客户端仍然可以工作。 呜啊!

希望这有助于其他人。 今天花了我3个小时。

要强制JMX服务器在指定端口上启动服务器 RMI注册表,以便可以在EC2安全组中阻止它们,请参阅以下答案:

如何关闭在特定端口上运行的rmiregistry?

编辑:

我们刚刚重新出现这个问题。 似乎Java JMX代码正在对框的主机名执行一些主机名查找,并使用它们来尝试连接并validationJMX连接。

问题似乎是要求盒子的本地主机名解析为盒子的local-ip。 例如,如果您的/etc/sysconfig/networkHOSTNAME=server1.foobar.com那么如果您在server1.foobar.com上进行DNS查找,则应该转到10-NET虚拟地址。 我们正在生成自己的/etc/hosts文件,并且文件中缺少本地主机的主机名。 这导致我们的应用程序要么在启动时暂停,要么根本不启动。

最后

简化JMX创建的一种方法是使用我的SimpleJMX包 。

第二个答案为什么与Amazon EC2的JMX连接失败? ,这里的难点是默认情况下,RMI端口是随机选择的,客户端需要访问JMX和RMI端口。 如果您正在运行jdk7u4或更高版本,则可以通过app属性指定RMI端口。 使用以下JMX设置启动我的服务器对我有用:

没有身份validation

 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.rmi.port=9998 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname= 

使用身份validation

 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.rmi.port=9998 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.password.file=/path/to/jmxremote.password -Djava.rmi.server.hostname= 

我还为我的实例在EC2安全组中打开了端口9998-9999。

使用ssh隧道有点不同的方法

  1. (在远程计算机上)将以下标志传递给JVM

-Dcom.sun.management.jmxremote.port=1099 -Djava.net.preferIPv4Stack=true -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=127.0.0.1

  1. (在远程计算机上)检查java开始使用的端口

netstat -tulpn | grep java

tcp 0 0 0.0.0.0:37484 0.0.0.0:* LISTEN 2904/java tcp 0 0 0.0.0.0:1099 0.0.0.0:* LISTEN 2904/java tcp 0 0 0.0.0.0:45828 0.0.0.0:* LISTEN 2904/java

  1. (在本地计算机上)为所有端口创建ssh隧道

ssh -N -L 1099:127.0.0.1:1099 ubuntu@ ssh -N -L 37484:127.0.0.1:37484 ubuntu@ ssh -N -L 45828:127.0.0.1:45828 ubuntu@

  1. (在本地计算机上)通过Java Mission Control连接到“localhost:1099”

Gray给出的答案对我有用,但是我发现我必须打开TCP端口0到65535或者我没有进入。我认为你可以连接主JMX端口,然后分配另一个端口。 我从这篇一直很适合我的博客文章中得到了这个 。