How do I attach VisualVM to a simple Java process running in a Docker container
Solution 1:
At first you should run you application with these JVM params:
-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:
EXPOSE 9010
Also specify port binding with docker run command:
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.
Solution 2:
I followed an other SO response to a similar question and it worked.
I started my Java process inside the container by adding those JVM params:
-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 run
command.
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".
Solution 3:
FWIW, this is how I was able to attach VisualVM to a Java process inside a Docker container running on 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);
}
}
}
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:
$ javac Main.java
$ docker build -t main .
$ docker run -p 9010:9010 -it main
Then attach VisualVM using JMX to localhost:9010
Solution 4:
As answered by Anthony.
I had to use the -Djava.rmi.server.hostname
java option on my Windows machine.
Just be sure not to use the CMD in JSON format in your Dockerfile as this doesn't support shell expansion.
Dockerfile example:
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
Solution 5:
To all of you that still suffer from an error like the below:
In my case it was that i used in my Docker YML different port mappings for the ports:
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