Interfaz web para JMX con Hawtio

Escrito por el .
java planeta-codigo
Enlace permanente Comentarios

Una cosa es la funcionalidad que proporciona una aplicación y otra las tareas de administración y mantenimiento relacionas con la aplicación. Estas tareas de administración y mantenimiento no son tareas destinadas a los usuarios sino destinadas a los administradores de la aplicación. Estas tareas pueden ser manuales y ejecutadas a conveniencia siendo muy útil poder ejecutarlas sin necesidad de realizar cambios en el código ni un despliegue de la aplicación. La tecnología JMX de Java define una arquitectura para administrar y monitorizar aplicaciones que se puede utilizar para estas tareas administrativas, Hawtio es una interfaz que permite el acceso y ejecución a JMX mediante un navegador y proporciona una librería para integrase con Spring Boot.

Java

Las computadoras y las aplicaciones realizan sus funciones de forma rápida y determinista ofreciendo su servicio a sus usuarios. El software generalmente se diseña para el caso en el que todo funcione correctamente pero también ha de estar preparado para los casos en los que se produzca un error ya sea simplemente emitiendo una traza o autocorrigiendo el problema con un reintento.

Especialmente con el advenimiento de los microservicios que se basan en la comunicación por red las aplicaciones han de estar preparadas para los errores. Si un proceso en una computadora es muy fiable salvo por fallo del hardware las redes son mucho menos fiables más cuando hay comunicación entre varios servicios dado que la conectividad de red se puede perder o un servicio puede fallar repentinamente por un fallo de software o por un fallo de hardware.

Para reparar los efectos de estos errores o simplemente para ejecutar de forma explícita ciertos procesos administrativos independientemente de si es a raíz de un error o no. Las aplicaciones puden incorporar estas adicionalmente estas funcionalidades administrativas y proporcionar una forma de invocarlas, de forma sencilla, rápida y evitando en la medida posible errores manuales.

La funcionalidad principal y las tareas administrativas

Seguramente la funcionalidad principal que proporciona una aplicación no requiera ninguna acción, mantenimiento ni acciones manuales. La aplicación mientras esté en funcionamiento proporcionará su servicio ya sea una aplicación web que sirva contenido para un navegador web o una API basada en REST que proporcione datos y reciba peticiones de otras aplicaciones.

Otras funcionalidades relacionadas con la aplicación se desean ejecutar por ejemplo para corregir algún error puntual en la aplicación, para obtener algún dato o simplemente para realizar una acción que se desea iniciar de forma manual. Las tareas administrativas pueden ser parte una parte de la funcionalidad que es necesario ejecutar a conveniencia en momentos determinados.

Hay que poder acceder a ellas de alguna forma, idealmente mejor sin desplegar una nueva versión por los riesgos de un despliegue sobre todo si los procesos y aseguramiento de calidad no permiten hacer despliegues con confianza o los despliegues requieren demasiado tiempo o suponen una interrupción del servicio. Que esté la funcionalidad disponible e invocarla en el momento que se necesite hace más sencilla la necesidad.

La tecnología JMX de Java

La tecnología JMX es un estándar de Java que define una arquitectura para administrar y monitorizar aplicaciones y servicios Java. Desde el punto de vista del desarrollador JMX requiere programar unas clases de Java siguiendo las convenciones del estándar denominadas MBean. Las instancias de estas clases son registradas y administradas por el contenedor de JMX donde quedan disponibles para uso.

En el artículo Interfaz de monitorización e instrumentalización con JMX en aplicaciones Java comentaba cómo crear una clase MBean con unos métodos para realizar acciones y otro método para devolver datos. El ejemplo incluía la forma de usar JMX en una aplicación Java y en una aplicación de Spring Boot con el soporte que ofrece el framework para la utilización de JMX en Spring Boot.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package io.github.picodotdev.blogbitix.springboothawtio;

public interface HelloMBean {

    void sayHello();

    int add(int x, int y);

    String getName();
}
HelloMBean.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package io.github.picodotdev.blogbitix.springboothawtio;

import org.springframework.stereotype.Component;

import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedAttribute;

@Component
@ManagedResource(objectName = "io.github.picodotdev.blogbitix:type=Hello")
public class Hello implements HelloMBean {

    @ManagedOperation
    public void sayHello() {
        System.out.println("hello, world");
    }

    @ManagedOperation
    public int add(int x, int y) {
        return x + y;
    }

    @ManagedAttribute
    public String getName() {
        return "Reginald";
    }
}
Hello.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package io.github.picodotdev.blogbitix.springboothawtio;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableMBeanExport;

@SpringBootApplication
@EnableMBeanExport
public class Main {

    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}
Main.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
spring:
  jmx:
    enabled: true

management:
  server:
    port: 8081
  endpoints:
    web:
      exposure:
        include: "hawtio,jolokia"

hawtio:
  authenticationEnabled: false
application.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
plugins {
    id 'application'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation platform('org.springframework.boot:spring-boot-dependencies:2.7.1')
    implementation platform('io.hawt:hawtio-bom:2.15.0')

    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'io.hawt:hawtio-springboot'
}

application {
    mainClass = 'io.github.picodotdev.blogbitix.springboothawtio.Main'
}

tasks.named('test') {
    useJUnitPlatform()
}
build.gradle

La interfaz Hawtio

La tecnología de JMX es una buena opción como punto de entrada en la implementación de esas tareas administrativas. Sin embargo, es necesario una forma de poder invocarlas.

El proyecto Hawtio proporciona una interfaz web en la que poder ver, obtener datos e invocar las operaciones ofrecidas por los MBeans registrados en el contenedor de JMX, los propios de la aplicación y muchos otros que proporciona Java y en el caso de utilizarlo Spring Boot.

Hawtio utiliza el proyecto Jolokia que expone mediante una interfaz REST los MBeans del contenedor JMX, Hawtio utiliza la interfaz REST de Jolokia para invocar las operaciones. Spring Boot proporciona soporte para la implementación de JMX y Hawtio proporciona una dependencia para su integración con Spring Boot que añade en la interfaz de administración un endpoint para acceder a la consola de Hawtio.

Las operaciones administrativas seguramente sean funcionalidades que realicen tareas que no deban ser accesibles para los usuarios y han de estar protegidos. Hawtio permite la integración con Keycloak para la autenticación y permite solicitar credenciales para el acceso a la consola web.

Consola de Hawtio como un actuator en aplicación de Spring Boot

Consola de Hawtio como un actuator en aplicación de Spring Boot

Añadir seguridad a la interfaz de Hawtio con HashiCorp Boundary

Por motivos de seguridad es deseable que la consola web de Hawtio no esté accesible mediante una conexión directa. Bondary es uno de los productos de HashiCorp que proporciona la funcionalidad de un bastión y un proxy de conexión a servicios internos, es más seguro que una VPN ya que a diferencia de una VPN únicamente otorga acceso a los sistemas necesarios y no a toda una red interna completa.

El uso de Boundary requiere una conexión a una base de datos, en el modo de desarrollo inicia una instancia de PostgreSQL mediante Docker. Una vez iniciado hay que autenticarse para obtener un token que otorga las credenciales de autorización de conexión a los diferentes sistemas o targets como los denomina Boundary.

Por defecto Boundary en el modo desarrollo crea una definición del host local y un target al puerto 22 de la máquina local. Dado que Hawtio seguramente esté en otros puertos, en el ejemplo en el puerto 8081 es necesario crear un target y añadir el host para la máquina local que permita la conexión a ese puerto.

Con Boundary iniciado y el target definido hay que establecer el túnel con la conexión a la máquina destino, esto crea un túnel abriendo un puerto en la máquina local que tiene como destino el target que se ha utilizado para la conexión, en este ejemplo a la propia máquina local al puerto 8081.

Consola de Boundary

Consola de Boundary

Creación de un target en Boundary Creación de un target en Boundary Creación de un target en Boundary

Creación de un target en Boundary

Antes de establecer el túnel de la conexión hay que autenticarse en Boudary lo que otorga un token para establecer la conexión, en el siguiente comando el token se guarda en una variable de entorno que comando de Buildary utiliza para la autorización al solicitar la conexión, en la conexión se indica el target y Boundary proporciona el puerto del túnel y el identificativo de la sesión.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
$ boundary authenticate password -auth-method-id=ampw_1234567890 -login-name=admin -password=password
...
Authentication information:
  Account ID:      acctpw_1234567890
  Auth Method ID:  ampw_1234567890
  Expiration Time: Thu, 21 Jul 2022 19:03:46 CEST
  User ID:         u_1234567890
...

at_Gf2PG4GGcQ_s17U6Fz5B2DGh7EBdAuAGaifTLUCV1LSMRHZXL7vme1L6RpcroPW39DSEjAMtJcvEhN6otiod3RSMRKRX7RGUndqACd2jx2i2gHBMr7Rdih8yFF
...
$ export BOUNDARY_TOKEN="at_Gf2PG4GGcQ_s17U6Fz5B2DGh7EBdAuAGaifTLUCV1LSMRHZXL7vme1L6RpcroPW39DSEjAMtJcvEhN6otiod3RSMRKRX7RGUndqACd2jx2i2gHBMr7Rdih8yFF"
$ boundary connect -target-id ttcp_99NRL1z7Fw

Proxy listening information:
  Address:             127.0.0.1
  Connection Limit:    -1
  Expiration:          Fri, 15 Jul 2022 03:08:05 CEST
  Port:                43923
  Protocol:            tcp
  Session ID:          s_jL5oGhBjb2
boundary-connect.sh

Esto proporciona un puerto en la máquina local abierto contra Boundary y este contra la máquina y el puerto destino. En este ejemplo está todo en la máquina local pero a efectos prácticos ahora es posible utilizar el puerto proporcionado por Boundary para hacer la conexión a la consola de Hawtio, en vez del puerto 8081 la conexión a través de Boundary se realiza mediante el puerto 43923.

Conexión a Hawtio a través de Boundary

Conexión a Hawtio a través de Boundary

Además, con Boundary es posible monitorizar las conexiones y sesiones establecidas lo que proporciona medidas adicionales de seguridad y auditoría.

Sesiones establecidas y activas establecidas a través de Boudary

Sesiones establecidas y activas establecidas a través de Boudary
Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew run


Comparte el artículo: