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

如何使用JMX连接到EC2上运行的Java实例,java,amazon-ec2,jmx,jconsole,Java,Amazon Ec2,Jmx,Jconsole,我们在连接Amazon的EC2集群中运行的Java应用程序时遇到问题。我们已经明确地将“JMX端口”(通常是RMI注册表端口)和服务器端口(完成大部分工作)都允许用于所讨论的实例的安全组。Jconsole连接,但似乎挂起,从不显示任何信息 我们正在使用如下内容运行java: java -server -jar foo.jar other parameters here > java.log 2>&1 我们已尝试: Telnet与端口连接,但不显示任何信息 我们可以通过ss

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

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

java -server -jar foo.jar other parameters here > java.log 2>&1
我们已尝试:

  • Telnet与端口连接,但不显示任何信息
  • 我们可以通过ssh使用remote-X11在实例本身上运行
    jconsole
    ,它连接并显示信息。因此,JRE正在本地导出它
  • 正在打开安全组中的所有端口。威伊
  • 使用
    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)
另外,我们正在使用我的包,它允许我们设置RMI注册表和服务器端口,这通常是由RMI注册表半随机选择的。您还可以使用类似以下JMX URI的内容强制执行此操作:

service:jmx:rmi://localhost:" + serverPort + "/jndi/rmi://:" + registryPort + "/jmxrmi"
现在,我们对服务器和注册表使用相同的端口。在过去,我们使用
X
作为注册表端口,使用
X+1
作为服务器端口,以简化安全组规则。您可以连接到
jconsole
或您正在使用的任何JMX客户端中的注册表端口

我们在连接Amazon的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.254
IP提供了EC2实例可以请求的关于自身的信息。我很失望,这不包括标签,这些标签只在经过身份验证的呼叫中可用

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

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

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

编辑:

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

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

最后


简化JMX创建的一种方法是使用my。

Gray给出的答案对我很有用,但是我发现我必须打开TCP端口0到65535,否则我无法进入。我认为您可以连接主JMX端口,然后分配另一个端口。在第二个答案中,这里的困难在于默认情况下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>
-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=
通过身份验证:

-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>
-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隧道的方法有点不同

  • (在远程计算机上)将以下标志传递给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
    
  • (在远程计算机上)检查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
    
  • (在本地计算机上)为所有端口创建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>`