Surveillance des applications Java avec Metricbeat et Jolokia

La machine virtuelle Java (JVM) offre une architecture complète pour la surveillance et la gestion des opérations. Dans cet article, vous découvrirez ce qu'est JMX (Java Management eXtension), comment explorer les informations exposées par cette fonctionnalité et comment en profiter pleinement avec Jolokia et la Suite Elastic. Si vous connaissez déjà JMX et Jolokia, vous pouvez ignorer la première partie de cet article et passer directement à la rubrique suivante pour en savoir plus sur les fonctionnalités Metricbeat associées.

JMX et JConsole

JMX est une technologie qui définit une architecture complète et un ensemble de modèles de conception pour surveiller et gérer les applications Java. Elle s'appuie sur les beans gérés, aussi appelés MBeans. Il s'agit de classes instanciées par injection de dépendance qui représentent des ressources dans une JVM. Ces représentations peuvent être utilisées pour gérer certains aspects de l'application ou, le plus souvent, pour recueillir des données statistiques sur l'utilisation de ces ressources. Au cœur de JMX se trouve le MBean Server, un élément qui agit comme un intermédiaire entre les MBeans, les applications au sein de la même JVM et le monde extérieur. Chaque interaction avec les MBeans passe par ce serveur. De manière générale, seul le code Java peut accéder directement à l'API JMX, mais il existe des adaptateurs qui traduisent cette API en protocoles standard. Par exemple, Jolokia la traduit en HTTP. JConsole est un outil particulièrement pratique pour JMX. Il inclut les distributions habituelles de Java Runtime. Lorsque vous l'ouvrez, il vous accueille avec une liste des processus Java en cours d'exécution sur votre machine. Si aucun processus n'est exécuté, vous pouvez voir JConsole.

Écran d'accueil JConsole

Lorsque vous vous connectez à l'un de ces processus, une fenêtre s'ouvre. Celle-ci contient plusieurs onglets incluant des informations de monitoring génériques concernant les différents aspects des processus, comme la mémoire ou les threads. Elle contient également un onglet incluant un navigateur de MBeans.

Fenêtre principale de JConsole

Ce navigateur contient la liste des MBeans du processus, regroupés par espaces de noms. Certains d'entre eux, comme ceux sous l'espace de nom java.lang, sont disponibles sur toutes les JVM. D'autres, en revanche, sont spécifiques à l'application. Pour chaque MBean, vous pouvez voir les différentes catégories pour les attributs, les opérations et les notifications. Pour la surveillance, nous nous concentrons sur les attributs. Bien que différents, certains MBeans déploient la même interface. Par exemple, il n'est pas rare de constater que des applications différentes, comme Tomcat ou Kafka, utilisent des récupérateurs de mémoire différents en fonction du cas d'utilisation. Cependant, JMX contient des objets du même type, mais dont les noms sont différents.

Navigateur MBean dans JConsole

Il peut s'avérer utile de pouvoir obtenir ces informations. Cependant, lorsque vous monitorez l'infrastructure, les fonctions telles que JConsole ne sont généralement pas disponibles. En outre, vous pouvez agréger des informations provenant de différents processus situés sur des serveurs différents. Heureusement, Metricbeat et Jolokia permettent de gérer ces opérations.

Collecte d'indicateurs JMX avec Metricbeat et Jolokia

Metricbeat peut recueillir des informations provenant de différents serveurs et les envoyer à Elasticsearch. Ainsi, vous pouvez les visualiser de diverses manières avec Kibana. Cependant, comme nous l'avons déjà vu, JMX ne peut être utilisé qu'avec des applications Java. C'est là qu'intervient Jolokia. Jolokia est un agent qui peut être déployé sur des JVM afin d'exposer leurs MBeans, via un point de terminaison HTTP de type REST. Ainsi, toutes ces informations sont facilement accessibles par les applications autres que Java exécutées sur le même hôte. Il peut être déployé en tant qu'agent de JVM normal, en tant que WAR pour les environnements Java EE ou en tant qu'agents Mule ou OSGI.

Pour déployer Jolokia en tant qu'agent, l'indicateur -javaagent doit être utilisé lors du lancement de l'application Java. Lorsque vous utilisez la commande Java directement, vous pouvez le traiter comme un argument. Cependant, certaines applications disposent de leurs propres scripts de démarrage et leur documentation peut contenir des méthodes alternatives. Par exemple, lorsque vous utilisez Kafka avec ses propres scripts de démarrage, vous devrez peut-être utiliser la variable d'environnement KAFKA_OPTS :

export KAFKA_OPTS=-javaagent:/opt/jolokia-jvm-1.5.0-agent.jar=port=8778,host=localhost
./bin/kafka-server-start.sh ./config/server.properties

Pour déployer Jolokia en tant que WAR, l'agent doit être installé dans le serveur Java EE. Par exemple, dans Tomcat, il vous faut copier le fichier WAR dans son répertoire d'applications Web. Pour chaque application, il est recommandé de consulter la documentation associée afin d'identifier la meilleure méthode d'exécution des agents Java. De même, nous vous conseillons d'étudier la documentation de Jolokia pour connaître les agents disponibles, ainsi que leurs options. Lorsqu'il n'est pas possible de déployer Jolokia sur la même JVM, l'agent propose un mode proxy qui peut être utilisé pour interroger JMX à partir d'une autre JVM.

Lorsque l'agent Jolokia est exécuté dans la JVM d'une application, la procédure de collecte d'indicateurs JMX avec Metricbeat est assez simple, à l'aide de l'ensemble d'indicateurs JMX du module Jolokia mentionné dans la rubrique Metricbeat 5.4. Ce module doit être configuré avec l'hôte et le port de l'agent Jolokia et un ensemble de mappages entre les indicateurs JMX et les champs d'événements Metricbeat. Prenons un exemple concret.

Exemple : surveillance d'une application Java avec Metricbeat et Jolokia

Supposons que Jolokia écoute localhost, port 8778, tel qu'avec l'exemple précédent utilisant Kafka. Il est possible d'utiliser JConsole pour rechercher les MBeans et les attributs que nous souhaitons surveiller. Lorsque vous sélectionnez le MBean, son nom s'affiche. Il est alors possible de le copier directement dans le fichier de configuration.

Thread de MBean dans JConsole

Dans cet exemple, nous allons surveiller le nombre de threads provenant du MBean java.lang:type=Threading, ainsi que l'utilisation de la mémoire du segment de java.lang:type=Memory. La configuration serait semblable à celle-ci :

- module: jolokia
  metricsets: ["jmx"]
  hosts: ["localhost:8778"]
  period: 10s
  namespace: "jvm"
  jmx.mappings:
  - mbean: "java.lang:type=Memory"
    attributes:
    - attr: "HeapMemoryUsage"
        field: "memory.heap"
    - attr: "NonHeapMemoryUsage"
        field: "memory.nonheap"
  - mbean: "java.lang:type=Threading"
    attributes:
    - attr: "ThreadCount"
        field: "thread.count"
    - attr: "DaemonThreadCount"
        field: "thread.daemon"

Metricbeat recueille régulièrement les informations et envoie les événements avec les quatre valeurs. Vous avez peut-être remarqué que les attributs d'utilisation de la mémoire dans JConsole ne correspondent pas à des valeurs simples, comme le nombre de threads. Il s'agit d'objets qui contiennent chacun quatre champs. Metricbeat se charge de restructurer les données de l'événement de manière à ce qu'il contienne dix champs dans l'espace de nom jolokia.jvm :

Événement avec les indicateurs Jolokia recueillis

Configurations avancées

Si vous le souhaitez, vous pouvez intégrer vos champs dans des événements différents. Avec Metricbeat 6.3, les mappages JMX peuvent aussi définir la manière dont les champs doivent être groupés, grâce au paramètre d'événement. Deux événements avec la même valeur seront regroupés ensemble. Par exemple, dans la configuration suivante, deux événements différents seront générés : un pour les champs de mémoire et l'autre pour les champs de threads.

  jmx.mappings:
  - mbean: "java.lang:type=Memory"
    attributes:
    - attr: "HeapMemoryUsage"
        field: "memory.heap"
        event: "memory"
    - attr: "NonHeapMemoryUsage"
        field: "memory.nonheap"
        event: "memory"
  - mbean: "java.lang:type=Threading"
    attributes:
    - attr: "ThreadCount"
      field: "thread.count"
      event: "threads"
    - attr: "DaemonThreadCount"
      field: "thread.daemon"
      event: "threads"

La prise en charge des caractères génériques est l'une des nouvelles fonctionnalités de la version 6.3. Elle permet d'utiliser un seul mappage pour plusieurs MBeans. Cela peut être très utile lorsqu'une application contient plusieurs instances d'un même type ou lorsque le nom spécifique n'est pas connu à l'avance. Par exemple, Tomcat possède plusieurs pools de threads. Nous pouvons étendre les mappages précédents avec une configuration supplémentaire afin d'obtenir le même nombre de threads et de connexions par pool :

  - mbean: "Catalina:name=*,type=ThreadPool"
    attributes:
    - attr: "currentThreadCount"
      field: "thread.count"
    - attr: "maxThreads"
      field: "thread.max"
    - attr: "connectionCount"
      field: "connection.count"

Avec cette configuration, un nouvel événement est envoyé pour chaque MBean correspondant. Celui-ci contient le nom du MBean sous forme de nouveau champ :

Événement avec les indicateurs Jolokia collectés lors de l'utilisation de caractères génériques

Pour parachever la prise en charge de Jolokia, nous allons ajouter un mode proxy dans Metricbeat 6.4, grâce à une contribution de la communauté, ainsi qu'une implémentation de Jolokia Discovery, qui permet de surveiller les applications Java dans des environnements plus dynamiques.

Jolokia Discovery est une technologie basée sur la multidiffusion UDP qui permet aux agents Jolokia d'annoncer leurs points de terminaison ainsi que des informations supplémentaires concernant le service auquel ils sont rattachés. Notre implémentation est alimentée par l'infrastructure de découverte automatique que nous utilisons déjà pour Kubernetes et Docker. Elle offre une configuration dynamique complète basée sur des modèles. Il est possible d'utiliser Jolokia Discovery dans les exemples précédents. Le résultat serait semblable à ce qui suit (réduit afin de recueillir uniquement le nombre de threads) :

metricbeat.autodiscover:
  providers:
  - type: jolokia
    templates:
    - condition:
        contains:
          jolokia.agent.version: "1.5.0"
      config:
      - module: "jolokia"
        metricsets: ["jmx"]
        hosts: ["${data.jolokia.url}"]
        namespace: "jvm"
        jmx.mappings: 
        - mbean: "java.lang:type=Threading"
          attributes:
          - attr: "ThreadCount"
            field: "thread.count"
    - condition:
        contains:
          jolokia.server.product: "tomcat"
      config:
      - module: "jolokia"
        metricsets: ["jmx"]
        hosts: ["${data.jolokia.url}"]
        namespace: "jvm"
        jmx.mappings:
        - mbean: "Catalina:name=*,type=ThreadPool"
          attributes:
          - attr: "currentThreadCount"
            field: "thread.count"

Cette configuration est composée de deux modèles. Le premier sera appliqué à toutes les instances Jolokia découvertes et le second sera uniquement appliqué aux instances Tomcat. Vous remarquerez que les configurations dans les modèles sont presque identiques à celles d'avant, mais qu'elles utilisent une variable pour l'hôte.

Même si cette fonctionnalité a été créée pour être utilisée avec l'ensemble d'indicateurs JMX, elle peut aussi s'appliquer à d'autres usages créatifs, avec d'autres modules ou même Filebeat. Pour en savoir plus, reportez-vous à la documentation du module Jolokia et d'Autodiscovery ou posez-nous vos questions sur le forum de discussion.