在 GCE 上的 Docker 中将 VisualVM 或 JConsole 连接到 Java

use*_*927 4 java jmx visualvm google-compute-engine docker

我正在尝试在远程 Java-in-Docker 进程上进行一些 CPU 采样。

我已经在这里查看了相关问题,并尝试了一切,但无济于事,所以我在这里发布我的设置。

我有一个 Java 进程 (openjdk-8) 在 Google 计算引擎 (GCE) 实例上的 Docker 容器中运行。GCE实例和容器都运行Debian-9。我想将 VisualVM 或 JConsole 连接到我的 Java 进程。

我可以在本地运行我的 docker 容器,并使用 localhost:9010 连接 VisualVM 和 jconsole。

我使用以下命令在 VM 启动脚本中启动容器:

docker run -d -p 9010:9010 <my container>
Run Code Online (Sandbox Code Playgroud)

Dockerfile 还具有:

EXPOSE 9010
Run Code Online (Sandbox Code Playgroud)

由 Dockerfile CMD 启动的 Java 进程具有以下相关参数:

"-Dcom.sun.management.jmxremote", \
"-Dcom.sun.management.jmxremote.port=9010", \
"-Dcom.sun.management.jmxremote.rmi.port=9010", \
"-Dcom.sun.management.jmxremote.local.only=false", \
"-Dcom.sun.management.jmxremote.authenticate=false", \
"-Dcom.sun.management.jmxremote.ssl=false", \
Run Code Online (Sandbox Code Playgroud)

我已使用以下命令在 gcloud 防火墙中打开端口 9010:

gcloud compute firewall-rules create jmx-port --allow=tcp:9010,udp:9010
Run Code Online (Sandbox Code Playgroud)

我已使用 netcat 验证该端口已打开并且可以与其建立 TCP 连接。

我从同一个 Docker 容器打开了其他端口,客户端成功连接到这些端口。它们以相同的方式公开并映射到主机端口(-p 端口:端口),并以相同的方式在防火墙中打开。

我正在传递 GCE 实例的外部 IP 地址。例如,如果我这样做:

gcloud compute instances list
Run Code Online (Sandbox Code Playgroud)

它告诉我:

NAME            ZONE           MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP      STATUS
my-server-b23j  us-central1-d  n1-standard-1               10.240.0.2   108.357.213.99   RUNNING
Run Code Online (Sandbox Code Playgroud)

然后我将使用这个论点:

108.357.213.99:9010
Run Code Online (Sandbox Code Playgroud)

作为远程 jmx 连接主机:端口对。

VisualVM 和 JConsole 都告诉我它们无法连接到远程 JMX 服务。在这两种情况下,我都拒绝安全连接,然后他们说:

Cannot connect to 108.357.213.99:9010 using
service:jmx:rmi:////jndi/rmi://108.357.213.99:9010/jmxrmi
Run Code Online (Sandbox Code Playgroud)

无奈之下,我添加了一条防火墙规则,在所有端口 0-65535 上启用 TCP/UDP 连接,但这并没有什么区别——它们仍然无法连接。

我读到 JMX-RMI 打开匿名端口,并且您可以(至少部分?)通过指定以下两者来禁用此行为:

"-Dcom.sun.management.jmxremote.port=9010", \
"-Dcom.sun.management.jmxremote.rmi.port=9010", \
Run Code Online (Sandbox Code Playgroud)

然而,它对我来说并不起作用。

我在这里读到您需要指定 rmi 服务器主机名:

-Djava.rmi.server.hostname='192.168.99.100'
Run Code Online (Sandbox Code Playgroud)

但我的服务器 IP 是临时的——它是在我创建实例时由 Google Compute Engine 分配的,因此我无法将其与其余 Java 参数一起硬连接到 Dockerfile 中。

我是否必须获得静态 IP 地址才能完成这项工作?

P.J*_*sch 6

一种可能的解决方案是 ssh 进入 GCE 盒子和端口转发端口 9010。这可以通过本地控制台完成:

gcloud compute ssh name-of-your-gce-engine -- -L 9010:localhost:9010
Run Code Online (Sandbox Code Playgroud)

然后在jconsolejvisualvm您连接到localhost:9010. 这里使用 localhost 意味着jconsole/jvisualvm将连接到您的本地计算机,此连接通过 ssh 隧道连接到您的 GCE 引擎,并连接到参数中定义的主机和端口-L,即localhost:9010,但从 GCE 引擎的角度来看。这意味着您最终会收到申请。

在启动程序之前你仍然需要设置rmi服务器名称,但是你必须使用

-Djava.rmi.server.hostname='localhost'
Run Code Online (Sandbox Code Playgroud)

这样 RMI 将告诉您jconsole/jvisualvm 使用 localhost,然后这将解析为您的本地隧道端点。当然你还需要这些:

"-Dcom.sun.management.jmxremote", \
"-Dcom.sun.management.jmxremote.port=9010", \
"-Dcom.sun.management.jmxremote.rmi.port=9010", \
"-Dcom.sun.management.jmxremote.local.only=false", \
"-Dcom.sun.management.jmxremote.authenticate=false", \
"-Dcom.sun.management.jmxremote.ssl=false", \
Run Code Online (Sandbox Code Playgroud)