P E N C I L
Login
Java Jmx with Aws/Ecs

Connect to JMX with RMI protocol

Although it's the easiest way to open a JMX connection, RMI protocol requires to edit security groups to open a ports.

It's good for dev only environment. In case JMX is necessary for your Prod env, JMXMP is a more secured solution, but it requires more setup. Refer to section "Jmxmp protocol" for more details.

In order to enable JMX remote access, all to do is adding Java system properties.

If you just want copy and paste to make it works, jump to the following for sample docker file.

Simple but unsecured configs

First, to make it simple, we start with unsecured configs.

Heads-up ! Everybody who knows your hostname can scan the port and connect to your application to monitor or even calls the methods of your Jmx bean. So, never do this on your prod env.

For Java application running at localhost, JMX is already availble. Just start JVisualVM and connect to your app.

If your app is running inside a docker and it does not even run at local, you need to enable JMX remote connection.

Simply add the following system property when application starts

JAVA_OPTS="-Dcom.sun.management.jmxremote 
-Dcom.sun.management.jmxremote.port=<jmx_port>
-Dcom.sun.management.jmxremote.authenticate=false 
-Dcom.sun.management.jmxremote.ssl=false 
-Djava.net.preferIPv4Stack=true 
-Djava.rmi.server.hostname=<rmi_host> 
-Dcom.sun.management.jmxremote.rmi.port=<rmi_port> 
-Dcom.sun.management.jmxremote.local.only=false

java $JAVA_OPTS -jar app.jar

Start JVisualVm, choose menu File > Add JMX connection, then input in Connection

<docker_ip>:<jmx_port>

<docker_ip> is the IP address of your docker container.

If you run your docker container with network=host, <docker_ip>=localhost

If the docker conatiner run with bridge network, run docker inspect

docker inspect <containerid> 

In the json response, check the field Network>Bridge>IPAddress

<jmx_port> is the port number in system property "com.sun.management.jmxremote.port" (above)

As the destination is not localhost, we set local.only=false

-Dcom.sun.management.jmxremote.local.only=false

For simplicity, we disable the authentications and ssl connection.

-Dcom.sun.management.jmxremote.authenticate=false 
-Dcom.sun.management.jmxremote.ssl=false 

There are still 2 system parameters:

-Djava.rmi.server.hostname=<rmi_host> 
-Dcom.sun.management.jmxremote.rmi.port=<rmi_port> 

First, JVisualVM connect to <docker_ip>:<jmx_port> to receive from the Java app the <rmi_host>:<rmi_port> and then JVisualVM starts to connect to it to get the data.

It's fine to have <rmi_port> to be the same as <jmx_port>. However, it's important that JVisualVM can reach that rmi_host:rmi_port.

That implies that the rmi_host must be a public IP, not a private IP and the docker exposes the port rmi_port.

The parameter preferIpV4Stack is not necessary for docker running at local host. It's only needed for ECS AWS.

-Djava.net.preferIPv4Stack=true 

Sample dockerfile can be found here

JMX to docker on ECS

Now if your docker is running on a EC2 instance, it's important to ensure that

1) <rmi_host> is the public IP of your EC2. From you local machine, you should be able to run telnet <rmi_host>:<rmi_port> and <rmi_host>:<jmx_port>

2) docker export 2 ports <rmi_port> and <jmx_port>

3) Add inbound rules for <rmi_port>, <jmx_port> on your EC2 instance:

Open EC2 > Network & Security > Security Groups

Action > Edit Inbound Rules
Add rules for <jmx_port> and <rmi_port>