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

33

我们无法连接到在亚马逊EC2集群中运行的Java应用程序。我们确实已经允许了“JMX端口”(通常是RMI注册表端口) 服务器端口(执行大部分工作)通过安全组访问相关实例。Jconsole可以连接但似乎会卡住,从未显示任何信息。

我们正在以以下方式运行java:

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

我们已经尝试过:

  • 通过telnet连接端口connect,但没有显示任何信息。
  • 我们可以在实例本身上使用远程X11 over SSH运行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)

此外,我们正在使用我的Simple JMX包,该包允许我们设置RMI注册表和服务器端口,这些端口通常是由RMI注册表随机选择的。您也可以使用类似以下JMX URI的方法强制执行此操作:

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

现在我们使用同一个端口号作为服务器和注册表的端口号。过去,我们曾经使用X作为注册表的端口号,X+1作为服务器的端口号,以便于设置安全组规则。您可以在jconsole或其他JMX客户端中连接到注册表端口号。

5个回答

44

我们无法连接到在Amazon EC2集群中运行的Java应用程序。

问题是由两个缺失的设置组合而成。第一个强制JRE优先使用ipv4而不是v6。这是必要的(我猜)因为我们正在尝试通过v4地址连接它:

-Djava.net.preferIPv4Stack=true

真正的阻碍因素是JMX先通过联系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-net地址,并在外部解析为公共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包


2
如果你真的需要(例如通过EC2命令行实用程序),你可以发出DescribeInstances调用来确定哪些标签适用于你的实例,但更好的做法是通过用户数据向实例传达配置信息。 - willglynn
2
AWS有一种机制,可以通过相同的实例元数据通道自动生成和交付凭据:请参阅IAM角色适用于EC2实例。您可以创建一个仅限于EC2 DescribeInstances访问的角色,使您能够自动完成所有操作,而无需进行凭证管理。 - willglynn
我在@RuiGonçalves处添加了更多细节。如果现在更有意义,请告诉我。 - Gray
1
安全组 @RuiGonçalves?你不能只打开12345端口。你还需要打开第二个分配的端口,对吧?参见:https://dev59.com/Ll3Va4cB1Zd3GeqPFPXx#8386052 - Gray
1
@Gray 你说得对,通过在我的实例中运行 netstat -lp 命令,我发现 Java 打开了两个额外的端口。我之前不知道需要一个额外的端口,我猜测之前测试时所有端口都对我打开了... 我真是太蠢了。难道没有非编程方式(例如系统属性)来定义第二个端口吗?如果没有,那我想我会尝试使用你的 SimpleJmx 包! - Rui Gonçalves
显示剩余4条评论

18
根据第二个答案 Why does JMX connection to Amazon EC2 fail?,这里的难点是默认情况下会随机选择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端口。


如果您的实例没有公共EC2主机名(例如,将其隐藏在ELB后面),并且您只想使jmx在内部工作,则按照Mark上述描述设置jmx端口和安全组,并为java.rmi.server.hostname放置实例主机名。如果您调用@Gray答案中列出的/meta-data/public-hostname/端点并收到空响应,则说明您的实例已经按此方式设置。 - Matt
2
添加-Dcom.sun.management.jmxremote.rmi.port解决了我的问题。谢谢。 - devesh-ahuja
你可以将两个端口设置为相同的数字,我认为@mmindenhall。 - Gray

8
通过使用SSH隧道的略微不同的方法
  1. (On the Remote machine) Pass the following flags to the 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. (On the Remote machine) Check which ports java started to use

    $ 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. (On the local machine) Make ssh tunnels for all the ports

    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. (On the local machine) Connect by Java Mission Control to localhost:1099


这种方法在自动化EC2实例时非常有用,因为您不知道您的服务将在哪个实例上运行,因此您事先不知道IP。 - Harshit
问题在于你不知道在连接之前需要转发哪些端口。但是,你可以将端口和 com.sun.management.jmxremote.rmi.port 都设置为相同的端口来使其正常工作。 - Gray
@Gray--SOstopbeingevil,这个问题不是第二步解决了吗? - Timofey
我的观点是,您必须通过ssh连接到框,运行netstat命令,然后再使用端口转发重新连接。如果您同时设置了两个端口,那么您只需要转发1099端口即可。 - Gray
这肯定更容易,不过还需要测试。 - Timofey

1
我们正在使用AWS弹性容器服务来运行我们的Spring Boot服务。以下配置允许我们连接到我们的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)

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

1
Gray提供的答案对我有用,但是我发现我必须打开TCP端口0到65535,否则我就进不去。我认为你可以连接到主JMX端口,然后分配另一个端口。我从这篇博客文章中得到了这个信息,这篇文章一直对我很有帮助。

我们不需要这样做,@Eric。我们只需打开RMI端口和JMX服务器端口。创建JMX服务器时,您确实需要指定两个端口。顺便说一下,simpleJmx包可以很容易地帮您完成这项工作:http://256.com/sources/simplejmx/。 - Gray
请参考此答案:https://dev59.com/Ll3Va4cB1Zd3GeqPFPXx#8386052 - Gray
灰色,这很有趣...然而,我正在使用Coda Hale Metrics库,它在设置JMX时有自己的魔力...我想知道它是否能让我做simplejmx所做的事情? - Eric Pugh
请更新链接。给定的链接似乎显示404错误。 - Avinash Anand
我修复了链接! - Eric Pugh
SimpleJMX已经迁移到http://256stuff.com/sources/simplejmx/。 - Gray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接