Developer Insights

Monitoring Java Applications Running Inside Docker Containers

How to monitor and trouble-shoot Java applications that are not running as Java processes on your local machine

Sean Tong | June 1, 2016

Java developers need to monitor and trouble-shoot Java applications from time to time. It’s usually straightforward to do that if an applications is running as a Java processes on your local machine. But when the Java application runs inside a Docker container on a Docker host, it becomes challenging to monitor them using tools running locally, even when the Docker host is just a virtual machine running on your desktop. In this blog post, I will describe a couple of ways to monitor such Java applications.

Connect VisualVM to an Application through JMX agents

VisualVM is a GUI tool that monitors and profiles a JVM. It comes with JDK and can be start by running “jvisualvm” command. You can also install it as a separate application. The tool connects to local Java processes directly, but in order to connect to a Java application running inside a Docker container, it needs to connect through a JMX port.

To enable remote JMX connection, you need to run your Java application in the Docker container with JVM options like these:

-Dcom.sun.management.jmxremote.rmi.port=9090

-Dcom.sun.management.jmxremote=true

-Dcom.sun.management.jmxremote.port=9090 

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

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

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

-Djava.rmi.server.hostname=192.168.99.100

Where java.rmi.server.host.name specifies the host name or IP address that your JMX client uses to connect to the target Java application.

Make sure to publish container’s port 9090 as the Docker host port 9090 when starting the Docker container:
$ docker run -p 9090:9090 {your_docker_image}

If your Docker host runs behind a firewall, you could use a SSH tunnel to connect to port 9090 of your Docker host. This time, you may need to set your RMI server host name to be localhost:
-Djava.rmi.server.hostname=localhost

Then ssh to Docker host using an SSH tunnel:
$ ssh -L 9090:localhost:9090 {your_docker_host}

This will forward the connection from your local port 9090 to localhostport 9090 on your Docker host server.

As a best practice, these JVM arguments should be passed as an environment variable that could be used by an entry script to start the Java application. For example:
$ docker run -p 9090:9090 -e MY_JAVA_OPTS=”…”  {your_docker_image}

Once your application is running with the the right options, you can connect your VisualVM to the application with the following steps:

1. Run VisualVM
$ jvisualvm

2. Add a remote host

Sean 1

3. Add a JMX connection
2 sean

3. Click on your JMS connection to connect to your application
sean 3

Running Java Mission Control and Java Flight Recorder

Java Mission Control (JMC) is a monitoring and performance tool offered by Oracle as a commercial feature of JDK 7 and 8. A key feature of JMC is Java Flight Recorder (JFR) that can be used to record event history for performance diagnosis and tuning. JMC is free to use for development.

To enable Java Mission Control, specify the following JVM options when starting your Java application in the Docker container, in addition to the same JMX-related options described above:

-XX:+UnlockCommercialFeatures

-XX:+FlightRecorder

Here are the steps to connect your JMC GUI client to a Java application that that has JMC/JFR feature enabled:

1. Run the Java Mission Control GUI that comes with Java JDK 7 or 8:
$ jmc

2. Set up a remote connection
sean 4 sean 5 sean 6

 

4. Click on 192.168.99.100:9090, then “MBean Server”, to monitor the application, or click on “192.168.99.100:9090” to start a flight recording session.
sean 7

Setting up New Relic

New Relic is a hosted solution for application monitoring. To enable New Relic for your Java application, you need to have a New Relic account and install New Relic Java agent with your application. Here are the key steps to set it up. For more details, see the New Relic Documentation.

1. Download New Relic Agent ZIP file from /newrelic/java-agent/newrelic-api/current/.

2. Unzip the downloaded file. The unzipped file folder should contain files newrelic.jar and newrelic.yml

3. Edit newrelic.ymlfile to configure your license key and application name

license_key: ‘{your_newrelic_license_key}’

app_name: ‘{your_app_name}’

4. Include the unzipped folder in your Docker image

5. Start your Java application inside the container with the following JVM option:
-javaagent:/path/to/newrelic.jar

6. Log in to your New Relic account and your should see your application shows up in the application list.

Debug Java Application Remotely

To debug your Java application remotely using your favorite IDE, use the following JVM option to start your application assuming the debugging agent is running at port 5005:

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

Make sure your publish port 5005 to your Docker host when starting your Docker container.

As an example, if you use IntelliJ as your Java IDE to debug, you can start by opening the project that contains the source code of your Java application.

Then, use “Run/Debug Configuration” Dialog to create a remote run/debug configuration.
sean 8

Now you can run “MyRemoteApp” application configuration to start remote debugging.

Summary

In this blog post, I have described how to configure VisualVM, JMC, New Relic, and Java remote debugging to monitor and profile your Java applications that runs inside Docker containers. I hope you find this information helpful!

  • Luiz Eduardo

    Nice post! I was wondering what could I do in case I have more than one container deployed running a Java application. How could I adapt it to use jmc, for example or visualvm?

    • Thanks, Luiz! You could configure a different JMX port for each container and bind it to the Docker host. For example, container 1 can use port 9090 for its JMX connection and bind it to the host’s port 9090 while container 2 can use port 9091 for its JMX connection and bind it to the host’s port 9091. ~Śean