Utveckling av ett plugin för Grafana: en historia av stora skott

Hej alla! För några månader sedan lanserade vi vårt nya open source-projekt i produktion - Grafana-plugin för övervakning av kubernetes, som vi kallade DevOpsProdigy KubeGraf. Plugin-källkoden är tillgänglig på offentligt arkiv på GitHub. Och i den här artikeln vill vi dela med dig av historien om hur vi skapade pluginet, vilka verktyg vi använde och vilka fallgropar vi stötte på under utvecklingsprocessen. Nu går vi!

Del 0 - inledning: hur kom vi till denna punkt?

Idén att skriva ett eget plugin för Grafan kom till oss av en slump. Vårt företag har övervakat webbprojekt av olika komplexitetsnivåer i mer än 10 år. Under denna tid har vi samlat på oss en stor mängd expertis, intressanta fall och erfarenhet av att använda olika övervakningssystem. Och vid något tillfälle frågade vi oss själva: "Finns det ett magiskt verktyg för att övervaka Kubernetes, så att, som de säger, "ställ in det och glöm det"?"... Branschstandarden för att övervaka k8s har naturligtvis länge varit Prometheus + Grafana kombination. Och som färdiga lösningar för denna stack finns det en stor uppsättning olika typer av verktyg: prometheus-operator, en uppsättning kubernetes-mixin-dashboards, grafana-kubernetes-app.

Insticksprogrammet grafana-kubernetes-app verkade vara det mest intressanta alternativet för oss, men det har inte stötts på mer än ett år och kan dessutom inte fungera med nya versioner av node-exporter och kube-state-metrics. Och vid något tillfälle bestämde vi oss: "Ska vi inte fatta vårt eget beslut?"

Vilka idéer vi bestämde oss för att implementera i vårt plugin:

  • visualisering av "applikationskartan": bekväm presentation av applikationer i klustret, grupperade efter namnrymder, distributioner...;
  • visualisering av anslutningar som "deployment - service (+ports)".
  • visualisering av fördelningen av klusterapplikationer över klusternoder.
  • insamling av mätvärden och information från flera källor: Prometheus och k8s api-server.
  • övervakning av både infrastrukturdelen (användning av CPU-tid, minne, diskdelsystem, nätverk) och applikationslogik - hälsostatuspods, antal tillgängliga repliker, information om att klara liveness/readyness-tester.

Del 1: Vad är ett "Grafana-plugin"?

Ur teknisk synvinkel är plugin för Grafana en vinkelstyrenhet, som lagras i Grafanas datakatalog (/var/grafana/plugins/ /dist/module.js) och kan laddas som en SystemJS-modul. Även i den här katalogen bör det finnas en plugin.json-fil som innehåller all metainformation om din plugin: namn, version, plugin-typ, länkar till arkivet/webbplatsen/licensen, beroenden och så vidare.

Utveckling av ett plugin för Grafana: en historia av stora skott
module.ts

Utveckling av ett plugin för Grafana: en historia av stora skott
plugin.json

Som du kan se på skärmdumpen angav vi plugin.type = app. Eftersom plugins för Grafana kan vara av tre typer:

panel: den vanligaste typen av plugin - det är en panel för att visualisera alla mätvärden, som används för att bygga olika instrumentpaneler.
datakälla: plugin-anslutning till någon datakälla (till exempel Prometheus-datakälla, ClickHouse-datakälla, ElasticSearch-datakälla).
app: Ett plugin som låter dig bygga din egen frontend-applikation inuti Grafana, skapa dina egna html-sidor och manuellt komma åt datakällan för att visualisera olika data. Även plugins av andra typer (datakälla, panel) och olika instrumentpaneler kan användas som beroenden.

Utveckling av ett plugin för Grafana: en historia av stora skott
Exempel på plugin-beroenden med type=app.

Du kan använda både JavaScript och TypeScript som programmeringsspråk (vi valde det). Förberedelser för hello-world plugins av vilken typ du kan hitta länken: detta förråd innehåller ett stort antal startpaket (det finns till och med ett experimentellt exempel på en plugin i React) med förinstallerade och konfigurerade byggare.

Del 2: förbereda den lokala miljön

För att arbeta med plugin-programmet behöver vi naturligtvis ett kubernetes-kluster med alla förinstallerade verktyg: prometheus, node-exporter, kube-state-metrics, grafana. Miljön bör ställas in snabbt, enkelt och naturligt, och för att säkerställa hot-reload bör Grafana-datakatalogen monteras direkt från utvecklarens maskin.

Det bekvämaste sättet, enligt vår mening, att arbeta lokalt med kubernetes är minikube. Nästa steg är att installera kombinationen Prometheus + Grafana med hjälp av prometheus-operator. I Denna artikel Processen att installera prometheus-operator på minikube beskrivs i detalj. För att aktivera beständighet måste du ställa in parametern uthållighet: sant i filen charts/grafana/values.yaml, lägg till din egen PV och PVC och ange dem i parametern persistence.existingClaim

Vårt sista minikube-lanseringsskript ser ut så här:

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 utveckling

Objektmodell

Som förberedelse för att implementera pluginet bestämde vi oss för att beskriva alla grundläggande Kubernetes-entiteter som vi kommer att arbeta med i form av TypeScript-klasser: pod, deployment, daemonset, statefulset, jobb, cronjob, service, nod, namnområde. Var och en av dessa klasser ärver från den gemensamma BaseModel-klassen, som beskriver konstruktorn, destruktorn, metoderna för uppdatering och växling av synlighet. Var och en av klasserna beskriver kapslade relationer med andra entiteter, till exempel en lista med poddar för en entitet av typen distribution.

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

Med hjälp av getters och seters kan vi visa eller ställa in de entitetsmått vi behöver i en bekväm och läsbar form. Till exempel, formaterad utdata från allokerbara CPU-noder:

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

sidor

En lista över alla våra plugin-sidor beskrivs initialt i vår pluing.json i avsnittet beroenden:

Utveckling av ett plugin för Grafana: en historia av stora skott

I blocket för varje sida måste vi ange PAGE NAME (det kommer sedan att konverteras till en slug som den här sidan kommer att vara tillgänglig); namnet på den komponent som är ansvarig för driften av denna sida (listan över komponenter exporteras till module.ts); anger användarrollen för vilken arbete med den här sidan är tillgängligt och navigeringsinställningar för sidofältet.

I komponenten som är ansvarig för driften av sidan måste vi ställa in templateUrl och skicka sökvägen till html-filen med markup dit. Inuti styrenheten, genom beroendeinjektion, kan vi komma åt upp till två viktiga vinkeltjänster:

  • backendSrv - en tjänst som ger interaktion med Grafana API-servern;
  • datasourceSrv - en tjänst som tillhandahåller lokal interaktion med alla datakällor installerade i din Grafana (till exempel metoden .getAll() - returnerar en lista över alla installerade datakällor; .get( ) - returnerar ett instansobjekt för en specifik datakälla.

Utveckling av ett plugin för Grafana: en historia av stora skott

Utveckling av ett plugin för Grafana: en historia av stora skott

Utveckling av ett plugin för Grafana: en historia av stora skott

Del 4: datakälla

Ur Grafanas synvinkel är datakällan exakt samma plugin som alla andra: den har sin egen ingångspunkt module.js, det finns en fil med metainformation plugin.json. När vi utvecklar ett plugin med typ = app kan vi interagera med både befintliga datakällor (till exempel prometheus-datasource) och våra egna, som vi kan lagra direkt i plugin-katalogen (dist/datasource/*) eller installera som ett beroende. I vårt fall kommer datakällan med plugin-koden. Det är också nödvändigt att ha en config.html-mall och en ConfigCtrl-kontroller, som kommer att användas för datakällans instanskonfigurationssida och Datasource-kontrollanten, som implementerar logiken för din datakälla.

I insticksprogrammet KubeGraf, ur användargränssnittets synvinkel, är datakällan en instans av ett kubernetes-kluster som implementerar följande funktioner (källkod är tillgänglig по ссылке):

  • samla in data från k8s api-server (får en lista över namnområden, distributioner...)
  • proxyförfrågningar till prometheus-datasource (som väljs i plugininställningarna för varje specifikt kluster) och formatering av svar för att använda data både på statiska sidor och i instrumentpaneler.
  • uppdatering av data på statiska plugin-sidor (med en inställd uppdateringsfrekvens).
  • bearbetar frågor för att generera ett mallblad i grafana-dashboards (metriFindQuery()-metoden)

Utveckling av ett plugin för Grafana: en historia av stora skott

Utveckling av ett plugin för Grafana: en historia av stora skott

Utveckling av ett plugin för Grafana: en historia av stora skott

  • anslutningstest med det sista k8s-klustret.
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"};
       })
}

En separat intressant punkt, enligt vår mening, är implementeringen av en autentiserings- och auktoriseringsmekanism för datakällan. Vanligtvis kan vi ur lådan använda den inbyggda Grafana-komponenten datasourceHttpSettings för att konfigurera åtkomst till den slutliga datakällan. Med den här komponenten kan vi konfigurera åtkomst till http-datakällan genom att ange webbadressen och grundläggande autentiserings-/auktoriseringsinställningar: login-lösenord eller client-cert/client-key. För att implementera möjligheten att konfigurera åtkomst med hjälp av en bärartoken (de facto-standarden för k8s), var vi tvungna att göra lite justeringar.

För att lösa detta problem kan du använda den inbyggda Grafana "Plugin Routes"-mekanismen (mer information på officiella dokumentationssida). I inställningarna för vår datakälla kan vi deklarera en uppsättning routingregler som kommer att behandlas av grafana-proxyservern. Till exempel är det för varje enskild slutpunkt möjligt att ställa in rubriker eller webbadresser med möjlighet till mall, vars data kan hämtas från fälten jsonData och secureJsonData (för lagring av lösenord eller tokens i krypterad form). I vårt exempel, frågor som /__proxy/api/v1/namnutrymmen kommer att skickas till formulärets webbadress
/api/v8/namespaces med rubriken Authorization: Bearer.

Utveckling av ett plugin för Grafana: en historia av stora skott

Utveckling av ett plugin för Grafana: en historia av stora skott

Naturligtvis, för att arbeta med k8s api-servern behöver vi en användare med skrivskyddad åtkomst, manifest för att skapa som du också kan hitta i plugin-källkod.

Del 5: release

Utveckling av ett plugin för Grafana: en historia av stora skott

När du väl har skrivit ditt eget Grafana-plugin, vill du naturligtvis göra det tillgängligt för allmänheten. I Grafana är detta ett bibliotek med plugins som är tillgängliga här grafana.com/grafana/plugins

För att ditt plugin ska vara tillgängligt i den officiella butiken måste du göra en PR in detta förrådgenom att lägga till innehåll som detta till filen repo.json:

Utveckling av ett plugin för Grafana: en historia av stora skott

där version är versionen av ditt plugin, url är en länk till arkivet och commit är hashen för commit för vilken en specifik version av plugin kommer att vara tillgänglig.

Och vid utgången kommer du att se en underbar bild som:

Utveckling av ett plugin för Grafana: en historia av stora skott

Data för den kommer automatiskt att hämtas från din Readme.md, Changelog.md och plugin.json-filen med plugin-beskrivningen.

Del 6: istället för slutsatser

Vi slutade inte utveckla vårt plugin efter release. Och nu arbetar vi på att korrekt övervaka användningen av resurser för klusternoder, introducera nya funktioner för att förbättra UX och även samla in en stor mängd feedback som erhållits efter installation av plugin både av våra kunder och från personer på GitHub (om du lämnar din fråga eller pull-förfrågan, jag blir väldigt glad :)

Vi hoppas att den här artikeln kommer att hjälpa dig att förstå ett så underbart verktyg som Grafana och kanske skriva ditt eget plugin.

Tack!)

Källa: will.com

Lägg en kommentar