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
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.
modul.ts
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.
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
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
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:
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.
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)
- 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å
/api/v8/namespaces med Autorisasjon: Bærer-overskrift.
For å jobbe med k8s api-serveren trenger vi naturligvis en bruker med skrivebeskyttet tilgang, manifester for å lage som du også kan finne i
Del 5: utgivelse
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
For at plugin-en din skal være tilgjengelig i den offisielle butikken, må du lage en PR-inngang
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:
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