Monitoreo de aplicaciones Java con Metricbeat y Jolokia

Una Java Virtual Machine [JVM] (máquina virtual Java) cuenta con una estructura integral para la gestión y el control operativo. En este artículo veremos qué es JMX (Java Management eXtensions), cómo explorar la información que expone y cómo aprovecharla con Jolokia y Elastic Stack. Si conoces JMX y Jolokia, puedes saltarte la primera parte e ir directamente a aprender sobre las funciones relacionadas de Metricbeat.

JMX y JConsole

JMX es una tecnología que define una arquitectura integral y un conjunto de patrones de diseño para supervisar y gestionar aplicaciones Java. Se basa en beans gestionados, mejor conocidos como Mbeans, clases instanciadas por inyección de dependencias que representan recursos en una JVM. Estas representaciones pueden usarse para gestionar ciertos aspectos de la aplicación o, con más frecuencia, para obtener estadísticas del uso de estos recursos. La esencia de JMX es el servidor Mbean Server, un elemento que actúa como intermediario entre los MBeans, las aplicaciones en la misma JVM y el mundo externo. Toda interacción con los Mbeans se realiza a través de este servidor. En general, solo el código Java puede acceder directamente a la API JMX, pero hay conectores que traducen esta API a protocolos estándar; por ejemplo, Jolokia la traduce al HTTP. Una herramienta útil para trabajar con JMX es JConsole, que se incluye en las distribuciones usuales de Java Runtime. Al abrirla, aparece la lista de procesos Java que se están ejecutando en su máquina. Si no tienes ninguno, al menos verás JConsole en sí.

pantalla de bienvenida de JConsole

Al conectarte a cualquiera de estos procesos, se abre una ventana con pestañas diferentes con información de monitoreo genérico sobre distintos aspectos de los procesos, como la memoria o los hilos (threads). También hay una pestaña con un navegador de Mbeans.

ventana principal de JConsole

En el navegador encontrarás la lista de Mbeans del proceso agrupados por espacios de nombres. Algunos de ellos, como los que están en el espacio de nombres java.lang, pueden encontrarse en cualquier JVM, mientras que otros son específicos de la aplicación. Verás diferentes categorías de atributos, operaciones y notificaciones para cada Mbean. Para la supervisión, nos centraremos en los atributos. Hay algunos Mbeans que, aunque son diferentes, implementan la misma interfaz. Por ejemplo, es común encontrar que distintas aplicaciones, como Tomcat o Kafka, usan Garbage Collectors (recolectores de basura) diferentes según el caso de uso, pero en JMX son objetos del mismo tipo, solo que con nombres diferentes.

navegador de Mbean en JConsole

Es realmente bueno poder descubrir toda esta información, pero al monitorear la infraestructura, por lo general no se dispone de algo como JConsole. Además, puede ser que también necesites agregar información desde diversos procesos que podrían estar en servidores diferentes. Afortunadamente, esto puede gestionarse con Metricbeat y Jolokia.

Obtención de métricas de JMX con Metricbeat y Jolokia

Metricbeat puede recoger información de diferentes servidores y enviarla a Elasticsearch, desde donde puede visualizarse de varias formas con Kibana. Pero, como hemos visto antes, JMX solo puede consumirse con aplicaciones Java, y es allí donde Jolokia entra en el juego. Jolokia es un agente que puede implementarse en las JVM para exponer sus Mbeans a través de un puerto HTTP de tipo REST, lo que hace que esta información esté fácilmente disponible para aplicaciones no Java que se estén ejecutando en el mismo host. Puede implementarse como un agente de JVM normal o como un archivo WAR para entornos Java EE o como agente de OSGi o Mule. Para implementar Jolokia como agente, debe usarse el flag «-javaagent» al ejecutar la aplicación Java. Al usar el comando Java directamente, se puede pasar como argumento, pero algunas aplicaciones pueden tener sus propios scripts de inicio y puede ser que en su documentación se recomienden diversas maneras de hacerlo. Por ejemplo, al usar Kafka con sus propios scripts de inicio, tal vez tengas que usar la variable de entorno «KAFKA_OPTS»:

Para implementar Jolokia como WAR, el agente debe instalarse en el servidor de Java EE.
./bin/kafka-server-start.sh ./config/server.properties

Metricbeat recoge la información periódicamente y envía los eventos con los cuatro valores. Por ejemplo, en Tomcat esto se hace al copiar el archivo WAR en su directorio de aplicaciones web. Se recomienda revisar la documentación de cada aplicación para saber cuál es la mejor manera de ejecutar agentes Java. También se recomienda echar un vistazo a la documentación de Jolokia para ver qué agentes están disponibles y cuáles son sus opciones. En los casos donde no sea posible implementar Jolokia en la misma JVM, Jolokia también cuenta con un modo proxy que puede usarse para consultar JMX desde otra JVM. Una vez que el agente de Jolokia se esté ejecutando en la JVM de una aplicación, es muy sencillo obtener métricas de JMX con Metricbeat usando el conjunto de métricas de JMX del módulo de Jolokia introducido en Metricbeat 5.4. Este módulo debe configurarse con el host y el puerto del agente de Jolokia y un conjunto de asignaciones entre las métricas de JMX y los campos de eventos de Metricbeat. Veámoslo con un ejemplo. Ejemplo:

Supervisión de una aplicación Java con Metricbeat y Jolokia ------------------------------------------------------------------ Supongamos que tenemos a Jolokia escuchando en el host local, puerto 8778, como sería el caso con el ejemplo previo con Kafka. Podemos usar JConsole para buscar los MBeans y atributos que queremos monitorear. Al seleccionar el Mbean, es posible ver su nombre y copiarlo directamente al archivo de configuración.

creación de hilos Mbean en JConsole

Para este ejemplo, vamos a monitorear el número de threads del Mbean java.lang:type=Threading y el uso de memoria dinámica de java.lang:type=Memory. La configuración sería de la manera siguiente:

- 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 recoge la información periódicamente y envía los eventos con los cuatro valores. Habrás notado que en JConsole esos atributos de uso de memoria no son valores simples como los recuentos de hilos, sino objetos que contienen cuatro campos cada uno. Metricbeat se encarga de reestructurar los datos en el evento, para que al final contenga diez campos en el espacio de nombres «jolokia.jvm»:

evento con las métricas de Jolokia obtenidas

Configuraciones más avanzadas

Tal vez quieras tener sus campos en diversos eventos. A partir de Metricbeat 6.3, las asignaciones de JMX también pueden definir la manera en que los campos deben agruparse usando la configuración del evento. Dos eventos con el mismo valor se agruparán. Por ejemplo, la configuración siguiente generaría dos eventos diferentes, uno para los campos de memoria y otro para los campos de hilos:

  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"

Otra nueva función introducida en la versión 6.3 es la compatibilidad con comodines, que nos permite usar una sola asignación para varios MBeans. Esto es útil cuando una aplicación contiene varias instancias del mismo tipo, o cuando de antemano se desconoce el nombre específico. Por ejemplo, Tomcat tiene varios conjuntos de hilos, y podríamos ampliar nuestras asignaciones anteriores con una configuración adicional para obtener también el número de hilos y conexiones por conjunto:

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

Con esta configuración se envía un nuevo evento por cada Mbean correspondiente, que contiene el nombre del Mbean y un campo nuevo: evento con las métricas de Jolokia obtenidas al usar comodines Para completar la compatibilidad con Jolokia, en Metricbeat 6.4 agregaremos el modo proxy gracias a una contribución de la comunidad, y también una implementación de Jolokia Discovery, lo que permitirá supervisar las aplicaciones Java en entornos más dinámicos. Jolokia Discovery es una tecnología basada en multidifusión UDP que permite a los agentes de Jolokia anunciar sus puntos finales junto información adicional sobre el servicio al que están adjuntos. Nuestra implementación está impulsada por la estructura autodiscover que ya usamos en Kubernetes y Docker, y ofrece una configuración dinámica con varias funciones basada en plantillas. Si quisiéramos sacar provecho de Jolokia Discovery para los ejemplos anteriores, podríamos hacer algo como esto (reducido a obtener solo recuento de hilos):

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"

Esta configuración consiste en dos plantillas, una de ellas se aplicará a todas las instancias de Jolokia descubiertas, y la segunda se aplicará solo a las instancias de Tomcat. Note cómo las configuraciones en las plantillas son casi las mismas que antes, pero ahora se usa una variable para el host. Aunque esta función se creó para usarse con el conjunto de métricas de JMX, está abierta a cualquier otro uso creativo con otros módulos, o incluso con filebeat. Si deseas obtener más información, échale un vistazo al módulo de Jolokia y la documentación de autodiscover o háganos cualquier pregunta en Discuss.