Utvikling av en plugin for Grafana: en historie med store skudd

Hei alle sammen! For noen måneder siden lanserte vi vårt nye åpen kildekode-prosjekt i produksjon - Grafana-pluginen for overvåking av kubernetes, som vi kalte DevOpsProdigy KubeGraf. Kildekoden til plugin-modulen er tilgjengelig på offentlig depot på GitHub. Og i denne artikkelen ønsker vi å dele med deg historien om hvordan vi laget plugin, hvilke verktøy vi brukte og hvilke fallgruver vi møtte under utviklingsprosessen. La oss gå!

Del 0 - innledning: hvordan kom vi til dette punktet?

Ideen om å skrive vår egen plugin for Grafan kom til oss ganske ved en tilfeldighet. Vårt firma har overvåket nettprosjekter av ulike kompleksitetsnivåer i mer enn 10 år. I løpet av denne tiden har vi samlet en stor mengde kompetanse, interessante saker og erfaring med bruk av ulike overvåkingssystemer. Og på et tidspunkt spurte vi oss selv: "Finnes det et magisk verktøy for å overvåke Kubernetes, slik at, som de sier, "sett det og glem det"? Prometheus + Grafana kombinasjon. Og som ferdige løsninger for denne stabelen er det et stort sett med ulike typer verktøy: prometheus-operator, et sett med kubernetes-mixin-dashboard, grafana-kubernetes-app.

Grafana-kubernetes-app-pluginen så ut til å være det mest interessante alternativet for oss, men det har ikke vært støttet på mer enn et år og kan dessuten ikke fungere med nye versjoner av node-eksporter og kube-state-metrics. Og på et tidspunkt bestemte vi oss: "Skal vi ikke ta vår egen avgjørelse?"

Hvilke ideer vi bestemte oss for å implementere i plugin-modulen vår:

  • visualisering av "applikasjonskartet": praktisk presentasjon av applikasjoner i klyngen, gruppert etter navnerom, distribusjoner...;
  • visualisering av tilkoblinger som "distribusjon - tjeneste (+porter)".
  • visualisering av fordelingen av klyngeapplikasjoner på tvers av klyngenoder.
  • innsamling av metrikk og informasjon fra flere kilder: Prometheus og k8s api-server.
  • overvåking av både infrastrukturdelen (bruk av CPU-tid, minne, diskdelsystem, nettverk) og applikasjonslogikk - helsestatuspoder, antall tilgjengelige replikaer, informasjon om bestått liveness/readyness-tester.

Del 1: Hva er en "Grafana-plugin"?

Fra et teknisk synspunkt er plugin for Grafana en vinkelkontroller, som er lagret i Grafana-datakatalogen (/var/grafana/plugins/ /dist/module.js) og kan lastes inn som en SystemJS-modul. Også i denne katalogen bør det være en plugin.json-fil som inneholder all metainformasjon om plugin-en din: navn, versjon, plugin-type, lenker til depotet/nettstedet/lisensen, avhengigheter og så videre.

Utvikling av en plugin for Grafana: en historie med store skudd
modul.ts

Utvikling av en plugin for Grafana: en historie med store skudd
plugin.json

Som du kan se på skjermbildet, spesifiserte vi plugin.type = app. Fordi plugins for Grafana kan være av tre typer:

panel: den vanligste typen plugin - det er et panel for å visualisere alle beregninger, brukt til å bygge forskjellige dashboards.
datakilde: plugin-kobling til en datakilde (for eksempel Prometheus-datakilde, ClickHouse-datakilde, ElasticSearch-datakilde).
app: En plugin som lar deg bygge din egen frontend-applikasjon inne i Grafana, lage dine egne html-sider og manuelt få tilgang til datakilden for å visualisere ulike data. Også plugins av andre typer (datakilde, panel) og ulike dashbord kan brukes som avhengigheter.

Utvikling av en plugin for Grafana: en historie med store skudd
Eksempel på plugin-avhengigheter med type=app.

Du kan bruke både JavaScript og TypeScript som programmeringsspråk (vi valgte det). Forberedelser for hello-world plugins av alle typer du kan finn via lenke: dette depotet inneholder et stort antall startpakker (det er til og med et eksperimentelt eksempel på en plugin i React) med forhåndsinstallerte og konfigurerte byggere.

Del 2: forberede lokalmiljøet

For å jobbe med plugin-en trenger vi naturligvis en kubernetes-klynge med alle forhåndsinstallerte verktøy: prometheus, node-exporter, kube-state-metrics, grafana. Miljøet bør settes opp raskt, enkelt og naturlig, og for å sikre hot-reload, bør Grafana-datakatalogen monteres direkte fra utviklerens maskin.

Den mest praktiske måten, etter vår mening, å jobbe lokalt med kubernetes er minikube. Det neste trinnet er å installere Prometheus + Grafana-kombinasjonen ved å bruke prometheus-operator. I denne artikkelen Prosessen med å installere prometheus-operator på minikube er beskrevet i detalj. For å aktivere utholdenhet må du angi parameteren utholdenhet: sant i charts/grafana/values.yaml-filen, legg til din egen PV og PVC og spesifiser dem i persistence.existingClaim-parameteren

Vårt siste minikube-lanseringsskript ser slik ut:

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

Del 3: faktisk utvikling

Objektmodell

Som forberedelse til implementering av plugin bestemte vi oss for å beskrive alle de grunnleggende Kubernetes-enhetene som vi vil jobbe med i form av TypeScript-klasser: pod, deployment, daemonset, statefulset, jobb, cronjob, service, node, navneområde. Hver av disse klassene arver fra den vanlige BaseModel-klassen, som beskriver konstruktøren, destruktoren, metoder for oppdatering og bytte av synlighet. Hver av klassene beskriver nestede relasjoner med andre enheter, for eksempel en liste over pods for en enhet av typen distribusjon.

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

Ved hjelp av gettere og settere kan vi vise eller angi enhetsberegningene vi trenger i en praktisk og lesbar form. For eksempel formatert utdata fra allokerbare CPU-noder:

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

sider

En liste over alle plugin-sidene våre er først beskrevet i vår pluing.json i avhengighetsdelen:

Utvikling av en plugin for Grafana: en historie med store skudd

I blokken for hver side må vi angi SIDENAVN (det vil da bli konvertert til en slug som denne siden vil være tilgjengelig); navnet på komponenten som er ansvarlig for driften av denne siden (listen over komponenter eksporteres til module.ts); som indikerer brukerrollen som arbeid med denne siden er tilgjengelig for og navigasjonsinnstillinger for sidefeltet.

I komponenten som er ansvarlig for driften av siden, må vi sette templateUrl, og sende stien til html-filen med markup der. Inne i kontrolleren, gjennom avhengighetsinjeksjon, kan vi få tilgang til opptil 2 viktige vinkeltjenester:

  • backendSrv - en tjeneste som gir interaksjon med Grafana API-serveren;
  • datasourceSrv - en tjeneste som gir lokal interaksjon med alle datakilder som er installert i din Grafana (for eksempel .getAll()-metoden - returnerer en liste over alle installerte datakilder; .get( ) - returnerer et forekomstobjekt av en bestemt datakilde.

Utvikling av en plugin for Grafana: en historie med store skudd

Utvikling av en plugin for Grafana: en historie med store skudd

Utvikling av en plugin for Grafana: en historie med store skudd

Del 4: datakilde

Fra Grafanas synspunkt er datakilden nøyaktig samme plugin som alle de andre: den har sitt eget inngangspunkt module.js, det er en fil med metainformasjon plugin.json. Når vi utvikler en plugin med type = app, kan vi samhandle med både eksisterende datakilder (for eksempel prometheus-datasource) og vår egen, som vi kan lagre direkte i plugin-katalogen (dist/datasource/*) eller installere som en avhengighet. I vårt tilfelle kommer datakilden med plugin-koden. Det er også nødvendig å ha en config.html-mal og en ConfigCtrl-kontroller, som vil bli brukt for konfigurasjonssiden for datakildeforekomsten og datakildekontrolleren, som implementerer driftslogikken til datakilden din.

I KubeGraf-plugin-modulen, fra brukergrensesnittets synspunkt, er datakilden en forekomst av en kubernetes-klynge som implementerer følgende funksjoner (kildekoden er tilgjengelig по ссылке):

  • samler inn data fra k8s api-serveren (får en liste over navneområder, distribusjoner...)
  • proxy-forespørsler til prometheus-datasource (som er valgt i plugin-innstillingene for hver spesifikke klynge) og formatering av svar for å bruke data både på statiske sider og i dashboards.
  • oppdatering av data på statiske plugin-sider (med en angitt oppdateringsfrekvens).
  • behandle spørringer for å generere et malark i grafana-dashboards (metriFindQuery()-metoden)

Utvikling av en plugin for Grafana: en historie med store skudd

Utvikling av en plugin for Grafana: en historie med store skudd

Utvikling av en plugin for Grafana: en historie med store skudd

  • forbindelsestest med den siste k8s-klyngen.
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"};
       })
}

Et eget interessant poeng, etter vår mening, er implementeringen av en autentiserings- og autorisasjonsmekanisme for datakilden. Vanligvis, ut av boksen, kan vi bruke den innebygde Grafana-komponenten datasourceHttpSettings for å konfigurere tilgang til den endelige datakilden. Ved å bruke denne komponenten kan vi konfigurere tilgang til http-datakilden ved å spesifisere url og grunnleggende autentisering/autorisasjonsinnstillinger: påloggingspassord eller klientsert/klientnøkkel. For å implementere muligheten til å konfigurere tilgang ved hjelp av en bærer-token (de facto-standarden for k8s), måtte vi gjøre en liten justering.

For å løse dette problemet kan du bruke den innebygde Grafana "Plugin Routes"-mekanismen (mer informasjon på offisiell dokumentasjonsside). I innstillingene til datakilden vår kan vi deklarere et sett med rutingsregler som vil bli behandlet av grafana proxy-serveren. For hvert enkelt endepunkt er det for eksempel mulig å sette overskrifter eller nettadresser med mulighet for maling, hvor dataene kan hentes fra feltene jsonData og secureJsonData (for lagring av passord eller tokens i kryptert form). I vårt eksempel, spørringer som /__proxy/api/v1/navneområder vil bli proksert til url-en til skjemaet
/api/v8/namespaces med Autorisasjon: Bærer-overskrift.

Utvikling av en plugin for Grafana: en historie med store skudd

Utvikling av en plugin for Grafana: en historie med store skudd

For å jobbe med k8s api-serveren trenger vi naturligvis en bruker med skrivebeskyttet tilgang, manifester for å lage som du også kan finne i plugin kildekode.

Del 5: utgivelse

Utvikling av en plugin for Grafana: en historie med store skudd

Når du har skrevet ditt eget Grafana-plugin, vil du naturligvis gjøre det offentlig tilgjengelig. I Grafana er dette et bibliotek med plugins tilgjengelig her grafana.com/grafana/plugins

For at plugin-en din skal være tilgjengelig i den offisielle butikken, må du lage en PR-inngang dette depotetved å legge til innhold som dette i repo.json-filen:

Utvikling av en plugin for Grafana: en historie med store skudd

der versjon er versjonen av plugin-en din, url er en lenke til depotet, og commit er hashen til commit-en som en spesifikk versjon av plugin-en vil være tilgjengelig for.

Og ved utgangen vil du se et fantastisk bilde som:

Utvikling av en plugin for Grafana: en historie med store skudd

Dataene for den hentes automatisk fra Readme.md, Changelog.md og plugin.json-filen med plugin-beskrivelsen.

Del 6: i stedet for konklusjoner

Vi sluttet ikke å utvikle plugin-modulen vår etter utgivelsen. Og nå jobber vi med å overvåke ressursbruken til klyngenoder på riktig måte, introdusere nye funksjoner for å forbedre brukeropplevelsen, og også hente inn en stor mengde tilbakemeldinger mottatt etter installasjon av plugin både av våre kunder og fra folk på GitHub (hvis du forlater problemet eller pull-forespørselen din, jeg blir veldig glad :)

Vi håper at denne artikkelen vil hjelpe deg å forstå et så fantastisk verktøy som Grafana og kanskje skrive din egen plugin.

Takk!)

Kilde: www.habr.com

Legg til en kommentar