如何将 VisualVM 附加到 Docker 容器中的简单 Java 进程 运行

How do I attach VisualVM to a simple Java process running in a Docker container

实际上我想要一个适用于 JEE 容器的解决方案,特别是适用于 Glassfish,但在我尝试了多种设置组合但没有成功之后,我将设置简化为最简单的情况。

这是我在 Docker 容器中启动的 Hello World 守护程序。我想附加 jconsoleVisulaVM 到它。一切都在同一台机器上。

public class Main {
  public static void main(String[] args) {
    while (true) {
      try {
        Thread.sleep(3000);
        System.out.println("Hello, World");
      } catch (InterruptedException e) {
        break;
      }
    }
  }
}

Docker文件

FROM java:8
COPY . /usr/src/myapp
WORKDIR /usr/src/myapp
RUN javac Main.java
CMD ["java", "Main"]

建筑物:docker build -t hello-world-daemon .

运行: docker run -it --rm --name hwd hello-world-daemon

问题:

这里我就不展示我失败的尝试了,以免正确答案有偏差。这应该是一个很常见的问题,但我找不到可行的解决方案。

更新。有效的解决方案

这个Docker文件有效

FROM java:8
COPY . /usr/src/myapp
WORKDIR /usr/src/myapp
RUN javac Main.java
CMD ["java", \
"-Dcom.sun.management.jmxremote", \
"-Dcom.sun.management.jmxremote.port=9010", \
"-Dcom.sun.management.jmxremote.local.only=false", \
"-Dcom.sun.management.jmxremote.authenticate=false", \
"-Dcom.sun.management.jmxremote.ssl=false", "Main"]
EXPOSE 9010

结合docker 运行命令

docker run -it --rm --name hwd -p 9010:9010 hello-world-daemon

VisualVM通过右击Local->Add JMX Connection,然后输入localhost:9010,或者通过添加远程主机[=30]来连接=]

JConsole 通过选择 远程进程 localhost:9010.

进行连接

将连接定义为远程时,可以使用 ifconfig 列出的任何接口。例如,地址为 172.17.0.1docker0 接口有效。容器的地址 172.17.0.2 也有效。

首先你应该运行你应用这些 JVM 参数:

-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9010
-Dcom.sun.management.jmxremote.local.only=false
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

那么你应该为 docker:

公开端口
EXPOSE 9010

同时使用 docker 运行 命令指定端口绑定:

docker run -p 9010:9010 -it --rm --name hwd hello-world-daemon

之后您可以使用 Jconsole 连接到本地 9010 端口并管理 Docker 中的应用程序 运行。

我关注了 an other SO response to a similar question 并且成功了。

我通过添加这些 JVM 参数在容器内启动了我的 Java 进程:

-Dcom.sun.management.jmxremote.port=<port> \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.rmi.port=<port> \
-Djava.rmi.server.hostname=$HOST_HOSTNAME

并启动 Docker 容器,指定 -e HOST_HOSTNAME=$HOSTNAME -p <port>docker run 命令。

然后我可以通过添加远程 JMX 连接 ("File" > "Add a JMX Connection...") 并指定 [=13] 从我的本地 JVisualVm 访问这个远程 Java 应用程序=] 在 "Connection" 输入中,并检查 "Do not require SSL connection".

所回答。 我不得不在我的 Windows 机器上使用 -Djava.rmi.server.hostname java 选项。

请确保不要在您的 Dockerfile 中使用 JSON 格式的 CMD,因为它不支持 shell 扩展。

Dockerfile 示例:

FROM java:8
COPY . /usr/src/myapp
WORKDIR /usr/src/myapp
RUN javac Main.java
#Do not use CMD in JSON format here because shell expansion doesn't work in JSON format
#Shell expansion is needed for the ${HOST} variable.
CMD java -Dcom.sun.management.jmxremote=true \
-Dcom.sun.management.jmxremote.rmi.port=9010 \
-Dcom.sun.management.jmxremote.port=9010 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.local.only=false \
-Djava.rmi.server.hostname=${HOST} \
Main

对于仍然遭受如下错误困扰的所有人:

在我的例子中,我在 Docker YML 中使用了端口的不同端口映射:

例如:

15100:9090

但显然在您的端口绑定中,您必须为外部端口和内部端口分配 相同的端口 !

参考: https://forums.docker.com/t/exposing-mapped-jmx-ports-from-multiple-containers/5287/5

FWIW,这就是我如何将 VisualVM 附加到 Docker 容器内的 Java 进程 运行ning 在 macOS 上:

Main.java:

public class Main {
    public static void main(String args[]) throws Exception {
        while (true) {
            System.out.print("Hello ");
            System.out.println("world");
            Thread.sleep(1000);
        }
    }
}

Docker文件:

FROM openjdk:11.0.2-slim
COPY Main.class /
WORKDIR /
ENTRYPOINT ["java", \
"-Dcom.sun.management.jmxremote=true", \
"-Dcom.sun.management.jmxremote.port=9010", \
"-Dcom.sun.management.jmxremote.local.only=false", \
"-Dcom.sun.management.jmxremote.authenticate=false", \
"-Dcom.sun.management.jmxremote.ssl=false", \
"-Dcom.sun.management.jmxremote.rmi.port=9010", \
"-Djava.rmi.server.hostname=localhost", \
"Main"]

编译 Java 代码,构建映像和 运行 容器,如下所示:

$ javac Main.java
$ docker build -t main .
$ docker run -p 9010:9010 -it main

然后使用 JMX 将 VisualVM 附加到 localhost:9010

感谢大家为我指明了正确的方向。最后我让它在更复杂的配置中工作:Kubernetes via Docker Desktop under Windows 10 on local machine.

我的应用配置:

-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.port=30491
-Dcom.sun.management.jmxremote.rmi.port=30491
-Djava.rmi.server.hostname=localhost

Pod 的端口:

ports:
- name: jmx
  containerPort: 30491
  protocol: TCP

服务端口:

ports:
- name: jmx
  nodePort: 30491
  port: 9010
  protocol: TCP
  targetPort: jmx

您还可以使用 docker-compose 来设置您的容器。步骤:

创建您的映像 (Dockerfile)

FROM openjdk:11
COPY . /usr/src/myapp
WORKDIR /usr/src/myapp

建立你的形象

docker build -t app .

创建标签

docker tag app:latest app:staging

设置您的 docker-compose

app:
    image: app:staging
    ports:
      - 8050:8050
      - 8051:8051
    volumes:
      - ./target/app.jar:/usr/src/myapp/app.jar
    entrypoint:
      - java 
      - -Dspring.profiles.active=local 
      - -Dcom.sun.management.jmxremote=true
      - -Dcom.sun.management.jmxremote.port=8051
      - -Dcom.sun.management.jmxremote.local.only=false 
      - -Dcom.sun.management.jmxremote.authenticate=false
      - -Dcom.sun.management.jmxremote.ssl=false
      - -Dcom.sun.management.jmxremote.rmi.port=8051
      - -Djava.rmi.server.hostname=localhost
      - -jar 
      - ./app.jar

端口 8050 是我用来 运行 JVM 和 8051 建立远程连接的端口。我已经使用 VisualVM 进行了测试,看看我是否可以连接到容器内的 JVM 并且它有效。您只需要添加一个 JMX 连接:

然后会出现流程: