【问题标题】:How do I attach VisualVM to a simple Java process running in a Docker container如何将 VisualVM 附加到在 Docker 容器中运行的简单 Java 进程
【发布时间】:2016-01-31 00:20:36
【问题描述】:

实际上,我想要一个适用于 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;
      }
    }
  }
}

Dockerfile

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

问题:

  • CMD 命令行中应该添加哪些 JVM 参数?
  • 应该公开和发布哪些端口?
  • Docker 容器应该使用什么网络模式?

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

更新。有效的解决方案

这个 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

结合 docker run 命令

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

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

JConsole 通过选择带有localhost:9010远程进程 进行连接。

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

【问题讨论】:

    标签: java docker jmx


    【解决方案1】:

    首先,您应该使用这些 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 run 命令指定端口绑定:

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

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

    【讨论】:

    • 不.. VisualVM:Cannot connect to localhost:9010 using service:jmx:rmi:///jndi/rmi://localhost:9010/jmxrmi。控制台:Connection failed: error during JRMP connection establishment; nested exception is: java.net.SocketException: Connection reset
    • 为什么同一个端口要暴露两次?
    • 不要连接到本地主机,连接到您的网络接口。
    • 终于成功了。我的错误是我在命令行中的 Main 类名之后附加了 JVM 选项。所有-D 选项都被java 默默忽略。
    • @EthanLeroy -jar foo.jar 之后的任何参数都发送到主类的主函数(在 JAR 清单中定义为 Main-Class);基本上,-jar 之前的参数用于 JVM,-jar 之后的参数用于正在运行的程序
    【解决方案2】:

    我关注了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 &lt;port&gt; 指定给 docker run 命令。

    然后,我可以通过添加远程 JMX 连接(“文件”>“添加 JMX 连接...”)并在“连接”中指定 &lt;dockerhostname&gt;:&lt;port&gt;,从本地 JVisualVm 访问此远程 Java 应用程序" 输入,并勾选“不需要 SSL 连接”。

    【讨论】:

    • $HOST_HOSTNAME 到底是什么?它是运行 docker 的机器主机还是其他东西?
    • 是的,就是运行docker的主机的主机名。它可能是 hostname 命令的结果,因此您可以在启动容器时将其传递给 docker:-e HOST_HOSTNAME=`hostname`
    • 谢谢,-Djava.rmi.server.hostname 是我缺少的部分
    【解决方案3】:

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

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

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

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

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

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

    【讨论】:

    • 这个答案对我来说非常有效,并且给出示例 Dockerfile 让它变得简单。
    【解决方案4】:

    正如Anthony 所回答。 我必须在我的 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
    

    【讨论】:

      【解决方案5】:

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

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

      例如:

      15100:9090
      

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

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

      【讨论】:

      • 自 2019 年 12 月以来,您是否找到了解决此问题的方法?还是仍然存在?
      【解决方案6】:

      感谢大家将我引向正确的方向。最后我让它在更复杂的配置中工作: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
      

      【讨论】:

        【解决方案7】:

        您也可以使用 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 连接:

        然后会出现流程:

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-02-05
          • 2020-06-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-09-14
          • 1970-01-01
          相关资源
          最近更新 更多