Desenvolupament d'un complement per a Grafana: una història de grans trets

Hola a tots! Fa uns mesos, vam llançar el nostre nou projecte de codi obert en producció: el connector Grafana per supervisar els kubernetes, que vam anomenar DevOpsProdigy KubeGraf. El codi font del connector està disponible a repositori públic a GitHub. I en aquest article volem compartir amb vosaltres la història de com hem creat el connector, quines eines hem utilitzat i quines trampes hem trobat durant el procés de desenvolupament. Som-hi!

Part 0 - introducció: com hem arribat a aquest punt?

La idea d'escriure el nostre propi connector per a Grafan ens va sorgir per casualitat. La nostra empresa fa més de 10 anys que monitoritza projectes web de diversos nivells de complexitat. Durant aquest temps, hem acumulat una gran quantitat d'experiència, casos interessants i experiència en l'ús de diversos sistemes de monitorització. I en algun moment ens vam preguntar: "Hi ha una eina màgica per controlar Kubernetes, de manera que, com diuen, "configureu-lo i oblideu-lo"? Combinació Prometeu + Grafana. I com a solucions ja fetes per a aquesta pila, hi ha un gran conjunt d'eines de diversos tipus: prometheus-operator, un conjunt de taulers kubernetes-mixin, grafana-kubernetes-app.

El connector grafana-kubernetes-app semblava ser l'opció més interessant per a nosaltres, però fa més d'un any que no és compatible i, a més, no pot funcionar amb noves versions de node-exporter i kube-state-metrics. I en algun moment vam decidir: "No hauríem de prendre la nostra pròpia decisió?"

Quines idees vam decidir implementar al nostre complement:

  • visualització del “mapa d'aplicacions”: presentació còmoda de les aplicacions al clúster, agrupades per espais de noms, desplegaments...;
  • visualització de connexions com "desplegament - servei (+ports)".
  • visualització de la distribució de les aplicacions del clúster entre els nodes del clúster.
  • recollida de mètriques i informació de diverses fonts: Prometheus i k8s api server.
  • supervisió tant de la part d'infraestructura (ús del temps de la CPU, memòria, subsistema de disc, xarxa) com de la lògica de l'aplicació: pods d'estat de salut, nombre de rèpliques disponibles, informació sobre la superació de proves de vida/preparació.

Part 1: Què és un "connector Grafana"?

Des d'un punt de vista tècnic, el connector per a Grafana és un controlador angular, que s'emmagatzema al directori de dades de Grafana (/var/grafana/plugins/ /dist/module.js) i es pot carregar com a mòdul SystemJS. També en aquest directori hi hauria d'haver un fitxer plugin.json que contingui tota la metainformació sobre el vostre connector: nom, versió, tipus de connector, enllaços al repositori/lloc/llicència, dependències, etc.

Desenvolupament d'un complement per a Grafana: una història de grans trets
mòdul.ts

Desenvolupament d'un complement per a Grafana: una història de grans trets
plugin.json

Com podeu veure a la captura de pantalla, hem especificat plugin.type = app. Com que els connectors per a Grafana poden ser de tres tipus:

panell: el tipus de connector més comú: és un panell per visualitzar qualsevol mètrica, utilitzat per crear diversos taulers de control.
Font de dades: connector del connector a alguna font de dades (per exemple, font de dades Prometheus, font de dades ClickHouse, font de dades ElasticSearch).
aplicació: un connector que us permet crear la vostra pròpia aplicació frontal dins de Grafana, crear les vostres pròpies pàgines html i accedir manualment a la font de dades per visualitzar diverses dades. A més, es poden utilitzar complements d'altres tipus (font de dades, panell) i diversos taulers de control com a dependències.

Desenvolupament d'un complement per a Grafana: una història de grans trets
Exemple de dependències del connector amb type=app.

Podeu utilitzar tant JavaScript com TypeScript com a llenguatge de programació (el vam triar). Preparacions per als connectors hello-world de qualsevol tipus que pugueu troba l'enllaç: aquest dipòsit conté un gran nombre de paquets d'inici (fins i tot hi ha un exemple experimental d'un connector a React) amb constructors preinstal·lats i configurats.

Part 2: Preparació de l'entorn local

Per treballar amb el connector, naturalment necessitem un clúster de kubernetes amb totes les eines preinstal·lades: prometheus, node-exporter, kube-state-metrics, grafana. L'entorn s'ha de configurar de manera ràpida, senzilla i natural, i per garantir la recàrrega en calent, el directori de dades de Grafana s'ha de muntar directament des de la màquina del desenvolupador.

La forma més còmoda, segons la nostra opinió, de treballar localment amb kubernetes és minikube. El següent pas és instal·lar la combinació Prometheus + Grafana mitjançant prometheus-operator. EN Aquest article El procés d'instal·lació de prometheus-operator a minikube es descriu en detall. Per habilitar la persistència, heu d'establir el paràmetre persistència: cert al fitxer charts/grafana/values.yaml, afegiu el vostre propi PV i PVC i especifiqueu-los al paràmetre persistence.existingClaim

El nostre script de llançament final de minikube té aquest aspecte:

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

Part 3: desenvolupament real

Model d'objectes

En preparació per implementar el connector, vam decidir descriure totes les entitats bàsiques de Kubernetes amb les quals treballarem en forma de classes TypeScript: pod, desplegament, daemonset, statefulset, job, cronjob, service, node, namespace. Cadascuna d'aquestes classes hereta de la classe BaseModel comuna, que descriu el constructor, el destructor i els mètodes per actualitzar i canviar la visibilitat. Cadascuna de les classes descriu relacions imbricades amb altres entitats, per exemple, una llista de pods per a una entitat de tipus desplegament.

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

Amb l'ajuda de getters i setters, podem mostrar o configurar les mètriques d'entitat que necessitem d'una forma còmoda i llegible. Per exemple, sortida amb format dels nodes de CPU assignables:

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

pàgines

Una llista de totes les nostres pàgines de connectors es descriu inicialment al nostre pluing.json a la secció de dependències:

Desenvolupament d'un complement per a Grafana: una història de grans trets

En el bloc de cada pàgina hem d'indicar el NOM DE LA PÀGINA (després es convertirà en un slug pel qual es podrà accedir a aquesta pàgina); el nom del component responsable del funcionament d'aquesta pàgina (la llista de components s'exporta a module.ts); indicant el rol d'usuari per al qual està disponible el treball amb aquesta pàgina i la configuració de navegació per a la barra lateral.

En el component responsable del funcionament de la pàgina, hem d'establir templateUrl, passant-hi el camí al fitxer html amb marcatge. Dins del controlador, mitjançant la injecció de dependència, podem accedir a fins a 2 serveis angulars importants:

  • backendSrv: un servei que proporciona interacció amb el servidor de l'API de Grafana;
  • datasourceSrv: un servei que proporciona interacció local amb totes les fonts de dades instal·lades al vostre Grafana (per exemple, el mètode .getAll()): retorna una llista de totes les fonts de dades instal·lades; .get( ) - retorna un objecte d'instància d'una font de dades específica.

Desenvolupament d'un complement per a Grafana: una història de grans trets

Desenvolupament d'un complement per a Grafana: una història de grans trets

Desenvolupament d'un complement per a Grafana: una història de grans trets

Part 4: font de dades

Des del punt de vista de Grafana, la font de dades és exactament el mateix connector que tots els altres: té el seu propi punt d'entrada module.js, hi ha un fitxer amb metainformació plugin.json. Quan desenvolupem un connector amb type = app, podem interactuar tant amb fonts de dades existents (per exemple, prometheus-datasource) com amb les nostres pròpies, que podem emmagatzemar directament al directori de connectors (dist/datasource/*) o instal·lar-les com a dependència. En el nostre cas, la font de dades ve amb el codi del connector. També és necessari tenir una plantilla config.html i un controlador ConfigCtrl, que s'utilitzarà per a la pàgina de configuració de la instància de la font de dades i el controlador de la font de dades, que implementa la lògica de la font de dades.

Al connector KubeGraf, des del punt de vista de la interfície d'usuari, la font de dades és una instància d'un clúster de kubernetes que implementa les següents capacitats (el codi font està disponible по ссылке):

  • recollida de dades del servidor api k8s (aconseguint una llista d'espais de noms, desplegaments...)
  • enviar sol·licituds de proxy a prometheus-datasource (que es selecciona a la configuració del connector per a cada clúster específic) i donar format a les respostes per utilitzar dades tant en pàgines estàtiques com en taulers.
  • actualització de dades a pàgines de connectors estàtiques (amb una freqüència d'actualització establerta).
  • processament de consultes per generar un full de plantilla als taulers de control de grafana (mètode metriFindQuery())

Desenvolupament d'un complement per a Grafana: una història de grans trets

Desenvolupament d'un complement per a Grafana: una història de grans trets

Desenvolupament d'un complement per a Grafana: una història de grans trets

  • prova de connexió amb 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: "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 punt interessant a part, al nostre parer, és la implementació d'un mecanisme d'autenticació i autorització per a la font de dades. Normalment, fora de la caixa, podem utilitzar el component Grafana integrat datasourceHttpSettings per configurar l'accés a la font de dades final. Mitjançant aquest component, podem configurar l'accés a la font de dades http especificant l'URL i la configuració bàsica d'autenticació/autorització: contrasenya-inici de sessió o certificat-client/clau-client. Per tal d'implementar la capacitat de configurar l'accés mitjançant un testimoni de portador (l'estàndard de facto per a k8s), hem hagut de fer una mica de retoc.

Per resoldre aquest problema, podeu utilitzar el mecanisme integrat de Grafana "Plugin Routes" (més detalls a pàgina de documentació oficial). A la configuració de la nostra font de dades, podem declarar un conjunt de regles d'encaminament que seran processades pel servidor intermediari de grafana. Per exemple, per a cada punt final individual és possible establir capçaleres o URL amb possibilitat de plantilla, dades per a les quals es poden extreure dels camps jsonData i secureJsonData (per emmagatzemar contrasenyes o testimonis en forma xifrada). En el nostre exemple, consultes com /__proxy/api/v1/namespaces serà proxy a l'URL del formulari
/api/v8/namespaces amb la capçalera Autorització: portador.

Desenvolupament d'un complement per a Grafana: una història de grans trets

Desenvolupament d'un complement per a Grafana: una història de grans trets

Naturalment, per treballar amb el servidor API k8s necessitem un usuari amb accés només de lectura, manifests per crear que també podeu trobar a codi font del connector.

Part 5: alliberament

Desenvolupament d'un complement per a Grafana: una història de grans trets

Un cop hàgiu escrit el vostre propi connector Grafana, naturalment voldreu fer-lo disponible públicament. A Grafana, aquesta és una biblioteca de connectors disponible aquí grafana.com/grafana/plugins

Perquè el vostre connector estigui disponible a la botiga oficial, heu de fer un PR aquest repositoriafegint contingut com aquest al fitxer repo.json:

Desenvolupament d'un complement per a Grafana: una història de grans trets

on version és la versió del vostre connector, l'url és un enllaç al repositori i commit és el hash del commit per al qual estarà disponible una versió específica del connector.

I a la sortida veureu una imatge meravellosa com:

Desenvolupament d'un complement per a Grafana: una història de grans trets

Les dades s'agafaran automàticament del vostre fitxer Readme.md, Changelog.md i el fitxer plugin.json amb la descripció del connector.

Part 6: en lloc de conclusions

No vam deixar de desenvolupar el nostre complement després del llançament. I ara estem treballant per supervisar correctament l'ús dels recursos dels nodes del clúster, introduint noves funcions per millorar l'UX i també per obtenir una gran quantitat de comentaris rebuts després d'instal·lar el connector tant pels nostres clients com per part de la gent de GitHub (si surts). el teu problema o sol·licitud d'extracció, estaré molt content :)

Esperem que aquest article us ajudi a entendre una eina tan meravellosa com Grafana i, potser, a escriure el vostre propi connector.

Gràcies!)

Font: www.habr.com

Compreu allotjament fiable per a llocs amb protecció DDoS, servidors VPS VDS 🔥 Compra allotjament web fiable amb protecció DDoS, servidors VPS VDS | ProHoster