¡Hola a todos! Hace unos meses lanzamos a producción nuestro nuevo proyecto open-source: un plugin de Grafana para monitorear Kubernetes, que llamamos . El código fuente del plugin está disponible en . En este artículo queremos compartir con ustedes la historia de cómo creamos el plugin, qué herramientas utilizamos y con qué obstáculos nos encontramos en el proceso de desarrollo. ¡Empecemos!
Parte 0 — Introducción: ¿cómo llegamos hasta aquí?
La idea de crear nuestro propio plugin para Grafana surgió de manera completamente casual. Nuestra empresa lleva más de 10 años monitoreando proyectos web de diferentes niveles de complejidad. Durante este tiempo hemos acumulado una gran cantidad de experiencia, casos interesantes y conocimientos en el uso de diversos sistemas de monitoreo. Y en algún momento nos hicimos la pregunta: "¿Existe alguna herramienta mágica para monitorear Kubernetes que, como se dice, 'la instalas y te olvidas'?". El estándar para monitorear k8s es, por supuesto, la combinación de Prometheus + Grafana. Y para este stack existen muchas soluciones listas: prometheus-operator, un conjunto de dashboards kubernetes-mixin, grafana-kubernetes-app.
La opción más interesante para nosotros fue el plugin grafana-kubernetes-app, pero no ha recibido soporte en más de un año y, además, no es compatible con las nuevas versiones de node-exporter y kube-state-metrics. En algún momento decidimos: "¿Y si hacemos nuestra propia solución?"
Las ideas que decidimos implementar en nuestro plugin son:
- visualización de un 'mapa de aplicaciones': una representación conveniente de las aplicaciones en el clúster, agrupadas por namespaces, deployments…;
- visualización de relaciones tipo 'deployment — service (+ports)'.
- visualización de la distribución de aplicaciones en el clúster según los nodos del clúster.
- recopilación de métricas e información de varias fuentes: Prometheus y el servidor API de k8s.
- monitoreo tanto de la infraestructura (uso de CPU, memoria, subsistemas de disco, red), como de la lógica de las aplicaciones: estado de salud de los pods, número de réplicas disponibles, información sobre las pruebas de liveness/readyness.
Parte 1: ¿Qué es un 'plugin para Grafana'?
Desde un punto de vista técnico, un plugin para Grafana es un controlador angular que se almacena en el directorio de datos de Grafana (/var/grafana/plugins/<your_plugin_name>/dist/module.js) y puede ser cargado como un módulo SystemJS. También en este directorio debe haber un archivo plugin.json que contenga toda la metainformación sobre su plugin: nombre, versión, tipo de plugin, enlaces al repositorio/sitio/licencia, dependencias, etc.

module.ts

plugin.json
Como se puede ver en la captura de pantalla, hemos indicado plugin.type = app. Ya que los plugins para Grafana pueden ser de tres tipos:
panel: el tipo de plugin más común — representa un panel para visualizar métricas, utilizado para construir varios dashboards.
datasource: plugin-conector a alguna fuente de datos (por ejemplo, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).
app: plugin que le permite construir su propia aplicación frontend dentro de Grafana, crear sus propias páginas html y acceder manualmente a la fuente de datos para visualizar diferentes datos. También se pueden utilizar como dependencias otros tipos de plugins (datasource, panel) y varios dashboards.

Ejemplo de dependencias de un plugin con type = app.
Como lenguaje de programación se puede utilizar tanto JavaScript como TypeScript (nuestra elección se detuvo en este último). Las plantillas para plugins hello-world de cualquier tipo pueden : en este repositorio se presenta una gran cantidad de starter-packs (incluso hay un ejemplo experimental de un plugin en React) con ensambladores preinstalados y configurados.
Parte 2: preparación del entorno local
Para trabajar en el plugin, necesitamos, por supuesto, 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 sin complicaciones, y para garantizar el hot-reload, el directorio de datos de Grafana debe montarse directamente desde la máquina del desarrollador.
El método más conveniente, en nuestra opinión, para trabajar localmente con kubernetes es . El siguiente paso es instalar la combinación Prometheus + Grafana utilizando prometheus-operator. En se describe detalladamente el proceso de instalación de prometheus-operator en minikube. Para habilitar la persistencia, es necesario establecer el parámetro persistence: true en el archivo charts/grafana/values.yaml, agregar su propio PV y PVC y especificarlos en el parámetro persistence.existingClaim
El script final para ejecutar 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.LParte 3: desarrollo propiamente dicho
Modelo de objeto
Como preparación para la implementación del complemento, decidimos describir todas las entidades básicas de Kubernetes con las que trabajaremos en forma de clases de TypeScript: pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Cada una de estas clases hereda de una clase base común llamada BaseModel, en la que se describen el constructor, el destructor y métodos para actualizar y alternar la visibilidad. En cada una de las clases se describen las relaciones anidadas con otras entidades, por ejemplo, la lista de pods en la entidad de tipo deployment.
import {Pod} from "./pod";
import {Service} from "./service";
import {BaseModel} from './traits/baseModel';
export class Deployment extends BaseModel{
pods: Array;
services: Array;
constructor(data: any){
super(data);
this.pods = [];
this.services = [];
}
}Con getters y setters, podemos mostrar o establecer las métricas necesarias de las entidades de manera conveniente y legible. Por ejemplo, la salida formateada de los CPU allocatables de un nodo:
get cpuAllocatableFormatted(){
let cpu = this.data.status.allocatable.cpu;
if(cpu.indexOf('m') > -1){
cpu = parseInt(cpu)/1000;
}
return cpu;
}Páginas
La lista de todas las páginas de nuestro complemento se describe inicialmente en nuestro pluing.json en la sección de dependencias:

En el bloque para cada página, debemos especificar el NOMBRE DE LA PÁGINA (que luego se convertirá en slug, a través del cual estará disponible esta página); el nombre del componente que se encarga de la operación de esta página (la lista de componentes se exporta en module.ts); la indicación del rol de usuario que tiene acceso a trabajar con esta página y la configuración de la navegación para la barra lateral.
En el componente responsable de la operación de la página, debemos establecer templateUrl, pasando la ruta al archivo html con la estructura. Dentro del controlador, a través de la inyección de dependencias, podemos acceder a 2 servicios importantes de Angular:
- backendSrv — servicio que proporciona la interacción con el servidor API de Grafana;
- datasourceSrv — servicio que proporciona la interacción local con todas las fuentes de datos instaladas en tu Grafana (por ejemplo, el método .getAll() — devuelve la lista de todas las fuentes de datos instaladas; .get() — devuelve el objeto instancia de una fuente de datos específica.



Parte 4: fuente de datos
Desde la perspectiva de Grafana, un datasource es un plugin como cualquier otro: tiene su punto de entrada module.js y un archivo de metainformación llamado plugin.json. Al desarrollar un plugin de tipo app, podemos interactuar tanto con datasources ya existentes (por ejemplo, prometheus-datasource) como con los nuestros, que podemos almacenar directamente en el directorio del plugin (dist/datasource/*) o instalar como dependencia. En nuestro caso, el datasource se entrega junto con el código del plugin. También es fundamental contar con una plantilla config.html y un controlador ConfigCtrl que se utilizarán para la página de configuración de la instancia del datasource y el controlador Datasource, donde se implementa la lógica de funcionamiento de tu datasource.
En el plugin KubeGraf, desde la perspectiva de la interfaz de usuario, el datasource es una instancia del clúster de Kubernetes, que incluye las siguientes capacidades (el código fuente está disponible ):
- recopilación de datos desde el api-server de k8s (obteniendo la lista de namespaces, deployments...)
- proxy de solicitudes al prometheus-datasource (que se selecciona en los ajustes del plugin para cada clúster específico) y formateo de respuestas para utilizar los datos tanto en páginas estáticas como en dashboards.
- actualización de datos en páginas estáticas del plugin (con un tiempo de refresh rate establecido).
- procesamiento de solicitudes para formar una lista de plantillas en grafana-dashboards (método .metriFindQuery())



- prueba de conexión con el clúster 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: "El datasource está OK", title: "Éxito"};
}else{
return {status: "error", message: "El datasource no está OK", title: "Error"};
}
}, error => {
return {status: "error", message: "El datasource no está OK", title: "Error"};
})
}Un aspecto interesante, en nuestra opinión, es la implementación del mecanismo de autenticación y autorización para el datasource. Por lo general, para configurar el acceso al origen de datos final, podemos utilizar el componente integrado de Grafana: datasourceHttpSettings. Con este componente, podemos configurar el acceso a la fuente de datos http, especificando la url y las configuraciones básicas de autenticación/autorización: usuario-contraseña, o client-cert/client-key. Para implementar la posibilidad de configurar el acceso mediante un bearer token (el estándar de facto para k8s), fue necesario hacer algunos ajustes.
Para abordar esta tarea, se puede utilizar el mecanismo integrado de Grafana "Plugin Routes" (más detalles en ). En la configuración de nuestro datasource, podemos declarar un conjunto de reglas de enrutamiento que serán procesadas por el servidor proxy de Grafana. Por ejemplo, para cada endpoint existe la posibilidad de establecer encabezados o url con opción de plantilla, cuyos datos pueden tomarse de los campos jsonData y secureJsonData (para almacenar contraseñas o tokens encriptados). En nuestro ejemplo, las solicitudes de tipo /__proxy/api/v1/namespaces se proxificarán a una url de tipo
/api/v1/namespaces con el encabezado Authorization: Bearer.


Naturalmente, para trabajar con el servidor api de k8s, necesitamos un usuario con permisos de solo lectura; los manifiestos para su creación también pueden encontrarse en .
Parte 5: lanzamiento

Después de escribir tu propio plugin para Grafana, naturalmente querrás publicarlo en acceso abierto. En Grafana hay una biblioteca de plugins disponible en
Para que tu plugin esté disponible en la tienda oficial, debes hacer un PR en , añadiendo en el archivo repo.json el contenido de tipo:

donde version es la versión de tu plugin, url es el enlace al repositorio, y commit es el hash del commit mediante el cual se podrá acceder a esa versión específica del plugin.
Y al final verás una imagen maravillosa de tipo:

Los datos para ella se recopilarán automáticamente de tu Readme.md, Changelog.md y del archivo plugin.json con la descripción del plugin.
Parte 6: en lugar de conclusiones
No hemos detenido el desarrollo de nuestro plugin después del lanzamiento. Actualmente estamos trabajando en un monitoreo correcto del uso de recursos de los nodos del clúster, implementando nuevas funciones para mejorar la experiencia del usuario, y también estamos revisando una gran cantidad de comentarios recibidos tras las instalaciones del plugin tanto de nuestros clientes como de los 'issues' en GitHub (si dejas tu 'issue' o 'pull request', estaré muy feliz 🙂 ).
Esperamos que este artículo te ayude a entender esta maravillosa herramienta que es Grafana y, tal vez, te inspire a escribir tu propio plugin.
¡Gracias!)
Fuente: habr.com
