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



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

我们正在使用以下内容运行java:

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

我们已经尝试过:

  • 到端口的电信网络连接,但不显示任何信息
  • 我们可以通过ssh使用remote-X11在实例本身上运行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客户端。

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

原来问题是两个丢失设置的组合。第一个强制JRE首选ipv4和,而不是v6。这是必要的(我想),因为我们正试图通过v4地址连接到它:

-Djava.net.preferIPv4Stack=true

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

-Djava.rmi.server.hostname=ec2-107-X-X-X.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.254IP提供了EC2实例可以请求的关于其自身的信息。我感到失望的是,没有包含仅在经过身份验证的调用中可用的标记。

我最初使用的是extern ipv4地址,但看起来JDK在启动时试图连接到服务器端口。如果它使用外部IP,那么这会减慢我们的应用程序启动时间,直到超时。公共主机名在本地解析为10网络地址,在外部解析为public-ipv4。因此,应用程序现在启动得很快,JMX客户端仍然可以工作。呜呜!

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

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

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

编辑:

我们刚刚让这个问题再次发生。Java JMX代码似乎正在对盒子的主机名进行一些主机名查找,并使用它们来尝试连接和验证JMX连接。

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

最后

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

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

无身份验证:

-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=<public EC2 hostname>

带身份验证:

-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=<public EC2 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
    
  2. (在远程机器上)检查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
    
  3. (在本地计算机上)为的所有端口创建ssh隧道

    ssh -N -L 1099:127.0.0.1:1099 ubuntu@<ec2_ip>
    ssh -N -L 37484:127.0.0.1:37484 ubuntu@<ec2_ip>
    ssh -N -L 45828:127.0.0.1:45828 ubuntu@<ec2_ip>`
    
  4. (在本地机器上)通过Java任务控制连接到localhost:1099

Gray给出的答案对我有效,但我发现我必须打开TCP端口0到65535,否则我无法进入。我认为可以在主JMX端口上连接,然后分配另一个端口。我从这篇博客文章中得到了这一点,它一直对我很有效

我们使用AWS弹性容器服务来运行我们的春季启动服务。下面的配置允许我们连接到docker容器。

无身份验证:

-Dcom.sun.management.jmxremote 
-Dcom.sun.management.jmxremote.port=9090 
-Dcom.sun.management.jmxremote.rmi.port=9090 
-Dcom.sun.management.jmxremote.authenticate=false 
-Dcom.sun.management.jmxremote.ssl=false 
-Djava.rmi.server.hostname=$(/usr/bin/curl -s --connect-timeout 2 
http://169.254.169.254/latest/meta-data/public-ipv4)

我发现它很简洁,也不需要任何其他服务端初始化脚本。

相关内容

  • 没有找到相关文章

最新更新