Desarrollo de un complemento para Grafana: una historia de peces gordos

¡Hola a todos! Hace unos meses, lanzamos a producción nuestro nuevo proyecto de código abierto: el complemento Grafana para monitorear Kubernetes, al que llamamos DevOpsProdigy KubeGraf. El código fuente del complemento está disponible en repositorio público en GitHub. Y en este artículo queremos compartir contigo la historia de cómo creamos el complemento, qué herramientas utilizamos y qué dificultades encontramos durante el proceso de desarrollo. ¡Vamos!

Parte 0 - introducción: ¿cómo llegamos a este punto?

La idea de escribir nuestro propio complemento para Grafan se nos ocurrió por casualidad. Nuestra empresa lleva más de 10 años supervisando proyectos web de diversos niveles de complejidad. Durante este tiempo, hemos acumulado una gran cantidad de conocimientos, casos interesantes y experiencia en el uso de varios sistemas de monitoreo. Y en algún momento nos preguntamos: “¿Existe una herramienta mágica para monitorear Kubernetes, de modo que, como dicen, “configúrelo y olvídese”?”. El estándar de la industria para monitorear k8, por supuesto, ha sido durante mucho tiempo el Combinación Prometeo + Grafana. Y como soluciones listas para usar para esta pila, existe un gran conjunto de varios tipos de herramientas: prometheus-operator, un conjunto de paneles de control kubernetes-mixin, grafana-kubernetes-app.

El complemento grafana-kubernetes-app nos pareció la opción más interesante, pero no ha sido compatible durante más de un año y, además, no puede funcionar con nuevas versiones de node-exporter y kube-state-metrics. Y en algún momento decidimos: “¿No deberíamos tomar nuestra propia decisión?”

Qué ideas decidimos implementar en nuestro complemento:

  • visualización del “mapa de aplicaciones”: presentación cómoda de las aplicaciones en el cluster, agrupadas por espacios de nombres, implementaciones...;
  • visualización de conexiones como “implementación - servicio (+puertos)”.
  • visualización de la distribución de aplicaciones del clúster entre los nodos del clúster.
  • recopilación de métricas e información de varias fuentes: Prometheus y servidor api k8s.
  • monitoreo tanto de la parte de infraestructura (uso de tiempo de CPU, memoria, subsistema de disco, red) como de la lógica de la aplicación: pods de estado de salud, número de réplicas disponibles, información sobre cómo pasar las pruebas de vida/preparación.

Parte 1: ¿Qué es un “complemento de Grafana”?

Desde un punto de vista técnico, el complemento para Grafana es un controlador angular, que se almacena en el directorio de datos de Grafana (/var/grafana/plugins/ /dist/module.js) y se puede cargar como un módulo SystemJS. También en este directorio debe haber un archivo plugin.json que contenga toda la metainformación sobre su complemento: nombre, versión, tipo de complemento, enlaces al repositorio/sitio/licencia, dependencias, etc.

Desarrollo de un complemento para Grafana: una historia de peces gordos
módulo.ts

Desarrollo de un complemento para Grafana: una historia de peces gordos
complemento.json

Como puede ver en la captura de pantalla, especificamos plugin.type = app. Porque los complementos para Grafana pueden ser de tres tipos:

panel: el tipo de complemento más común: es un panel para visualizar cualquier métrica, que se utiliza para crear varios paneles.
fuente de datos: conector de complemento para alguna fuente de datos (por ejemplo, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).
applicación: un complemento que le permite crear su propia aplicación frontend dentro de Grafana, crear sus propias páginas html y acceder manualmente a la fuente de datos para visualizar varios datos. Además, se pueden utilizar como dependencias complementos de otros tipos (fuente de datos, panel) y varios paneles.

Desarrollo de un complemento para Grafana: una historia de peces gordos
Ejemplo de dependencias de complementos con tipo = aplicación.

Puedes utilizar tanto JavaScript como TypeScript como lenguaje de programación (nosotros lo elegimos). Preparativos para complementos de hola mundo de cualquier tipo que puedas encontrar el enlace: este repositorio contiene una gran cantidad de paquetes de inicio (incluso hay un ejemplo experimental de un complemento en React) con constructores preinstalados y configurados.

Parte 2: preparar el entorno local

Para trabajar en el complemento, naturalmente necesitamos un clúster de Kubernetes con todas las herramientas preinstaladas: prometheus, node-exporter, kube-state-metrics, grafana. El entorno debe configurarse de forma rápida, sencilla y natural, y para garantizar la recarga en caliente, el directorio de datos de Grafana debe montarse directamente desde la máquina del desarrollador.

En nuestra opinión, la forma más conveniente de trabajar localmente con Kubernetes es minikubo. El siguiente paso es instalar la combinación Prometheus + Grafana usando prometheus-operator. EN este articulo El proceso de instalación de prometheus-operator en minikube se describe en detalle. Para habilitar la persistencia, debe configurar el parámetro persistencia: verdadera en el archivo charts/grafana/values.yaml, agregue su propio PV y PVC y especifíquelos en el parámetro persistence.existingClaim

Nuestro script de lanzamiento final de minikube se ve así:

minikube start --kubernetes-version=v1.13.4 --memory=4096 --bootstrapper=kubeadm --extra-config=scheduler.address=0.0.0.0 --extra-config=controller-manager.address=0.0.0.0
minikube mount 
/home/sergeisporyshev/Projects/Grafana:/var/grafana --gid=472 --uid=472 --9p-version=9p2000.L

Parte 3: desarrollo real

modelo de objetos

Al prepararnos para implementar el complemento, decidimos describir todas las entidades básicas de Kubernetes con las que trabajaremos en forma de clases de TypeScript: pod, implementación, daemonset, statefulset, trabajo, cronjob, servicio, nodo, espacio de nombres. Cada una de estas clases hereda de la clase BaseModel común, que describe el constructor, el destructor, los métodos para actualizar y cambiar la visibilidad. Cada una de las clases describe relaciones anidadas con otras entidades, por ejemplo, una lista de pods para una entidad de tipo implementación.

import {Pod} from "./pod";
import {Service} from "./service";
import {BaseModel} from './traits/baseModel';

export class Deployment extends BaseModel{
   pods: Array<Pod>;
   services: Array<Service>;

   constructor(data: any){
       super(data);
       this.pods = [];
       this.services = [];
   }
}

Con la ayuda de captadores y definidores, podemos mostrar o configurar las métricas de entidad que necesitamos en una forma conveniente y legible. Por ejemplo, salida formateada de nodos de CPU asignables:

get cpuAllocatableFormatted(){
   let cpu = this.data.status.allocatable.cpu;
   if(cpu.indexOf('m') > -1){
       cpu = parseInt(cpu)/1000;
   }
   return cpu;
}

Páginas

Inicialmente se describe una lista de todas nuestras páginas de complementos en nuestro pluing.json en la sección de dependencias:

Desarrollo de un complemento para Grafana: una historia de peces gordos

En el bloque de cada página debemos indicar el NOMBRE DE LA PÁGINA (luego se convertirá en un slug mediante el cual se podrá acceder a esta página); el nombre del componente responsable del funcionamiento de esta página (la lista de componentes se exporta a module.ts); indicando el rol de usuario para el cual está disponible el trabajo con esta página y la configuración de navegación para la barra lateral.

En el componente responsable del funcionamiento de la página, debemos configurar templateUrl, pasando allí la ruta al archivo html con marcado. Dentro del controlador, mediante inyección de dependencias, podemos acceder a hasta 2 servicios angulares importantes:

  • backendSrv: un servicio que proporciona interacción con el servidor API de Grafana;
  • datasourceSrv: un servicio que proporciona interacción local con todas las fuentes de datos instaladas en Grafana (por ejemplo, el método .getAll(): devuelve una lista de todas las fuentes de datos instaladas; .get( ): devuelve un objeto de instancia de una fuente de datos específica.

Desarrollo de un complemento para Grafana: una historia de peces gordos

Desarrollo de un complemento para Grafana: una historia de peces gordos

Desarrollo de un complemento para Grafana: una historia de peces gordos

Parte 4: fuente de datos

Desde el punto de vista de Grafana, datasource es exactamente el mismo complemento que todos los demás: tiene su propio punto de entrada module.js, hay un archivo con metainformación plugin.json. Al desarrollar un complemento con type = app, podemos interactuar tanto con fuentes de datos existentes (por ejemplo, prometheus-datasource) como con la nuestra, que podemos almacenar directamente en el directorio del complemento (dist/datasource/*) o instalar como una dependencia. En nuestro caso, la fuente de datos viene con el código del complemento. También es necesario tener una plantilla config.html y un controlador ConfigCtrl, que se usará para la página de configuración de la instancia de la fuente de datos y el controlador de la fuente de datos, que implementa la lógica de su fuente de datos.

En el complemento KubeGraf, desde el punto de vista de la interfaz de usuario, la fuente de datos es una instancia de un clúster de Kubernetes que implementa las siguientes capacidades (el código fuente está disponible enlace):

  • recopilar datos del servidor api k8s (obtener una lista de espacios de nombres, implementaciones...)
  • enviar solicitudes a prometheus-datasource (que se selecciona en la configuración del complemento para cada clúster específico) y formatear las respuestas para usar datos tanto en páginas estáticas como en paneles.
  • actualizar datos en páginas de complementos estáticos (con una frecuencia de actualización establecida).
  • procesar consultas para generar una hoja de plantilla en grafana-dashboards (método metriFindQuery())

Desarrollo de un complemento para Grafana: una historia de peces gordos

Desarrollo de un complemento para Grafana: una historia de peces gordos

Desarrollo de un complemento para Grafana: una historia de peces gordos

  • Prueba de conexión con el cluster k8s final.
testDatasource(){
   let url = '/api/v1/namespaces';
   let _url = this.url;
   if(this.accessViaToken)
       _url += '/__proxy';
   _url += url;
   return this.backendSrv.datasourceRequest({
       url: _url,
       method: "GET",
       headers: {"Content-Type": 'application/json'}
   })
       .then(response => {
           if (response.status === 200) {
               return {status: "success", message: "Data source is OK", title: "Success"};
           }else{
               return {status: "error", message: "Data source is not OK", title: "Error"};
           }
       }, error => {
           return {status: "error", message: "Data source is not OK", title: "Error"};
       })
}

Otro punto interesante, en nuestra opinión, es la implementación de un mecanismo de autenticación y autorización para la fuente de datos. Por lo general, podemos usar el componente integrado de Grafana datasourceHttpSettings para configurar el acceso a la fuente de datos final. Con este componente, podemos configurar el acceso a la fuente de datos http especificando la URL y la configuración básica de autenticación/autorización: contraseña de inicio de sesión o certificado de cliente/clave de cliente. Para implementar la capacidad de configurar el acceso mediante un token de portador (el estándar de facto para k8), tuvimos que hacer algunos ajustes.

Para resolver este problema, puede utilizar el mecanismo integrado de “Rutas de complemento” de Grafana (más detalles en página de documentación oficial). En la configuración de nuestra fuente de datos, podemos declarar un conjunto de reglas de enrutamiento que serán procesadas por el servidor proxy de grafana. Por ejemplo, para cada punto final individual es posible establecer encabezados o URL con la posibilidad de crear plantillas, cuyos datos se pueden tomar de los campos jsonData y SecureJsonData (para almacenar contraseñas o tokens en forma cifrada). En nuestro ejemplo, consultas como /__proxy/api/v1/espacios de nombres será enviado a la URL del formulario
/api/v8/namespaces con el encabezado Autorización: Portador.

Desarrollo de un complemento para Grafana: una historia de peces gordos

Desarrollo de un complemento para Grafana: una historia de peces gordos

Naturalmente, para trabajar con el servidor api k8s necesitamos un usuario con acceso de solo lectura, cuyos manifiestos para crear también puede encontrar en código fuente del complemento.

Parte 5: liberación

Desarrollo de un complemento para Grafana: una historia de peces gordos

Una vez que haya escrito su propio complemento de Grafana, naturalmente querrá hacerlo disponible públicamente. En Grafana esta es una biblioteca de complementos disponibles aquí grafana.com/grafana/plugins

Para que su complemento esté disponible en la tienda oficial, debe realizar un PR en este repositorioagregando contenido como este al archivo repo.json:

Desarrollo de un complemento para Grafana: una historia de peces gordos

donde version es la versión de su complemento, url es un enlace al repositorio y commit es el hash de la confirmación para la cual estará disponible una versión específica del complemento.

Y en la salida verás una imagen maravillosa como:

Desarrollo de un complemento para Grafana: una historia de peces gordos

Los datos correspondientes se obtendrán automáticamente de su archivo Readme.md, Changelog.md y plugin.json con la descripción del complemento.

Parte 6: en lugar de conclusiones

No dejamos de desarrollar nuestro complemento después del lanzamiento. Y ahora estamos trabajando para monitorear correctamente el uso de los recursos de los nodos del clúster, introduciendo nuevas funciones para mejorar la UX y también recopilando una gran cantidad de comentarios recibidos después de instalar el complemento tanto por parte de nuestros clientes como de las personas en GitHub (si deja su problema o solicitud de extracción, estaré muy feliz :)

Esperamos que este artículo le ayude a comprender una herramienta tan maravillosa como Grafana y, tal vez, a escribir su propio complemento.

Gracias!)

Fuente: habr.com

Añadir un comentario