web-dev-qa-db-fra.com

Comment accéder à l'interface JMX dans Docker de l'extérieur?

J'essaie de surveiller à distance une JVM fonctionnant dans Docker. La configuration ressemble à ceci:

  • machine 1: exécute une machine virtuelle Java (dans mon cas, exécutant kafka) dans docker sur une machine ubuntu; l'IP de cette machine est 10.0.1.201; l'application s'exécutant dans docker est à 172.17.0.85.

  • machine 2: exécute la surveillance JMX

Notez que lorsque j'exécute la surveillance JMX à partir de la machine 2, il échoue avec une version de l'erreur suivante (remarque: la même erreur se produit lorsque j'exécute jconsole, jvisualvm, jmxtrans et node-jmx/npm: jmx):

La trace de la pile en cas d'échec ressemble à ceci pour chacun des outils de surveillance JMX:

Java.rmi.ConnectException: Connection refused to Host: 172.17.0.85; nested exception is
    Java.net.ConnectException: Operation timed out
    at Sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.Java:619)
    (followed by a large stack trace)

Maintenant, la partie intéressante est que lorsque j'exécute les mêmes outils (jconsole, jvisualvm, jmxtrans et node-jmx/npm: jmx) sur la même machine qui exécute docker (machine 1 à partir du haut), la surveillance JMX fonctionne correctement.

Je pense que cela suggère que mon port JMX est actif et fonctionne correctement, mais que lorsque j'exécute la surveillance JMX à distance (à partir de la machine 2), il semble que l'outil JMX ne reconnaisse pas l'IP docker interne (172.17.0.85)

Voici les éléments de configuration réseau pertinents (je pense) sur la machine 1 où la surveillance JMX fonctionne (notez l'IP docker, 172.17.42.1):

docker0   Link encap:Ethernet  HWaddr ...
      inet addr:172.17.42.1  Bcast:0.0.0.0  Mask:255.255.0.0
      inet6 addr:... Scope:Link
      UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
      RX packets:6787941 errors:0 dropped:0 overruns:0 frame:0
      TX packets:4875190 errors:0 dropped:0 overruns:0 carrier:0
      collisions:0 txqueuelen:0
      RX bytes:1907319636 (1.9 GB)  TX bytes:639691630 (639.6 MB)

wlan0     Link encap:Ethernet  HWaddr ... 
      inet addr:10.0.1.201  Bcast:10.0.1.255  Mask:255.255.255.0
      inet6 addr:... Scope:Link
      UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
      RX packets:4054252 errors:0 dropped:66 overruns:0 frame:0
      TX packets:2447230 errors:0 dropped:0 overruns:0 carrier:0
      collisions:0 txqueuelen:1000
      RX bytes:2421399498 (2.4 GB)  TX bytes:1672522315 (1.6 GB)

Et ce sont les éléments de configuration réseau pertinents sur la machine distante (machine 2) à partir de laquelle je reçois les erreurs JMX:

lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    options=3<RXCSUM,TXCSUM>
    inet6 ::1 prefixlen 128 
    inet 127.0.0.1 netmask 0xff000000 
    inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 
    nd6 options=1<PERFORMNUD>

en1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    ether .... 
    inet6 ....%en1 prefixlen 64 scopeid 0x5 
    inet 10.0.1.203 netmask 0xffffff00 broadcast 10.0.1.255
    nd6 options=1<PERFORMNUD>
    media: autoselect
    status: active
25
Eric Broda

Pour être complet, la solution suivante a fonctionné. La machine virtuelle Java doit être exécutée avec des paramètres spécifiques définis pour activer la surveillance JMX du docker distant:

-Dcom.Sun.management.jmxremote
-Dcom.Sun.management.jmxremote.authenticate=false
-Dcom.Sun.management.jmxremote.ssl=false
-Dcom.Sun.management.jmxremote.port=<PORT>
-Dcom.Sun.management.jmxremote.rmi.port=<PORT>
-Djava.rmi.server.hostname=<IP>

where:

<IP> is the IP address of the Host that where you executed 'docker run'
<PORT> is the port that must be published from docker where the JVM's JMX port is configured (docker run --publish 7203:7203, for example where PORT is 7203). Both `port` and `rmi.port` can be the same. 

Une fois cela fait, vous devriez pouvoir exécuter la surveillance JMX (jmxtrans, node-jmx, jconsole, etc.) à partir d'une machine locale ou distante.

Merci à @ Chris-Heald pour en avoir fait une solution vraiment rapide et simple!

34
Eric Broda

Pour l'environnement de développement, vous pouvez définir Java.rmi.server.hostname à adresse IP catch-all 0.0.0.0

Exemple:

 -Djava.rmi.server.hostname=0.0.0.0 \
                -Dcom.Sun.management.jmxremote \
                -Dcom.Sun.management.jmxremote.port=${JMX_PORT} \
                -Dcom.Sun.management.jmxremote.rmi.port=${JMX_PORT} \
                -Dcom.Sun.management.jmxremote.local.only=false \
                -Dcom.Sun.management.jmxremote.authenticate=false \
                -Dcom.Sun.management.jmxremote.ssl=false
7
Serge

J'ai trouvé qu'il était difficile de configurer JMX sur RMI, en particulier à cause du -Djava.rmi.server.hostname=<IP> que vous devez spécifier au démarrage. Nous exécutons nos images de docker dans Kubernetes où tout est dynamique.

J'ai fini par utiliser JMXMP au lieu de RMI, car cela n'a besoin que d'un seul TCP ouvert et pas de nom d'hôte).

Mon projet actuel utilise Spring, qui peut être configuré en ajoutant ceci:

<bean id="serverConnector"
    class="org.springframework.jmx.support.ConnectorServerFactoryBean"/>

(En dehors de Spring, vous devez configurer votre propre JMXConncetorServer pour que cela fonctionne)

Parallèlement à cette dépendance (puisque JMXMP est une extension facultative et ne fait pas partie du JDK):

<dependency>
    <groupId>org.glassfish.main.external</groupId>
    <artifactId>jmxremote_optional-repackaged</artifactId>
    <version>4.1.1</version>
</dependency>

Et vous devez ajouter le même pot que votre chemin de classe lors du démarrage de JVisualVM afin de vous connecter via JMXMP:

jvisualvm -cp "$Java_HOME/lib/tools.jar:<your_path>/jmxremote_optional-repackaged-4.1.1.jar"

Connectez-vous ensuite avec la chaîne de connexion suivante:

service:jmx:jmxmp://<url:port>

(Le port par défaut est 9875)

5
Morten Berg

Après avoir beaucoup fouillé, j'ai trouvé cette configuration

-Dcom.Sun.management.jmxremote.ssl=false 
-Dcom.Sun.management.jmxremote.authenticate=false 
-Dcom.Sun.management.jmxremote.port=1098
-Dcom.Sun.management.jmxremote.rmi.port=1098
-Djava.rmi.server.hostname=localhost
-Dcom.Sun.management.jmxremote.local.only=false

La différence avec l'autre ci-dessus est que Java.rmi.server.hostname est défini sur localhost au lieu de 0.0.0.0

0
snovelli