Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari

Salutare tuturor! Acum câteva luni, am lansat noul nostru proiect open-source în producție - pluginul Grafana pentru monitorizarea kubernetes, pe care l-am numit DevOpsProdigy KubeGraf. Codul sursă al pluginului este disponibil la depozit public pe GitHub. Și în acest articol dorim să vă împărtășim povestea modului în care am creat pluginul, ce instrumente am folosit și ce capcane am întâlnit în timpul procesului de dezvoltare. Să mergem!

Partea 0 - introducere: cum am ajuns în acest punct?

Ideea de a scrie propriul nostru plugin pentru Grafan ne-a venit din întâmplare. Compania noastră monitorizează proiecte web de diferite niveluri de complexitate de mai bine de 10 ani. În acest timp, am acumulat o cantitate mare de expertiză, cazuri interesante și experiență în utilizarea diferitelor sisteme de monitorizare. Și la un moment dat ne-am întrebat: „Există un instrument magic pentru monitorizarea Kubernetes, astfel încât, așa cum se spune, „setează-l și uită-l”?”.. Standardul industriei pentru monitorizarea k8-urilor, desigur, a fost mult timp Combinația Prometheus + Grafana. Și ca soluții gata făcute pentru această stivă, există un set mare de diverse tipuri de instrumente: prometheus-operator, un set de tablouri de bord kubernetes-mixin, grafana-kubernetes-app.

Pluginul grafana-kubernetes-app părea a fi cea mai interesantă opțiune pentru noi, dar nu a fost suportat de mai mult de un an și, în plus, nu poate funcționa cu versiuni noi de node-exporter și kube-state-metrics. Și la un moment dat ne-am hotărât: „Nu ar trebui să luăm propria noastră decizie?”

Ce idei am decis să implementăm în pluginul nostru:

  • vizualizarea „hărții aplicațiilor”: prezentarea comodă a aplicațiilor din cluster, grupate pe spații de nume, implementări...;
  • vizualizarea conexiunilor de tipul „deployment - service (+ports)”.
  • vizualizarea distribuției aplicațiilor cluster între nodurile clusterului.
  • colectare de metrici și informații din mai multe surse: Prometheus și serverul api k8s.
  • monitorizarea atât a părții de infrastructură (utilizarea timpului CPU, a memoriei, a subsistemului de disc, a rețelei) cât și a logicii aplicației - pod-uri de stare de sănătate, numărul de replici disponibile, informații despre trecerea testelor de viață/pregătire.

Partea 1: Ce este un „plugin Grafana”?

Din punct de vedere tehnic, pluginul pentru Grafana este un controler unghiular, care este stocat în directorul de date Grafana (/var/grafana/plugins/ /dist/module.js) și poate fi încărcat ca modul SystemJS. De asemenea, în acest director ar trebui să existe un fișier plugin.json care să conțină toate meta-informațiile despre pluginul tău: nume, versiune, tip de plugin, link-uri către depozit/site/licență, dependențe și așa mai departe.

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari
modul.ts

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari
plugin.json

După cum puteți vedea în captură de ecran, am specificat plugin.type = app. Deoarece pluginurile pentru Grafana pot fi de trei tipuri:

panou: cel mai comun tip de plugin - este un panou pentru vizualizarea oricăror metrici, folosit pentru a construi diverse tablouri de bord.
sursă de date: conector de plugin la o sursă de date (de exemplu, sursă de date Prometheus, sursă de date ClickHouse, sursă de date ElasticSearch).
aplicaţia: Un plugin care vă permite să vă construiți propria aplicație frontend în interiorul Grafana, să vă creați propriile pagini html și să accesați manual sursa de date pentru a vizualiza diverse date. De asemenea, pluginuri de alte tipuri (sursă de date, panou) și diverse tablouri de bord pot fi folosite ca dependențe.

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari
Exemple de dependențe de plugin cu type=app.

Puteți folosi atât JavaScript, cât și TypeScript ca limbaj de programare (noi l-am ales). Pregătiri pentru pluginuri hello-world de orice tip puteți găsi prin link: acest depozit conține un număr mare de pachete de pornire (există chiar un exemplu experimental de plugin în React) cu constructori preinstalați și configurați.

Partea 2: pregătirea mediului local

Pentru a lucra la plugin, avem nevoie de un cluster Kubernetes cu toate instrumentele preinstalate: prometheus, node-exporter, kube-state-metrics, grafana. Mediul trebuie configurat rapid, ușor și natural, iar pentru a asigura reîncărcarea la cald, directorul de date Grafana ar trebui să fie montat direct de pe computerul dezvoltatorului.

Cel mai convenabil mod, în opinia noastră, de a lucra local cu kubernetes este minikube. Următorul pas este să instalați combinația Prometheus + Grafana folosind prometheus-operator. ÎN Acest articol Procesul de instalare a prometheus-operator pe minikube este descris în detaliu. Pentru a activa persistența, trebuie să setați parametrul persistență: adevărat în fișierul charts/grafana/values.yaml, adăugați propriile PV și PVC și specificați-le în parametrul persistence.existingClaim

Scriptul nostru final de lansare minikube arată astfel:

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

Partea 3: dezvoltarea efectivă

Model obiect

În pregătirea pentru implementarea pluginului, am decis să descriem toate entitățile Kubernetes de bază cu care vom lucra sub formă de clase TypeScript: pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Fiecare dintre aceste clase moștenește din clasa comună BaseModel, care descrie constructorul, destructorul, metodele de actualizare și comutare a vizibilității. Fiecare dintre clase descrie relații imbricate cu alte entități, de exemplu, o listă de poduri pentru o entitate de tip implementare.

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

Cu ajutorul getters și setters, putem afișa sau seta metrica entității de care avem nevoie într-o formă convenabilă și lizibilă. De exemplu, ieșirea formatată a nodurilor CPU alocabile:

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

pagini

O listă a tuturor paginilor noastre de plugin este descrisă inițial în pluing.json, în secțiunea de dependențe:

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari

In blocul pentru fiecare pagina trebuie sa indicam NUMELE PAGINII (va fi apoi convertit intr-un slug prin care aceasta pagina va fi accesibila); numele componentei responsabile de funcționarea acestei pagini (lista componentelor este exportată în module.ts); indicând rolul de utilizator pentru care este disponibil lucrul cu această pagină și setările de navigare pentru bara laterală.

În componenta responsabilă cu funcționarea paginii, trebuie să setăm templateUrl, trecând acolo calea către fișierul html cu marcaj. În interiorul controlerului, prin injecție de dependență, putem accesa până la 2 servicii unghiulare importante:

  • backendSrv - un serviciu care oferă interacțiune cu serverul Grafana API;
  • datasourceSrv - un serviciu care oferă interacțiune locală cu toate sursele de date instalate în Grafana (de exemplu, metoda .getAll() - returnează o listă cu toate sursele de date instalate; .get() ) - returnează un obiect de instanță al unei anumite surse de date.

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari

Partea 4: sursa de date

Din punctul de vedere al lui Grafana, datasource este exact același plugin ca toate celelalte: are propriul punct de intrare module.js, există un fișier cu metainformații plugin.json. Când dezvoltăm un plugin cu tip = app, putem interacționa atât cu sursele de date existente (de exemplu, prometheus-datasource) cât și cu propriile noastre, pe care le putem stoca direct în directorul de plugin (dist/datasource/*) sau le putem instala ca dependență. În cazul nostru, sursa de date vine cu codul pluginului. De asemenea, este necesar să aveți un șablon config.html și un controler ConfigCtrl, care va fi folosit pentru pagina de configurare a instanței sursei de date și controlerul sursei de date, care implementează logica sursei dvs. de date.

În plugin-ul KubeGraf, din punctul de vedere al interfeței cu utilizatorul, sursa de date este o instanță a unui cluster kubernetes care implementează următoarele capabilități (codul sursă este disponibil по ссылке):

  • colectarea datelor de pe serverul api k8s (obținerea unei liste de spații de nume, implementări...)
  • trimiterea solicitărilor către prometheus-datasource (care este selectată în setările pluginului pentru fiecare cluster specific) și formatarea răspunsurilor pentru a utiliza datele atât în ​​paginile statice, cât și în tablourile de bord.
  • actualizarea datelor pe paginile de plugin statice (cu o rată de reîmprospătare stabilită).
  • procesarea interogărilor pentru a genera o foaie șablon în tablourile de bord grafana (metoda metriFindQuery())

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari

  • test de conectare cu clusterul 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 punct interesant separat, în opinia noastră, este implementarea unui mecanism de autentificare și autorizare pentru sursa de date. De obicei, din cutie, putem folosi componenta încorporată Grafana datasourceHttpSettings pentru a configura accesul la sursa finală de date. Folosind această componentă, putem configura accesul la sursa de date http specificând adresa URL și setările de bază de autentificare/autorizare: login-parola sau client-cert/client-key. Pentru a implementa capacitatea de a configura accesul folosind un simbol purtător (standardul de facto pentru k8s), a trebuit să facem câteva modificări.

Pentru a rezolva această problemă, puteți utiliza mecanismul încorporat Grafana „Plugin Routes” (mai multe detalii la pagina de documentație oficială). În setările sursei noastre de date, putem declara un set de reguli de rutare care vor fi procesate de serverul proxy grafana. De exemplu, pentru fiecare endpoint individual este posibilă setarea antetelor sau adreselor URL cu posibilitatea de șabloane, date pentru care pot fi preluate din câmpurile jsonData și secureJsonData (pentru stocarea parolelor sau a token-urilor în formă criptată). În exemplul nostru, interogări precum /__proxy/api/v1/namespaces va fi proxy către adresa URL a formularului
/api/v8/namespaces cu antetul Autorizare: purtător.

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari

Desigur, pentru a lucra cu serverul api k8s avem nevoie de un utilizator cu acces numai în citire, manifeste pentru creare pe care le găsiți și în codul sursă al pluginului.

Partea 5: eliberare

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari

Odată ce ați scris propriul plugin Grafana, veți dori în mod natural să îl faceți disponibil public. În Grafana, aceasta este o bibliotecă de pluginuri disponibile aici grafana.com/grafana/plugins

Pentru ca pluginul dvs. să fie disponibil în magazinul oficial, trebuie să faceți un PR acest depozitprin adăugarea de conținut ca acesta în fișierul repo.json:

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari

unde versiunea este versiunea pluginului dvs., url este un link către depozit, iar commit este hash-ul commit-ului pentru care va fi disponibilă o anumită versiune a pluginului.

Și la ieșire veți vedea o imagine minunată precum:

Dezvoltarea unui plugin pentru Grafana: o istorie a loviturilor mari

Datele pentru acesta vor fi preluate automat din fișierul dvs. Readme.md, Changelog.md și plugin.json cu descrierea pluginului.

Partea 6: în loc de concluzii

Nu ne-am oprit să dezvoltăm pluginul nostru după lansare. Și acum lucrăm la monitorizarea corectă a utilizării resurselor nodurilor cluster, introducerea de noi funcții pentru a îmbunătăți UX și, de asemenea, obținerea unei cantități mari de feedback primit după instalarea pluginului atât de către clienții noștri, cât și de la oamenii de pe GitHub (dacă părăsiți problema dvs. sau cererea de tragere, voi fi foarte fericit :)

Sperăm că acest articol vă va ajuta să înțelegeți un instrument atât de minunat precum Grafana și, poate, să vă scrieți propriul plugin.

Спасибо!)

Sursa: www.habr.com

Cumpărați găzduire de încredere pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără găzduire web fiabilă cu protecție DDoS, servere VPS VDS | ProHoster