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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35108868/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 16:19:11  来源:igfitidea点击:

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

javadockerjmx

提问by nolexa

Actually I wanted a solution working for JEE containers, specifically for Glassfish, but after I tried many combinations of settings and did not succeed, I reduced the setup to the simplest possible case.

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

Here is my Hello World daemon started in a Docker container. I want to attach jconsoleor VisulaVMto it. Everything is on the same machine.

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

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

Dockerfile

文件

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

Building: docker build -t hello-world-daemon .

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

Running: docker run -it --rm --name hwd hello-world-daemon

跑步: docker run -it --rm --name hwd hello-world-daemon

Questions:

问题:

  • what JVM parameters should be added to CMDcommand line?
  • what ports should be exposed and published?
  • what network mode should Docker container be using?
  • 应该在CMD命令行中添加哪些 JVM 参数?
  • 应该公开和发布哪些端口?
  • Docker 容器应该使用什么网络模式?

I do not show my failed attempts here so that correct answers will not be biased. This should be a pretty common problem, yet I could not find a working solution.

我没有在这里展示我失败的尝试,以便正确的答案不会有偏见。这应该是一个非常普遍的问题,但我找不到可行的解决方案。

Update. Worked solution

更新。有效的解决方案

This Dockerfile works

这个 Dockerfile 有效

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

in combination with the docker run command

结合 docker run 命令

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

VisualVMconnects via right click Local->Add JMX Connection, and then entering localhost:9010, or through adding a remote host.

VisualVM通过右键单击Local->Add JMX Connection,然后输入localhost:9010,或通过添加远程主机来连接。

JConsoleconnects via selecting a Remote processwith localhost:9010.

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

When defining the connection as remote, any interface listed by ifconfigcan be used. For instance, docker0interface with address 172.17.0.1works. The container's address 172.17.0.2works too.

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

采纳答案by eg04lt3r

At first you should run you application with these JVM params:

首先,您应该使用这些 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

Then you should expose port for docker:

然后你应该为 docker 暴露端口:

EXPOSE 9010

Also specify port binding with docker run command:

还要使用 docker run 命令指定端口绑定:

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

After that you can connect with Jconsole to local 9010 port and manage application run in Docker.

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

回答by Anthony O.

I followed an other SO response to a similar questionand it worked.

我遵循了对类似问题的其他 SO 响应,并且它起作用了。

I started my Java process inside the container by adding those JVM params:

我通过添加这些 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

and started the Docker container specifying -e HOST_HOSTNAME=$HOSTNAME -p <port>to the docker runcommand.

并开始多克尔容器指定-e HOST_HOSTNAME=$HOSTNAME -p <port>docker run命令。

Then I've been able to access to this remote Java app from my local JVisualVm by adding a remote JMX connection ("File" > "Add a JMX Connection...") and specifying <dockerhostname>:<port>in the "Connection" input, and checking "Do not require SSL connection".

然后,通过添加远程 JMX 连接(“文件”>“添加 JMX 连接...”)并<dockerhostname>:<port>在“连接”输入中指定并检查“不需要 SSL 连接”。

回答by Chris

As answered by Anthony. I had to use the -Djava.rmi.server.hostnamejava option on my Windows machine.

正如安东尼回答的那样。我不得不-Djava.rmi.server.hostname在我的 Windows 机器上使用java 选项。

Just be sure not to use the CMD in JSON format in your Dockerfile as this doesn't support shell expansion.

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

Dockerfile example:

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

回答by Robocide

To all of you that still suffer from an error like the below:

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

enter image description here

在此处输入图片说明

In my case it was that i used in my Docker YML different port mappings for the ports:

在我的情况下,我在我的 Docker YML 中使用了不同的端口映射:

e.g:

例如:

15100:9090

but apparently in your port bindings you must assign the SAME port for external port and internal port!

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

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

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

回答by Rob van der Leek

FWIW, this is how I was able to attach VisualVM to a Java process inside a Docker container running on macOS:

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

Main.java:

主.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);
        }
    }
}

Dockerfile:

Dockerfile:

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"]

Compile the Java code, build the image and run the container like this:

编译 Java 代码,构建镜像并像这样运行容器:

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

Then attach VisualVM using JMX to localhost:9010

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

回答by Alterant

Thanks to all of you for routing me to the right direction. Finally I got it working in more complex config: Kubernetes via Docker Desktop under Windows 10 on local machine.

感谢你们所有人为我指明了正确的方向。最后我让它在更复杂的配置中工作:Kubernetes 通过 Docker 桌面在本地机器上的 Windows 10 下。

My app's config:

我的应用程序的配置:

-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's port:

Pod 的端口:

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

Service's port:

服务的端口:

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