31

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

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

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

我们尝试过:

  • Telnet 到端口连接,但未显示任何信息。
  • 我们可以jconsole通过 ssh 使用 remote-X11 在实例本身上运行,它会连接并显示信息。因此,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 客户端。

4

5 回答 5

43

我们在连接到在 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

上面命令中的神秘169.254.169.254IPwget提供了 EC2 实例可以请求的关于自身的信息。我很失望这不包括仅在经过身份验证的调用中可用的标签。

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

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

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

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

编辑:

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

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

最后

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

于 2012-12-05T23:48:09.787 回答
18

根据第二个答案,为什么 JMX 连接到 Amazon EC2 失败?,这里的难点在于,默认情况下RMI端口是随机选择的,客户端需要同时访问JMX和RMI端口。如果您运行的是 jdk7u4 或更高版本,则可以通过 app 属性指定 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。

于 2014-12-30T07:23:05.467 回答
7

使用 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 Mission Control 连接到localhost:1099

于 2017-04-17T20:10:24.080 回答
1

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

于 2013-03-21T01:46:50.780 回答
1

我们正在使用 AWS Elastic Container Service 来运行我们的 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)

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

于 2020-05-11T10:59:40.003 回答