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

Posted

技术标签:

【中文标题】如何使用 JMX 连接到在 EC2 上运行的 Java 实例【英文标题】:How to connect to Java instances running on EC2 using JMX 【发布时间】:2012-11-23 22:51:42 【问题描述】:

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

顺便说一句,我们正在使用我的Simple JMX 包,它允许我们设置两个 RMI 注册表和服务器端口,这些端口通常由 RMI 注册表半随机选择。您还可以使用以下 JMX URI 强制执行此操作:

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

如今,我们为服务器和注册表使用相同的端口。过去我们使用X 作为注册端口,X+1 作为服务器端口,以简化安全组规则。您连接到jconsole 中的注册表端口或您正在使用的任何 JMX 客户端。

【问题讨论】:

【参考方案1】:

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

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

-Djava.net.preferIPv4Stack=true

真正的阻碍是 JMX 通过首先联系 RMI 端口来工作,该端口以 主机名 和 JMX 客户端连接的端口进行响应。如果没有其他设置,它将使用框的本地 IP,这是一个远程客户端无法路由到的 10.X.X.X 虚拟地址。我们需要添加以下设置,即服务器的 external 主机名或 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 实例可以请求的关于自身的信息。我很失望这包含仅在经过身份验证的调用中可用的标签。

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

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

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

How to close rmiregistry running on particular port?

编辑:

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

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

最后

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

【讨论】:

如果您确实需要(例如通过 EC2 命令行实用程序),您可以发出 DescribeInstances 调用来确定哪些标签适用于您的实例,但更好的做法是通过以下方式将配置信息传达给实例用户数据。 谢谢@willglynn。我希望获得标签而不必将我的访问/密钥添加到我的实例中。是的,我们现在使用 UserData 但我宁愿它是 key=value 类型的东西,这样操作员就不会打错字了。 有一种机制可以通过同一实例元数据通道自动生成和交付 AWS 凭证:请参阅 IAM roles for EC2 instances。您可以创建一个仅限于 EC2 DescribeInstances 访问的角色,让您自动执行所有操作,而不会因凭证管理而发疯。 安全组@RuiGonçalves?您不能只打开端口 12345。您还需要打开第二个分配的端口,对吗?见:***.com/questions/8386001/… @Gray 你是对的,在我的实例中通过netstat -lp 我可以看到java打开了两个额外的端口。我不知道我还需要一个,我想当我在测试之前测试所有为我打开的端口时......我傻了。没有一种非编程方式(即通过系统属性的方式)来定义第二个端口,是吗?如果没有,我想我会试试你的 SimpleJmx 包,然后!【参考方案2】:

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

【讨论】:

如果您的实例没有公共 EC2 主机名(例如,将其隐藏在 ELB 后面)并且您只希望 jmx 在内部工作,然后按照 Mark 的描述设置 jmx 端口和安全组,并将实例主机名放入 java.rmi.server.hostname。如果您调用@Gray 的答案中列出的 /meta-data/public-hostname/ 端点并且您得到一个空响应,您就会知道您的实例是以这种方式设置的。 添加 -Dcom.sun.management.jmxremote.rmi.port 为我解决了这个问题。谢谢。 您可以将两个端口设置为相同的数字,我认为@mmindenhall。【参考方案3】:

使用 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>`
    

    (在本地机器上)通过 Java Mission Control 连接到 localhost:1099

【讨论】:

此方法在您自动化 EC2 实例时很有用,因为您不知道您的服务将在哪个实例中运行,因此您事先不知道 IP。 问题是你在附加你需要转发的端口之前并不知道。但是,您可以将端口和 com.sun.management.jmxremote.rmi.port 设置为同一个端口以使其正常工作。 @Gray--SOstopbeingevil 这个问题可以通过第 2 步解决,不是吗? 我的意思是你必须通过 ssh 连接到盒子,运行 netstat 命令,然后使用转发的端口再次连接。如果你设置了两个端口,那么你只需要转发 1099。 这个肯定比较简单,不过需要测试一下【参考方案4】:

Gray 给出的答案对我有用,但是我发现我必须打开 TCP 端口 0 到 65535 否则我无法进入。我认为您可以连接到主 JMX端口,然后分配另一个端口。我从 this blog post 那里得到的,它一直对我很有效。

【讨论】:

我们确实必须这样做@Eric。我们只打开了 RMI 端口和 JMX 服务器端口。创建 JMX 服务器时,您确实需要指定这两个端口。顺便说一句,simpleJmx 包很容易为您做到这一点:256.com/sources/simplejmx 看到这个答案:***.com/questions/8386001/… Gray,这很有趣......但是,我正在使用 Coda Hale Metrics 库,它在设置 JMX 时有自己的魔力......我想知道它是否可以让我做 simplejmx 所做的事情? 请更新链接。给定的链接似乎给出了 404 我修复了链接!【参考方案5】:

我们正在使用 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)

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

【讨论】:

以上是关于如何使用 JMX 连接到在 EC2 上运行的 Java 实例的主要内容,如果未能解决你的问题,请参考以下文章

如何将 redshift 数据库连接到在 ec2 实例上运行的 bash 脚本

无法连接到在 EC2 上运行的 Mongodb

visualvm 可以通过 JMX 自动连接到远程进程吗?

如何使用 JMX 连接到 localhost jvm 上的 java 程序?

如何将aws上的ec2实例连接到我公司的***

无法连接到在 EC2 实例上运行的 Tigase 服务器:连接被拒绝