78

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.

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

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 .

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

Questions:

  • what JVM parameters should be added to CMD command line?
  • what ports should be exposed and published?
  • what network mode should Docker container be using?

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

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 -it --rm --name hwd -p 9010:9010 hello-world-daemon

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

JConsole connects via selecting a Remote process with localhost:9010.

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

nolexa
  • 2,132
  • 1
  • 16
  • 19

6 Answers6

58

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.

eg04lt3r
  • 2,269
  • 12
  • 19
  • 7
    Nope.. VisualVM: `Cannot connect to localhost:9010 using service:jmx:rmi:///jndi/rmi://localhost:9010/jmxrmi`. Jconsole: `Connection failed: error during JRMP connection establishment; nested exception is: java.net.SocketException: Connection reset` – nolexa Jan 31 '16 at 00:59
  • 1
    why do you expose same port twice? – nolexa Jan 31 '16 at 01:03
  • Connect not to localhost, to your network interface. – eg04lt3r Jan 31 '16 at 01:22
  • You expose docker port to port on your network interface. – eg04lt3r Jan 31 '16 at 01:22
  • To which interface? If I connect on `docker0` interface `172.17.0.1`, or on `wlan1` interface `192.168.0.103`, I get *Connection faild: Connection refused* – nolexa Jan 31 '16 at 01:37
  • Understood. So, when run container specify this bind with -p 9010:9010 option and all will be fine. – eg04lt3r Jan 31 '16 at 02:00
  • Here's my command line `docker run -it --rm --name hwd -p 9010:9010 hello-world-daemon` The java process: `root 4398 2673 0 03:09 pts/29 00:00:00 java Main -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` Does not work – nolexa Jan 31 '16 at 02:15
  • Add this option --bind 127.0.0.1 and try to connect to localhost. – eg04lt3r Jan 31 '16 at 02:43
  • I'm sorry, I don't understand. Docker run does not have such option. – nolexa Jan 31 '16 at 03:56
  • 5
    It worked finally. My mistake was that I appended JVM options after the `Main` class name in the command line. All `-D` options were silently ignored by `java`. – nolexa Jan 31 '16 at 14:16
  • Same applies when using `-jar`. For some reason the `-Dcom.sun.management` parameters must be passed before the `-jar` option. Weird in my eyes. – Ethan Leroy Apr 18 '17 at 07:59
  • 1
    @EthanLeroy any arguments after `-jar foo.jar` are sent to the main function of the main class (defined as `Main-Class` in the JAR manifest); basically, arguments before `-jar` are for the JVM, arguments after `-jar` are for the program that's being run – kbolino Feb 28 '18 at 19:20
  • Beware: both `jmxremote.rmi.port` AND `jmxremote.port` need to be set. – johanwannheden Mar 10 '20 at 07:43
14

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

Community
  • 1
  • 1
Anthony O.
  • 16,644
  • 12
  • 92
  • 151
  • What is `$HOST_HOSTNAME` exactly? Is it host of machine running docker or something else? – Albert Bikeev Jun 09 '18 at 17:02
  • 1
    Yes, it is the hostname of the host running docker. It can be the result of the `hostname` command, so you could pass it to docker while launching your container like this: `-e HOST_HOSTNAME=\`hostname\`` – Anthony O. Jun 12 '18 at 09:15
6

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

  • This answer worked perfectly for me, and giving the example Dockerfile made it easy. – Paul Apr 24 '20 at 20:29
3

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
Chris
  • 581
  • 2
  • 10
1

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:

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

Robocide
  • 5,430
  • 4
  • 32
  • 37
1

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.

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:

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

Service's port:

ports:
- name: jmx
  nodePort: 30491
  port: 9010
  protocol: TCP
  targetPort: jmx
Alterant
  • 135
  • 2
  • 9