Monitoring de Java par SNMP

Le support natif de SNMP est arrivé avec le JDK 5 d'Oracle, en même temps que les autres outils de gestion de la JVM comme jconsole, jps ou jstat. Cet article a été rédigé (et testé) avec le JRE/JDK 8 d'Oracle, mais il devrait être applicable à toutes les versions depuis la 5, à quelques détails près. En revanche, il ne s'applique pas à OpenJDK qui n'a pas le support de SNMP.

D'autres solutions, plus souples, peuvent être utilisées pour publier des informations depuis Java en SNMP, mais elles ne sont pas traitées dans cet article. Seule la technique de publication native à la JVM d'Oracle est abordée.


Configuration Java

Options de la JVM

La JVM d'Oracle offre la possibilité de publier des informations sur son fonctionnement par SNMP (en version 2c). Le support de SNMP est activé avec des propriétés système qui ressemblent beaucoup à celles pour JMX.

-Dcom.sun.management.snmp.port=8161
-Dcom.sun.management.snmp.interface=0.0.0.0
-Dcom.sun.management.snmp.acl=false
-Dcom.sun.management.snmp.acl.file=snmp.acl

Le fichier ACL contient une portion acl et une portion trap. Dans la portion acl, on définit d'abord les communities qui serviront aux clients pour se connecter. Pour chaque community, on donne un droit d'accès : read-only ou read-write. Enfin, on précise les managers, sous la forme d'une liste de hostnames, d'adresses IP ou de masques IP, qui auront le droit d'accéder.

acl = { 
 { 
   communities = monitor, tomcat
   access = read-only
   managers = localhost
 } 
 { 
   communities = admin
   access = read-write
   managers = localhost
 } 
} 

Ce fichier ne doit être accessible que par l'utilisateur qui exécute le process Java, en lecture et éventuellement en écriture :

chmod 400 snmp.acl

Avec ces options, le process Java ouvre le port 8161, en UDP. Vous pouvez le vérifier avec netstat :

netstat -pln | grep 8161

Attention, tout ce qui a été décrit ici ne marche pas avec OpenJDK. La partie SNMP du JDK d'Oracle n'a pas été portée dans OpenJDK.

Intégration dans Tomcat

Dans Tomcat, on utilise le fichier $CATALINA_HOME/bin/setenv.sh pour renseigner les propriétés système, en les mettant dans la variable d'environnement CATALINA_OPTS.

Ce fichier peut prendre la forme suivante :

CATALINA_OPTS="-Dcom.sun.management.snmp.port=8161 -Dcom.sun.management.snmp.acl.file=$CATALINA_HOME/conf/snmp.acl"


Données accessibles

Informations publiées

Le fichier MIB Java définit de façon statique les informations modifiables et celles qui sont consultables. Les informations qui me semblent les plus intéressantes sont les suivantes :

On peut aussi avoir des informations sur les différentes zones de la heap (jvmMemPoolName.N, jvmMemPoolInitSize.N, jvmMemPoolUsed.N, jvmMemPoolCommitted.N, jvmMemPoolMaxSize.N), sur les threads (jvmThreadInstName.N, jvmThreadInstCpuTimeNs.N, jvmThreadInstLockName.N,...).

Informations modifiables

Pour savoir quelles données sont modifiables, il faut faire une recherche sur "read-write" dans le MIB. On trouve par exemple la verbosité du GC (jvmMemoryGCVerboseLevel) ou le flag de monitoring de la CPU (jvmThreadCpuTimeMonitoring).

Comment tester ?

Pour interroger la JVM et lui demander ses informations SNMP, j'ai utilisé les outils Net-SNMP en ligne de commande sur Ubuntu. Pour qu'ils fonctionnent correctement, il faut les installer et ajouter les fichiers de définitions MIB qui ne sont pas installés par défaut.

sudo apt-get install snmp snmp-mibs-downloader
sudo wget --directory-prefix=/usr/share/snmp/mibs/ http://docs.oracle.com/javase/8/docs/jre/api/management/JVM-MANAGEMENT-MIB.mib

Tester avec snmpwalk

On peut maintenant interroger notre process pour avoir toutes les informations :

snmpwalk -v2c -c tomcat localhost:8161 .

Les deux options passées ici sont obligatoires : -v pour la version de SNMP (2c, ici) et -c pour la community.

Les informations ne sont pas très lisibles car cette commande utilise des identifiants numériques (OID) pour chaque objet, comme par exemple iso.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.21.2. Pour que ce soit plus lisible et que snmpwalk affiche des identifiants textuels (comme jvmMemGCCount.3) il faut indiquer le MIB à snmpwalk :

snmpwalk -v2c -m JVM-MANAGEMENT-MIB -c tomcat localhost:8161 .

Pour interroger une ressource précise, on remplace le '.' par un identifiant d'objet, sous forme numérique ou textuelle.

snmpwalk -v2c -m JVM-MANAGEMENT-MIB -c tomcat localhost:8161 jvmThreadDaemonCount

ou

snmpwalk -v2c -c tomcat localhost:8161 JVM-MANAGEMENT-MIB::jvmThreadDaemonCount

Cette dernière commande donne un résultat qui doit ressemble à ça :

JVM-MANAGEMENT-MIB::jvmThreadDaemonCount.0 = Gauge32: 12

On retrouve dans cette réponse l'OID, préfixé par le MIB, avec le type de la valeur (Gauge32) et la valeur elle-même. On peut affiner cette sortie avec l'option -O pour éliminer l'identifiant (-Ov), le type (-OQ) ou l'unité (-OU). La commande suivante affiche uniquement la valeur :

snmpwalk -v2c -OvUQ -c tomcat localhost:8161 JVM-MANAGEMENT-MIB::jvmThreadDaemonCount

Tester avec snmpget

snmpget a une syntaxe et un fonctionnement très proches de snmpwalk. La différence entre les deux réside dans la capacité à obtenir une liste d'objets. Par exemple les arguments passés à la JVM ont tous un OID qui commence par .1.3.6.1.4.1.42.2.145.3.163.1.1.4.20.1.2. Avec snmpwalk, en interrogeant ce nœud, on les obtient tous.

snmpwalk -v2c -m JVM-MANAGEMENT-MIB -c tomcat localhost:8161 .1.3.6.1.4.1.42.2.145.3.163.1.1.4.20.1.2

En revanche, on ne peut pas utiliser snmpget pour ce nœud. Il ne fonctionne que sur les objets de terminaison.

snmpget -v2c -m JVM-MANAGEMENT-MIB -c tomcat localhost:8161 .1.3.6.1.4.1.42.2.145.3.163.1.1.4.20.1.2.1
snmpget -v2c -m JVM-MANAGEMENT-MIB -c tomcat localhost:8161 .1.3.6.1.4.1.42.2.145.3.163.1.1.4.20.1.2.2
snmpget -v2c -m JVM-MANAGEMENT-MIB -c tomcat localhost:8161 .1.3.6.1.4.1.42.2.145.3.163.1.1.4.20.1.2.3
...

Tester avec snmpset

Pour que la commande snmpset fonctionne, il faut l'utiliser avec une community qui a des droits read-write (admin dans l'exemple d'ACL ci-dessus). La syntaxe de snmpset ressemble à celle de snmpget, à la différence prêt qu'après l'OID, on précise le type de valeur et la valeur qu'on veut enregistrer.

Par exemple, pour activer les traces des garbage collectors :

snmpset -v2c -m JVM-MANAGEMENT-MIB -c admin localhost:8161 jvmMemoryGCVerboseLevel.0 i 2