Cómo incrustar dashboards de Kibana

Una petición frecuente de los ingenieros de frontend como yo es incrustar paneles existentes de fuentes como Kibana® en una aplicación web JavaScript. Es una tarea que tuve que realizar en varias ocasiones porque queríamos desplegar vistas generadas por los usuarios rápidamente o permitir que los usuarios controlaran una vista dada. A juzgar por las preguntas habituales que recibimos de la maravillosa comunidad de desarrolladores, no estoy sola.

Las herramientas de visualización de datos, como los paneles de Kibana, permiten incluso al usuario con menos conocimientos técnicos o de diseño crear vistas rápida y fácilmente sobre datos de Elasticsearch® y vistas prototipo. De hecho, significa que la integración de un dashboard en una aplicación web existente es la parte más difícil, especialmente si queremos integrar controles web personalizados para guiar la visualización de los datos y ofrecer un estilo y una experiencia coherentes a los usuarios.

Aquí explicaré con ejemplos de código cómo integrar dashboards de Kibana en una app web mediante iframes HTML. También abordaré la autenticación de Kibana para estas vistas y cómo conectar controles personalizados a las vistas integradas mediante JavaScript.

¿Qué es un iframe?

Ambos ejemplos que se presentan en este artículo emplean un iframe para integrar nuestro dashboard. Un iframe, representado por la etiqueta HTML<iframe>, permite insertar otro sitio web en el documento actual. En concreto, incluiremos el panel de control global de vuelos cargado desde el conjunto de datos de vuelos de muestra en nuestro propio despliegue de Elastic® dentro de nuestra página.

Al incrustar otras fuentes en tu aplicación, es importante cerciorarte de que esta sea una fuente de datos confiable a la que los usuarios deberían tener acceso. Debemos hacer uso de políticas de seguridad de contenido adecuadas, aplicar restricciones con el atributo sandbox y los permisos para limitar las acciones del contenido incrustado. Al no especificar el sandbox en nuestro iframe, incluimos todas las restricciones por defecto.

El rendimiento también es algo que hay que tener en cuenta al incluir contenido de terceros en tu aplicación. Como los iframes pueden consumir más ancho de banda que otros recursos, usar muchos de ellos en una sola aplicación puede ralentizar toda la aplicación. Para quienes buscan incrustar varios dashboards de Kibana en tu aplicación, trata de limitar el número incluido tanto como sea posible y realiza pruebas de rendimiento de la aplicación. Si bien es fácil agregar componentes y dashboards, como desarrolladores debemos asegurarnos de proporcionar los datos que los usuarios necesitan en lugar de todos los controles vistosos que desean. Así que, al elegir entre dashboards y visualizaciones, trabaja con los consumidores para identificar lo que realmente necesitan.

Incrustación básica con iframe HTML

El código para incluir el dashboard Global Flight en tu aplicación web, como se explica en este ejemplo básico, se genera fácilmente desde Kibana mediante la opción Compartir:

diagrama de código para insertar de Kibana

Se genera un fragmento de iframe que agrega las opciones relevantes que has seleccionado, junto con los filtros actuales, para que puedas pegarlo en tu HTML:

<iframe src="https://my-deployment:9243/app/dashboards#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(refreshInterval%3A(pause%3A!t%2Cvalue%3A0)%2Ctime%3A(from%3Anow-1y%2Fd%2Cto%3Anow))&show-top-menu=true&show-query-input=true&show-time-filter=true" height="600" width="800"></iframe>

El fragmento generado está haciendo uso de medidas de píxeles para el ancho y la altura del iframe. El dimensionamiento ha sido comúnmente un desafío para garantizar que el tamaño del iframe refleje el contenido que hay dentro. La mejor práctica es considerar ajustar el tamaño del iframe en relación con el punto de vista usando los atributos de tamaño de la vista vw y vh, o consultas de medios para manejar múltiples tamaños de dispositivos diferentes como parte del diseño receptivo moderno.

Dada la cantidad de configuraciones disponibles, puede resultar confuso averiguar qué es lo que se necesita. Las opciones permiten configurar el estado del dashboard y los controles visibles dentro del iframe.

El tipo de URL a generar puede ser una de dos opciones distintas:

  1. Snapshot: una URL que codifica el estado actual completo del dashboard, lo que significa que los cambios en el dashboard no están presentes en la versión incrustada. 
  2. Objeto almacenado: emplea una URL que haga referencia al ID del objeto almacenado del dashboard, lo que significa que cualquier cambio realizado en el dashboard después de que se genere la URL será visible para los usuarios de la aplicación JavaScript.

La experiencia del autor es que estos dashboards están sujetos a cambios. Por lo tanto, la opción objeto guardado sería la opción más adecuada para incrustar para garantizar que los cambios en el dashboard realizados después de que se generó la URL sean visibles.

La configuración incluir indica los controles adicionales que se incluirán en la parte superior del dashboard integrado:

Elementos del dashboard de Kibana
  1. Menú superior: configuración que contiene las funciones del dashboard como edición y pantalla completa, controladas al incluir show-top-menu=true en la URL de Kibana. 
  2. Consulta: la barra de consulta KQL te permite filtrar los datos visibles en el dashboard, representados por el show-query-input=true parámetro URL. 
  3. Filtro de tiempo: el selector de fechas para seleccionar el rango de datos en el dashboard, habilitado usando show-time-filter=true dentro de la URL. 
  4. Barra de filtro: oculta la configuración para agregar filtrado de los datos, lo que requiere establecer el parámetro de URL hide-filter-bar en true.

Sin usar la URL pública, se pedirá iniciar sesión para acceder al dashboard. En este punto, la experiencia no es fluida, pero el dashboard es accesible para quienes tienen credenciales de inicio de sesión.

Dashboard integrado sin autenticación anónima

Inicio de sesión automático

Para garantizar que el dashboard se muestre automáticamente, es necesario integrar la autenticación con el dashboard en Kibana para eliminar la necesidad de que los usuarios introduzcan sus credenciales tanto para la aplicación JavaScript como para el dashboard. Esto proporciona una experiencia fluida. Esto se puede hacer de dos maneras:

  1. Habilitar autenticación anónima para dar un conjunto predeterminado de credenciales y derechos a cualquier solicitud entrante en la que no se pueda extraer ningún token de autenticación (disponible en el nivel gratuito).

  2. Agregar soporte para el proveedor de SAML de inicio de sesión único, o SSO, para redirigir a los usuarios no autenticados al portal de inicio de sesión único y pasar los usuarios autenticados directamente al dashboard. Esta es una característica con licencia.

Aquí cubriremos la opción anónima. En primer lugar, necesitamos agregar un proveedor de autenticación anónima anonymous1 a nuestro kibana.yml:

xpack.security.authc.providers:
  anonymous.anonymous1:
    order: 0
    credentials:
      username: "my_anonymous_user"
      password: "password"

La URL del iframe también debe regenerarse para especificar el parámetro auth_provider_hint para vincular las credenciales configuradas para el proveedor anonymous1 al contenido incrustado:

<iframe src="https://my-deployment-9f9945.kb.eu-west-2.aws.cloud.es.io:9243/app/dashboards?auth_provider_hint=anonymous1#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(refreshInterval%3A(pause%3A!f%2Cvalue%3A120000)%2Ctime%3A(from%3Anow-1y%2Cto%3Anow))&show-time-filter=true" height="600" width="800"></iframe>

No incluir el parámetroauth_provider_hint=anonymous1 hará que no se pueda continuar al dashboard como invitado. De manera similar, sin un rol de usuario correspondiente registrado en Kibana con el nombre de usuario y la contraseña correctos, se generarán errores de autenticación:

Credenciales de autenticación integrada inválidas

Para corregir esto, por favor cerciórate de tener un usuario registrado con la contraseña correcta que coincida con la configuración del proveedor en tu kibana.yml. Se recomienda restringir los privilegios de esta cuenta al mínimo requerido, dado que el acceso se concederá a usuarios no autenticados.

Kibana crear usuario de autenticación

Llegado este punto, puede que pienses que ya estás listo. Sin embargo, cuando vayas a conectarte a tu dashboard, verás que ocurren algunos eventos extraños de actualización repetidos:

Bloque de política de contenido del dashboard integrado

Este problema se debe a que el navegador bloquea el dashboard de Kibana. Los navegadores web modernos aplican la política de mismo origen para restringir el contenido incrustado. Dos URL comparten el mismo origen si tienen el mismo protocolo, puerto y host. En términos sencillos, cualquier contenido que provenga de un origen diferente se bloqueará de forma predeterminada a menos que lo permita la política de contenido.

Para permitir que el navegador transmita cookies de sesión al servidor Kibana en su pila ELK con las funciones de seguridad habilitadas, que es la configuración predeterminada a partir de Elastic v8.x, debe configurar la opción sameSiteCookies en kibana.yml:

xpack.security.sameSiteCookies: "None"

Con este paso final, podemos ver nuestro dashboard de Kibana integrado en nuestra aplicación JavaScript:

dashboard básico de Kibana integrado

Uso de controles personalizados

Quizá notaste que este dashboard emplea controles para filtrar los datos. Es importante permitir que los usuarios investiguen los datos y reduzcan su selección para encontrar información interesante.

En ciertas situaciones, usar los controles en el dashboard puede no ser la decisión correcta. Quizá quieras usar tus propios controles personalizados para cohesión de diseño dentro de una aplicación existente. Alternativamente, puedes colocar el dashboard junto a otras fuentes de datos y visualizaciones que quieras filtrar para formar una experiencia cohesiva.

En este ejemplo avanzado, mostramos cómo pasar la configuración del intervalo de fechas de un selector de fechas y una selección desplegable al dashboard para forzar una actualización en el dashboard:

Panel avanzado de kibana embebido

Usar controles personalizados significa que necesitamos entender la composición de la URL del dashboard. Vamos a ver el siguiente ejemplo:

https://elastic-deployment-9f9945.kb.eu-west-2.aws.cloud.es.io:9243/app/dashboards?auth_provider_hint=anonymous1#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(filters:!(),refreshInterval:(pause:!f,value:0),time:(from:'${selectedStartDate}',to:'${selectedEndDate}'))&_a=(query:(language:kuery,query:'${carrierQuery}'))&hide-time-filter=true

Además de los parámetros analizados en el ejemplo básico, necesitamos manipular los filtros. Como se comentó anteriormente en la comunidad, en Kibana existen dos niveles de filtros:

  1. El estado global, denotado por el parámetro _g, denota el estado que se mueve entre aplicaciones individuales de Kibana. Un ejemplo clave de esto son los filtros fijados, incluyendo la fecha de inicio y fin seleccionada.

  2. Estado limitado a aplicaciones individuales como el dashboard actual. Esto se representa mediante el parámetro _a de URL.

Para pasar el rango de fechas de cualquier selector de fechas, el iframe de la URL debe actualizarse con la fecha de inicio y fin seleccionadas cuando se aplica un nuevo rango de fechas al control. Inicialmente, establecimos estos valores en un rango relativo del año pasado. Usando easepick como ejemplo, las nuevas fechas se capturan en el evento de selección registrado en la configuración y se convierten al formato de fecha ISO requerido antes de que el atributo src del iframe se actualice con la nueva URL.

let selectedStartDate = 'now-1y';
let selectedEndDate = 'now';

const picker = new easepick.create({
    element: '#datepicker',
    css: [
        'https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css'
    ],
    zIndex: 10,
    firstDay: 0,
    autoApply: false,
    format: 'MMM DD, YYYY @ HH:MM:00',
    plugins: [
        'RangePlugin',
        'TimePlugin'
    ],
    setup(picker) {
        picker.on('select', (e) => {
            const dateFormat = 'YYYY-MM-DDTHH:MM:00.000Z';
            selectedStartDate =  picker.getStartDate().format(dateFormat);
            selectedEndDate =  picker.getEndDate().format(dateFormat);
            
            dashboardUri=getDashboardUri();
            iframe.setAttribute('src', dashboardUri);
        });
     }
});

En cuanto a la propia URL, el parámetro global del filtro _g se actualiza con el rango seleccionado, como se ve en el getDashboardUri() método helper:

function getDashboardUri() {
    return `https://my-deployment-9f9945.kb.eu-west-2.aws.cloud.es.io:9243/app/dashboards?auth_provider_hint=anonymous1#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(filters:!(),refreshInterval:(pause:!f,value:0),time:(from:'${selectedStartDate}',to:'${selectedEndDate}'))&hide-time-filter=true`;
}

Para cualquier campo de datos que quieras filtrar en controles como menúes desplegables, necesitamos pasar esos valores usando la opción de consulta en el parámetro _a. Tomando el siguiente select de HTML como ejemplo:

<div class="carrier-select-container">
  <label for="carrier-select">Carrier</label>
  <select name="carrier-select" id="carrier-select" onchange="updateWithCarrier()">
    <option value="ES-Air">ES-Air</option>
    <option value="JetBeats">JetBeats</option>
    <option value="Kibana Airlines">Kibana Airlines</option>
    <option value="Logstash Airways">Logstash Airways</option>
  </select>
</div>

Es posible extraer el valor seleccionado cuando se cambia del método updateWithCarrier()que está conectado al evento onchange. El evento se extrae del control de selección en el controlador de eventos:

function updateWithCarrier() {
    const carrierSelect = document.getElementById('carrier-select');
    selectedCarrier = carrierSelect.value || '';

    dashboardUri=getDashboardUri();
    iframe.setAttribute('src', dashboardUri);
}

Ten en cuenta que todavía estamos usando el getDashboardUri(), que debe actualizarse para generar una consulta KQL que se pase a la URL del dashboard mediante la opción query en el filtro de la aplicación:

function getDashboardUri() {
  const carrierQuery = rison.encode_object({Carrier : encodeURIComponent(selectedCarrier)});
  return `https://my-deployment-9f9945.kb.eu-west-2.aws.cloud.es.io:9243/app/dashboards?auth_provider_hint=anonymous1#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(filters:!(),refreshInterval:(pause:!f,value:0),time:(from:'${selectedStartDate}',to:'${selectedEndDate}'))&_a=(query:(language:kuery,query:'${carrierQuery}'))&hide-time-filter=true`;
}

Kibana emplea la codificación Rison y URI, que debe aplicarse a la consulta antes de la inclusión. Esto se indica en la definición de carrierQuery anterior, donde usamos rison.js junto con el escape del valor seleccionado usando el método habitual encodeURIComponent.

Una vez conectado, verás que el dashboard se actualiza cada vez con la nueva selección. Solo está atento a los errores que sugieran que Rison está mal formado como este error reportado en nuestros foros que puede ser difícil de depurar.

Ten en cuenta que las URL siempre están sujetas a cambios y, por lo tanto, corres el riesgo de que tu funcionalidad se rompa con las nuevas versiones de cualquier herramienta de terceros que elijas incrustar. Asegúrate de verificar los cambios que rompen la compatibilidad en cada versión de Kibana, y prueba de regresión tu aplicación cuidadosamente.

Haciendo más dashboards de Kibana

Aquí nos adentramos en el mundo de los dashboards de Kibana integrados. Vimos un ejemplo sencillo que emplea un único iframe HTML, junto con un ejemplo más complejo que emplea nuestros propios componentes de JavaScript para pasar parámetros al dashboard. Todo el código está disponible en este repositorio de GitHub y se puede adaptar fácilmente para usar la tecnología web, el marco de trabajo JavaScript o TypeScript favoritos.

Comparte cualquier pregunta o problema que encuentres al insertar dashboards en nuestros foros comunitarios. Siempre estamos encantados de ayudar. ¡Feliz uso de dashboards!