Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros

Ola a todos! Hai uns meses, lanzamos en produción o noso novo proxecto de código aberto: o complemento Grafana para supervisar os kubernetes, que chamamos DevOpsProdigy KubeGraf. O código fonte do complemento está dispoñible en repositorio público en GitHub. E neste artigo queremos compartir con vós a historia de como creamos o complemento, que ferramentas usamos e que trampas atopamos durante o proceso de desenvolvemento. Imos!

Parte 0 - introdución: como chegamos a este punto?

A idea de escribir o noso propio complemento para Grafan xurdiunos por casualidade. A nosa empresa leva máis de 10 anos supervisando proxectos web de varios niveis de complexidade. Durante este tempo, acumulamos unha gran cantidade de experiencia, casos interesantes e experiencia no uso de varios sistemas de vixilancia. E nalgún momento preguntámonos: "Existe unha ferramenta máxica para supervisar Kubernetes, para que, como din, "configúrao e esquéceo"? Combinación Prometheus + Grafana. E como solucións preparadas para esta pila, hai un gran conxunto de varios tipos de ferramentas: prometheus-operator, un conxunto de paneis de control kubernetes-mixin, grafana-kubernetes-app.

O complemento grafana-kubernetes-app pareceu ser a opción máis interesante para nós, pero hai máis dun ano que non se admite e, ademais, non pode funcionar con novas versións de node-exporter e kube-state-metrics. E nalgún momento decidimos: "Non deberíamos tomar a nosa propia decisión?"

Que ideas decidimos implementar no noso complemento:

  • visualización do “mapa de aplicacións”: presentación cómoda das aplicacións no clúster, agrupadas por espazos de nomes, despregamentos...;
  • visualización de conexións como "despliegue - servizo (+portos)".
  • visualización da distribución das aplicacións do clúster entre os nodos do clúster.
  • recollida de métricas e información de varias fontes: Prometheus e o servidor api k8s.
  • vixilancia tanto da parte da infraestrutura (uso do tempo da CPU, da memoria, do subsistema do disco, da rede) como da lóxica da aplicación: pods de estado de saúde, número de réplicas dispoñibles, información sobre a superación das probas de vida/preparación.

Parte 1: que é un "complemento Grafana"?

Desde un punto de vista técnico, o complemento para Grafana é un controlador angular, que se almacena no directorio de datos de Grafana (/var/grafana/plugins/ /dist/module.js) e pódese cargar como un módulo SystemJS. Tamén neste directorio debería haber un ficheiro plugin.json que conteña toda a metainformación sobre o teu complemento: nome, versión, tipo de complemento, ligazóns ao repositorio/sitio/licenza, dependencias, etc.

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros
módulo.ts

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros
plugin.json

Como podes ver na captura de pantalla, especificamos plugin.type = app. Porque os complementos para Grafana poden ser de tres tipos:

taboleiro: o tipo de complemento máis común: é un panel para visualizar calquera métrica, usado para construír varios paneis.
fonte de datos: conector de complemento a algunha fonte de datos (por exemplo, fonte de datos Prometheus, fonte de datos ClickHouse, fonte de datos ElasticSearch).
app: Un complemento que che permite crear a túa propia aplicación frontend dentro de Grafana, crear as túas propias páxinas html e acceder manualmente á fonte de datos para visualizar varios datos. Ademais, pódense usar complementos doutro tipo (fonte de datos, panel) e varios paneis de mando como dependencias.

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros
Exemplos de dependencias de complementos con type=app.

Podes usar tanto JavaScript como TypeScript como linguaxe de programación (escollimos nós). Preparativos para os complementos hello-world de calquera tipo que poidas atopar a ligazón: este repositorio contén un gran número de paquetes de inicio (incluso hai un exemplo experimental de complemento en React) con creadores preinstalados e configurados.

Parte 2: preparación do medio local

Para traballar no complemento, naturalmente necesitamos un clúster de kubernetes con todas as ferramentas preinstaladas: prometheus, node-exporter, kube-state-metrics, grafana. O ambiente debe configurarse de forma rápida, sinxela e natural, e para garantir a recarga en quente, o directorio de datos de Grafana debe montarse directamente desde a máquina do programador.

O xeito máis cómodo, na nosa opinión, de traballar localmente con kubernetes é minikube. O seguinte paso é instalar a combinación Prometheus + Grafana usando prometheus-operator. EN Este artigo Descríbese en detalle o proceso de instalación de prometheus-operator en minikube. Para activar a persistencia, debes configurar o parámetro persistencia: verdade no ficheiro charts/grafana/values.yaml, engade o teu propio PV e PVC e especifícaos no parámetro persistence.existingClaim

O noso guión de lanzamento final de minikube é 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: desenvolvemento real

Modelo de obxectos

Como preparación para a implementación do complemento, decidimos describir todas as entidades básicas de Kubernetes coas que traballaremos en forma de clases TypeScript: pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Cada unha destas clases herda da clase BaseModel común, que describe o construtor, o destrutor, os métodos para actualizar e cambiar a visibilidade. Cada unha das clases describe relacións aniñadas con outras entidades, por exemplo, unha lista de pods para unha entidade de tipo despregamento.

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 = [];
   }
}

Coa axuda de getters e setters, podemos mostrar ou configurar as métricas de entidades que necesitamos nunha forma cómoda e lexible. Por exemplo, a saída formatada dos 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áxinas

Unha lista de todas as nosas páxinas de complementos descríbese inicialmente no noso pluing.json na sección de dependencias:

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros

No bloque de cada páxina debemos indicar o NOME DA PÁXINA (logo converterase nun slug polo que se poderá acceder a esta páxina); o nome do compoñente responsable do funcionamento desta páxina (a lista de compoñentes expórtase a module.ts); indicando o rol de usuario para o que está dispoñible o traballo con esta páxina e a configuración de navegación para a barra lateral.

No compoñente responsable do funcionamento da páxina, debemos establecer templateUrl, pasando alí o camiño ao ficheiro html con marcado. Dentro do controlador, mediante a inxección de dependencias, podemos acceder a ata 2 servizos angulares importantes:

  • backendSrv - un servizo que ofrece interacción co servidor da API de Grafana;
  • datasourceSrv: un servizo que proporciona interacción local con todas as fontes de datos instaladas no seu Grafana (por exemplo, o método .getAll()): devolve unha lista de todas as fontes de datos instaladas; .get( ) - devolve un obxecto de instancia dunha fonte de datos específica.

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros

Parte 4: fonte de datos

Desde o punto de vista de Grafana, a fonte de datos é exactamente o mesmo complemento que todos os demais: ten o seu propio punto de entrada module.js, hai un ficheiro con metainformación plugin.json. Ao desenvolver un complemento con type = app, podemos interactuar tanto coas fontes de datos existentes (por exemplo, prometheus-datasource) como coas nosas, que podemos almacenar directamente no directorio de complementos (dist/datasource/*) ou instalar como dependencia. No noso caso, a fonte de datos vén co código do complemento. Tamén é necesario ter un modelo config.html e un controlador ConfigCtrl, que se utilizará para a páxina de configuración da instancia da fonte de datos e o controlador da fonte de datos, que implementa a lóxica da súa fonte de datos.

No complemento KubeGraf, desde o punto de vista da interface de usuario, a fonte de datos é unha instancia dun clúster de kubernetes que implementa as seguintes capacidades (o código fonte está dispoñible по ссылке):

  • recollendo datos do servidor api k8s (obtención dunha lista de espazos de nomes, despregamentos...)
  • enviar solicitudes de proxy a prometheus-datasource (que se selecciona na configuración do complemento para cada clúster específico) e dar formato ás respostas para usar datos tanto en páxinas estáticas como en paneis.
  • actualizar datos en páxinas de complementos estáticas (cunha taxa de actualización establecida).
  • procesando consultas para xerar unha folla de modelo en grafana-dashboards (método metriFindQuery())

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros

  • proba de conexión co clúster final k8s.
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"};
       })
}

Un punto interesante aparte, na nosa opinión, é a implementación dun mecanismo de autenticación e autorización para a fonte de datos. Normalmente, fóra da caixa, podemos usar o compoñente Grafana incorporado datasourceHttpSettings para configurar o acceso á fonte de datos final. Usando este compoñente, podemos configurar o acceso á fonte de datos http especificando o URL e a configuración básica de autenticación/autorización: contrasinal de inicio de sesión ou certificado-cliente/clave-cliente. Para implementar a capacidade de configurar o acceso usando un token de portador (o estándar de facto para k8s), tivemos que facer un pequeno axuste.

Para resolver este problema, pode usar o mecanismo integrado de Grafana "Rotas de complementos" (máis detalles en páxina de documentación oficial). Na configuración da nosa fonte de datos, podemos declarar un conxunto de regras de enrutamento que serán procesadas polo servidor proxy de grafana. Por exemplo, para cada punto final individual é posible establecer cabeceiras ou urls con posibilidade de modelado, cuxos datos poden ser tomados dos campos jsonData e secureJsonData (para almacenar contrasinais ou tokens en forma cifrada). No noso exemplo, consultas como /__proxy/api/v1/namespaces será proxy ao URL do formulario
/api/v8/namespaces coa cabeceira Authorization: Bearer.

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros

Por suposto, para traballar co servidor api k8s necesitamos un usuario con acceso de só lectura, manifestos para crear que tamén podes atopar en código fonte do complemento.

Parte 5: liberación

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros

Unha vez que escribas o teu propio complemento de Grafana, naturalmente quererás facelo dispoñible públicamente. En Grafana esta é unha biblioteca de complementos dispoñible aquí grafana.com/grafana/plugins

Para que o teu complemento estea dispoñible na tenda oficial, debes facer un PR este repositorioengadindo contido coma este ao ficheiro repo.json:

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros

onde versión é a versión do teu complemento, url é unha ligazón ao repositorio e commit é o hash do commit para o que estará dispoñible unha versión específica do complemento.

E na saída verás unha imaxe marabillosa como:

Desenvolvemento dun complemento para Grafana: unha historia de grandes tiros

Os datos para iso capturaranse automaticamente do teu Readme.md, Changelog.md e do ficheiro plugin.json coa descrición do complemento.

Parte 6: en vez de conclusións

Non deixamos de desenvolver o noso complemento despois do lanzamento. E agora estamos a traballar para supervisar correctamente o uso dos recursos dos nodos do clúster, introducir novas funcións para mellorar a UX e tamén recoller unha gran cantidade de comentarios recibidos despois de instalar o complemento tanto polos nosos clientes como por parte da xente de GitHub (se deixas o teu problema ou solicitude de extracción, estarei moi feliz :)

Agardamos que este artigo che axude a comprender unha ferramenta tan marabillosa como Grafana e, quizais, a escribir o teu propio complemento.

Grazas!)

Fonte: www.habr.com

Engadir un comentario