如何将 VisualVM 附加到 Docker 容器中的简单 Java 进程 运行
How do I attach VisualVM to a simple Java process running in a Docker container
实际上我想要一个适用于 JEE 容器的解决方案,特别是适用于 Glassfish,但在我尝试了多种设置组合但没有成功之后,我将设置简化为最简单的情况。
这是我在 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;
}
}
}
}
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
问题:
- 应该将哪些 JVM 参数添加到
CMD
命令行?
- 应该公开和发布哪些端口?
- Docker 容器应该使用什么网络模式?
这里我就不展示我失败的尝试了,以免正确答案有偏差。这应该是一个很常见的问题,但我找不到可行的解决方案。
更新。有效的解决方案
这个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.1
的 docker0
接口有效。容器的地址 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 连接:
然后会出现流程:
实际上我想要一个适用于 JEE 容器的解决方案,特别是适用于 Glassfish,但在我尝试了多种设置组合但没有成功之后,我将设置简化为最简单的情况。
这是我在 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;
}
}
}
}
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
问题:
- 应该将哪些 JVM 参数添加到
CMD
命令行? - 应该公开和发布哪些端口?
- Docker 容器应该使用什么网络模式?
这里我就不展示我失败的尝试了,以免正确答案有偏差。这应该是一个很常见的问题,但我找不到可行的解决方案。
更新。有效的解决方案
这个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.1
的 docker0
接口有效。容器的地址 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".
如 -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 连接:
然后会出现流程: