Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra

Përshëndetje të gjithëve! Disa muaj më parë ne lançuam në prodhim projektin tonë të ri open-source — plugin-in Grafana për monitorimin e Kubernetes, të cilin e quajtëm DevOpsProdigy KubeGraf. Kodi burimor i plugin-it është në dispozicion në repo publik në GitHub. Në këtë artikull ne duam të ndajmë me ju historinë e mënyrës se si e krijuam plugin-in, cilat mjete përdorëm dhe me cilat vështirësi u përballëm gjatë zhvillimit. Le të fillojmë!

Pjesa 0 — hyrje: si e arritëm deri këtu?

Ideja për të shkruar plugin-in tonë për Grafanën lindi krejt rastësisht. Kompania jonë merret me monitorimin e projekteve web për më shumë se 10 vjet. Gjatë kësaj kohe ne kemi fituar një bagazh të madh ekspertize, rastesh interesante, dhe përvojash në përdorimin e sistemeve të ndryshme të monitorimit. Ndërsa një moment ne na erdhi në mendje pyetja: "A ekziston ndonjë mjet magjik për monitorimin e Kubernetes-it, që, siç thuhet, 'e vendos dhe harro'"? Standardi industrial për monitorimin e k8s, natyrisht, është bashkimi Prometheus + Grafana. Dhe për zgjidhjet e gatshme për këtë stack ekziston një grup i madh mjetesh të ndryshme: prometheus-operator, grup dashboard-esh kubernetes-mixin, aplikacioni grafana-kubernetes.

Opsioni më interesant për ne dukej të ishte plugin-i grafana-kubernetes-app, por ai nuk është mbështetur për më shumë se një vit dhe, përveç kësaj, nuk punon me versionet e reja të node-exporter dhe kube-state-metrics. Ndërsa një moment vendosëm: "A nuk do ta bënim ne zgjidhjen tonë?"

Cilat ide vendosëm të realizojmë në plugin-in tonë:

  • vizualizimi i 'hartës së aplikacionit': përfaqësim i përshtatshëm i aplikacioneve në kluster, të grupuara sipas namespace-ve, deployment-eve…;
  • vizualizimi i lidhjeve të tipit 'deployment — shërbim (+ports)'.
  • vizualizimi i shpërndarjes së aplikacioneve të klusterit sipas nod-eve të klusterit.
  • mbledhja e metrikave dhe informacionit nga disa burime: Prometheus dhe serveri i API të k8s.
  • monitorimi i pjesës infrastrukturore (përdorimi i kohës së procesorit, memories, sistemit disk, rrjetit), si dhe logjikës së aplikacioneve — statusi i shëndetit të pod-eve, numri i replikave të disponueshme, informacion mbi kalimin e testeve të liveness/readyness.

Pjesa 1: Çfarë është 'plugin për Grafana'?

Nga këndvështrimi teknik, një plugin për Grafana është një kontrollues angular që ruhet në direktorinë data të Grafanës (/var/grafana/plugins/<your_plugin_name>/dist/module.js) dhe mund të ngarkohet si një modul SystemJS. Gjithashtu, në këtë direktor duhet të jetë një skedar plugin.json, që përmban të gjitha metainformacionet për plugin-in tuaj: emri, versioni, tipi i plugin-it, lidhjet me depot/sitë/licencë, varësitë dhe kështu me radhë.

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra
module.ts

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra
plugin.json

Siç shihet në foto, ne kemi caktuar plugin.type = app. Sepse plugin-et për Grafana mund të jenë të tre llojeve:

panel: tipi më i zakonshëm i plugin-eve — përfaqëson një panel për vizualizimin e disa metrikave, përdoret për ndërtimin e disa dashboard-eve të ndryshme.
datasource: plugin-connector në një burim të dhënash (për shembull, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).
app: plugin që ju lejon të ndërtoni aplikacionin tuaj të frontend-it brenda Grafana, të krijoni faqet tuaja html dhe të bëni kërkesa manuale në datasource për vizualizimin e disa të dhënave të ndryshme. Gjithashtu, si varësi mund të përdoren plugin-e të lloje të tjera (datasource, panel) dhe dashboard-e të ndryshme.

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra
Shembulli i varësive të plugin-it me type = app.

Si gjuhë programimi mund të përdoret si JavaScript, ashtu edhe TypeScript (ne e kemi zgjedhur atë). Skellet për plugin-e hello-world të çdo lloji mund të gjënden në lidhje: në këtë depo paraqitet një numër i madh starter-pack’ash (madje ka edhe një shembull eksperimental plugin-i në React) me ndërtues të parainstaluar dhe të konfiguruar.

Pjesa 2: përgatitja e mjedisit lokal

Për të punuar në plugin, natyrisht, na nevojitet një grup kubernetes me të gjitha mjete të parainstaluara: prometheus, node-exporter, kube-state-metrics, grafana. Mjedisi duhet të konfigurohet shpejt, lehtësisht dhe pa probleme, dhe për të siguruar hot-reload, директoria e të dhënave të Grafana duhet të montohet përmes kompjuterit të zhvilluesit.

Më i rehatshmi, sipas mendimit tonë, është minikube. Hapi tjetër është të instalohet lidhja Prometheus + Grafana me anë të prometheus-operator. Në këtë artikull përshkruhet në detaje procesi i instalimit të prometheus-operator në minikube. Për të aktivizuar persistencën, duhet të vendosni parametrin persistence: true në skedarin charts/grafana/values.yaml, shtoni PV dhe PVC tuaj të vetëdijshëm dhe tregoni ato në parametrin persistence.existingClaim

Skema përfundimtare e nisjes së minikube duket kështu:

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

Pjesa 3: zhvillimi i drejtpërdrejtë

Modeli i Objektit

Si përgatitje për implementimin e plugin-it, ne vendosëm të përshkruajmë të gjitha entitetet bazë të Kubernetes me të cilat do të punojmë në formën e klasave TypeScript: pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Secila nga këto klasa trashëgon nga klasa e përbashkët BaseModel, e cila përshkruan konstruktorin, destruktorin dhe metodat për përditësimin dhe ndryshimin e dukshmërisë. Në secilën nga klasat përshkruhen marrëdhëniet e ngulitura me entitete të tjera, për shembull, lista e pod-eve për entitetin e tipit 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 = [];
   }
}

Me anë të getter’ave dhe setter’ave, ne mund të nxjerrim ose vendosim metrikat e nevojshme të entiteteve në një format të përshtatshëm dhe të lexueshëm. Për shembull, output-i i formatizuar i allocatable cpu të nodës:

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

Faqet

Lista e të gjitha faqeve të plugin-it tonë përshkruhet fillimisht në pluing.json në seksionin e varësive:

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra

Në bllokun për secilën faqe, ne duhet të specifikojmë EMRIN E FAQES (ky do të konvertohet më pas në slug, me të cilin do të jetë e accesueshme kjo faqe); emrin e komponentit, i cili është përgjegjës për funksionimin e kësaj faqe (lista e komponentëve eksporton në module.ts); përcaktimin e rolit të përdoruesit, për të cilin është e aksesuar puna me këtë faqe dhe konfigurimet e navigimit për panelin anësor.

Në komponentin që është përgjegjës për funksionimin e faqes, ne duhet të vendosim templateUrl, duke kaluar aty rrugën deri në skedarin html me markup. Brenda kontrolluesit, përmes injeksionit të varësive, ne mund të kemi qasje në 2 shërbime të rëndësishme të angular:

  • backendSrv — shërbimi që siguron ndërveprimin me serverin api të Grafana;
  • datasourceSrv — shërbimi që siguron ndërveprimin lokal me të gjitha datasource-t që janë instaluar në Grafana tuaj (për shembull, metoda .getAll() — kthen një listë të të gjitha datasource-ve të instaluara; .get() — kthen objektin-instancën e një datasource-i të caktuar.

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra

Pjesa 4: datasource

Sipas Grafana, një burim të dhënash është i njëjtë si çdo plugin tjetër: ai ka hyrjen e tij module.js, ka një skedë me metainformata plugin.json. Kur zhvillojmë një plugin me tipin = app, mund të interaktojmë me burime ekzistuese të dhënash (p.sh. prometheus-datasource) si dhe me të tuat, të cilat mund t'i mbajmë direkt në direktorinë e plugin-it (dist/datasource/*) ose t'i installojmë si varësi. Në rastin tonë, burimi i të dhënave vjen së bashku me kodin e plugin-it. Gjithashtu, është e domosdoshme që të kemi një model config.html dhe kontrolluesin ConfigCtrl, të cilat do të përdoren për faqen e konfigurimit të instancës së burimit të të dhënave dhe kontrolluesin e Burimit të të Dhënave, në të cilin implementohet logjika e funksionimit të burimit të dhënave.

Në plugin-in KubeGraf, nga pikëpamja e ndërfaqes së përdoruesit, burimi i të dhënave përfaqëson një instancë të klasterit kubernetes, në të cilin janë implementuar funksionalitetet e mëposhtme (kodin burimor e ka në dispozicion në lidhje):

  • marrja e të dhënave nga api-server i k8s (marrja e listës së namespace-ve, deploymenteve…)
  • proksimi i kërkesave për burimin e të dhënave prometheus (i zgjedhur në konfigurimet e plugin-it për çdo klaster specifik) dhe formatimi i përgjigjeve për përdorimin e të dhënave si në faqet statike ashtu edhe në panelët e kontrollit.
  • përditësimi i të dhënave në faqet statike të plugin-it (me një kohë të caktuar refresh rate).
  • përpunimi i kërkesave për formimin e listës së templates në grafana-dashboards (metoda .metriFindQuery())

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra

  • testimi i lidhjes me klasterin përfundimtar 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: "Burimi i të dhënave është OK", title: "Sukses"};
           }else{
               return {status: "error", message: "Burimi i të dhënave nuk është OK", title: "Gabim"};
           }
       }, error => {
           return {status: "error", message: "Burimi i të dhënave nuk është OK", title: "Gabim"};
       })
}

Një moment interesant, sipas mendimit tonë, është implementimi i mekanizmit të autentifikimit dhe autorizimit për datasource. Zakonisht, nga kutia, për konfigurimin e aksesit në burimin e fundit të të dhënave, mund të përdorim komponentin e integruar të Grafana — datasourceHttpSettings. Me këtë komponent mund të konfigurojmë qasjen në burimin e të dhënave http, duke specifikuar url dhe parametrat bazë të autentifikimit/ autorizimit: emri i përdoruesit - fjalëkalimi, ose client-cert/client-key. Për të realizuar mundësinë e konfigurimit të qasjes me tokenin bearer (standard de facto për k8s), duhej të bëhej pak "kimistrie".

Për zgjidhjen e kësaj çështjeje mund të përdorim mekanizmin e integruar të Grafana "Plugin Routes" (më shumë në faqen zyrtare të dokumentacionit). Në cilësimet e datasource-it tonë mund të shpallim një grup rregullash rruge, të cilat do të përpunohen nga serveri proxy i grafana. Për shembull, për çdo endpoint të veçantë ekziston mundësia e vendosjes së kokave ose url me mundësinë e templating, të dhënat për të cilat mund të merren nga fushat jsonData dhe secureJsonData (për ruajtjen e fjalëkalimeve ose tokeneve në formë të shkruar). Në shembullin tonë, kërkesat si /__proxy/api/v1/namespaces do të proksohen në url si
/api/v1/namespaces me vendosjen e kokës Authorization: Bearer.

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra

Natyrisht, për të punuar me serverin api të k8s, na nevojitet një përdorues me akses vetëm për lexim, manifestet për krijimin e të cilit mund t'i gjeni gjithashtu në kodin burimor të plugin-it.

Pjesa 5: lansohet

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra

Pasi të shkruani plugin-in tuaj për Grafana, natyrisht do të dëshironit ta publikoni atë në mënyrë të hapur. Në Grafana ka një bibliotekë plugins që është e aksesueshme në lidhjen grafana.com/grafana/plugins

Për ta bërë plugin-in tuaj të aksesueshëm në dyqanin zyrtar, duhet të bëni PR në këtë depo, duke shtuar në skedarin repo.json përmbajtjen si kjo:

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra

ku version — është versioni i plugin-it tuaj, url — lidhja për depo, dhe commit — hash i commit-it, me të cilin do të jetë e aksesueshme versioni specifike e plugin-it.

Dhe në fund do të shihni një pamje të mrekullueshme si kjo:

Zhvillimi i një plugin-i për Grafana: historia e mësimeve të marra

Të dhënat për të do të dihen automatikisht nga Readme.md, Changelog.md dhe skedari plugin.json që përshkruan plugin-in.

Pjesa 6: në vend të përfundimeve

Ne e kemi ndaluar zhvillimin e plugin-it tonë pas lançimit. Aktualisht po punojmë për monitorimin e saktë të përdorimit të burimeve të nyjave të klasterit, implementimin e veçorive të reja për të përmirësuar përvojën e përdoruesit, si dhe po shqyrtojmë një sasi të madhe të reagimeve që kemi marrë pas instalimeve të plugin-it nga klientët tanë, si dhe nga çështjet e raportuara në GitHub (nëse lini një çështje ose një kërkesë tërheqjeje, do të isha shumë i lumtur 🙂 ).

Shpresojmë që ky artikull t'ju ndihmojë të kuptoni një mjet kaq të mrekullueshëm si Grafana dhe, ndoshta, të shkruani plugin-in tuaj të vet.

Faleminderit!)

Burimi: habr.com

Blini hostim të besueshëm për faqe interneti me mbrojtje DDoS, serverë VPS VDS 🔥 Blini hostim të besueshëm për faqe interneti me mbrojtje DDoS, serverë VPS VDS - ProHoster