Udvikling af et Grafana-plugin: A History of Mistakes

Hej alle sammen! For et par måneder siden lancerede vi vores nye open source-projekt i produktion - et Grafana-plugin til overvågning af kubernetes, som vi kaldte DevOpsProdigy KubeGraf. Kildekoden til plugin er tilgængelig på offentligt lager på GitHub. I denne artikel vil vi dele historien med dig om, hvordan vi skabte pluginnet, hvilke værktøjer vi brugte, og hvilke faldgruber vi stødte på under udviklingsprocessen. Lad os gå!

Del 0 - Introduktion: Hvordan kom vi hertil?

Ideen til at skrive vores eget plugin til Grafana kom til os helt ved et uheld. Vores virksomhed har overvåget webprojekter af varierende kompleksitetsniveauer i over 10 år. I løbet af denne tid har vi oparbejdet en stor mængde ekspertise, interessante cases og erfaring med at bruge forskellige overvågningssystemer. Og på et tidspunkt spurgte vi os selv: "Er der et magisk værktøj til at overvåge Kubernetes, så du, som man siger, kan "indstille det og glemme det"?".. Branchestandarden for overvågning af K8S har naturligvis længe været Prometheus + Grafana-pakken. Og som færdige løsninger til denne stak er der et stort sæt af forskellige typer værktøjer: prometheus-operator, et sæt dashboards kubernetes-mixin, grafana-kubernetes-app.

Den mest interessante mulighed for os så ud til at være pluginnet grafana-kubernetes-app, men det har ikke været understøttet i over et år og kan desuden ikke fungere med nye versioner af node-exporter og kube-state-metrics. Og på et tidspunkt besluttede vi: "Hvorfor laver vi ikke vores egen løsning?"

Hvilke ideer besluttede vi at implementere i vores plugin:

  • visualisering af "applikationskortet": en praktisk repræsentation af applikationer i en klynge, grupperet efter navnerum, implementeringer osv.;
  • visualisering af forbindelser af typen "deployment - service (+ports)".
  • visualisering af fordelingen af ​​klyngeapplikationer på tværs af klynge noder.
  • indsamling af metrics og information fra flere kilder: Prometheus og k8s api-server.
  • overvågning af både infrastrukturdelen (brug af processortid, hukommelse, disksubsystem, netværk) og applikationslogik - sundhedsstatus for pods, antal tilgængelige replikaer, information om gennemgangen af ​​liveness/readiness-tests.

Del 1: Hvad er et "Grafana-plugin"?

Teknisk set er et Grafana-plugin en vinkelcontroller, der er gemt i Grafanas datamappe (/var/grafana/plugins/ /dist/modul.js) og kan indlæses som et SystemJS-modul. Også i denne mappe skulle der være en fil plugin.json, som indeholder al metainformation om dit plugin: navn, version, plugin-type, links til repository/site/licens, afhængigheder og så videre.

Udvikling af et Grafana-plugin: A History of Mistakes
modul.ts

Udvikling af et Grafana-plugin: A History of Mistakes
plugin.json

Som du kan se på skærmbilledet, specificerede vi plugin.type = app. Fordi der er tre typer plugins til Grafana:

panel: den mest almindelige type plugins - er et panel til visualisering af nogle metrics, der bruges til at bygge forskellige dashboards.
datakilde: en plugin-forbindelse til en eller anden datakilde (f.eks. Prometheus-datakilde, ClickHouse-datakilde, ElasticSearch-datakilde).
app: et plugin, der giver dig mulighed for at bygge din egen frontend-applikation inde i Grafana, oprette dine egne html-sider og manuelt få adgang til datakilden for at visualisere forskellige data. Også plugins af andre typer (datakilde, panel) og forskellige dashboards kan bruges som afhængigheder.

Udvikling af et Grafana-plugin: A History of Mistakes
Eksempel på plugin-afhængigheder med type = app.

Du kan bruge enten JavaScript eller TypeScript som programmeringssprog (vi valgte TypeScript). Blanks for hello-world plugins af enhver type du kan find linket: dette lager indeholder et stort antal startpakker (der er endda et eksperimentelt eksempel på et plugin på React) med forudinstallerede og konfigurerede bundlere.

Del 2: Forberedelse af lokalmiljøet

For at arbejde på plugin'et har vi naturligvis brug for en kubernetes-klynge med alle de forudinstallerede værktøjer: prometheus, node-exporter, kube-state-metrics, grafana. Miljøet skal være hurtigt, nemt og ubesværet at konfigurere, og for at sikre hot-reload bør Grafana-databiblioteket monteres direkte fra udviklerens maskine.

Efter vores mening er den mest bekvemme måde at arbejde lokalt med kubernetes på minikube. Det næste trin er at installere Prometheus + Grafana-pakken ved hjælp af prometheus-operator. I denne artikel Processen med at installere prometheus-operator på minikube er beskrevet i detaljer. For at aktivere persistens skal du indstille parameteren vedholdenhed: sandt i charts/grafana/values.yaml filen, tilføj din egen PV og PVC og angiv dem i persistence.existingClaim parameteren

Vores sidste minikube-lanceringsscript ser sådan ud:

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: Direkte udvikling

Objektmodel

Som forberedelse til plugin-implementeringen besluttede vi at beskrive alle de grundlæggende Kubernetes-entiteter, som vi vil arbejde med som TypeScript-klasser: pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Hver af disse klasser arver fra en fælles BaseModel-klasse, som definerer en konstruktør, destruktor, metoder til opdatering og skift af synlighed. Hver klasse beskriver indlejrede relationer med andre entiteter, f.eks. en liste over pods for en enhed af implementeringstypen.

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 hjælp af gettere og sættere kan vi vise eller indstille de enhedsmetrikker, vi har brug for, i en bekvem og læsbar form. For eksempel det formaterede output 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

Listen over alle sider i vores plugin er indledningsvis beskrevet i vores pluing.json i afsnittet om afhængigheder:

Udvikling af et Grafana-plugin: A History of Mistakes

I blokken for hver side skal vi angive SIDENAVNET (det vil derefter blive konverteret til en slug, hvorved denne side vil være tilgængelig); navnet på den komponent, der er ansvarlig for driften af ​​denne side (listen over komponenter eksporteres til module.ts); angiv den brugerrolle, der kan arbejde med denne side og navigationsindstillinger for sidebjælken.

I den komponent, der er ansvarlig for sidens drift, skal vi indstille templateUrl, og give den stien til html-filen med markeringen. Inde i controlleren kan vi gennem afhængighedsinjektion få adgang til op til 2 vigtige vinkeltjenester:

  • backendSrv er en tjeneste, der giver interaktion med grafana api-serveren;
  • datasourceSrv — en tjeneste, der giver lokal interaktion med alle datakilder installeret i din Grafana (for eksempel returnerer .getAll()-metoden en liste over alle installerede datakilder; .get( ) - returnerer et instansobjekt af en specifik datakilde.

Udvikling af et Grafana-plugin: A History of Mistakes

Udvikling af et Grafana-plugin: A History of Mistakes

Udvikling af et Grafana-plugin: A History of Mistakes

Del 4: datakilde

Fra Grafanas synspunkt er datakilden nøjagtig det samme plugin som alle de andre: den har sit eget indgangspunkt module.js, der er en fil med metainformation plugin.json. Når vi udvikler et plugin med type = app, kan vi interagere med både eksisterende datakilder (for eksempel prometheus-datasource) og vores egne, som vi kan gemme direkte i plugin-biblioteket (dist/datasource/*) eller installere som en afhængighed. I vores tilfælde leveres datakilden med plugin-koden. Det er også nødvendigt at have en config.html-skabelon og en ConfigCtrl-controller, som vil blive brugt til datakildeforekomstens konfigurationsside og datakildecontrolleren, som implementerer logikken i din datakilde.

I KubeGraf-pluginnet er en datakilde fra brugergrænsefladeperspektivet en instans af en kubernetes-klynge, der implementerer følgende funktioner (kildekode tilgængelig по ссылке):

  • henter data fra k8s api-server (henter en liste over navneområder, implementeringer...)
  • proxy-forespørgsler til prometheus-datasource (som er valgt i plugin-indstillingerne for hver specifik klynge) og formatering af svar til brug af data både på statiske sider og i dashboards.
  • opdatering af data på statiske sider i plugin'et (med den indstillede opdateringshastighedstid).
  • behandler forespørgsler for at generere et skabelonark i grafana-dashboards (metode .metriFindQuery())

Udvikling af et Grafana-plugin: A History of Mistakes

Udvikling af et Grafana-plugin: A History of Mistakes

Udvikling af et Grafana-plugin: A History of Mistakes

  • testforbindelse med den endelige k8s-klynge.
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 særskilt interessant punkt, efter vores mening, er implementeringen af ​​godkendelses- og autorisationsmekanismen for datakilden. Som regel kan vi uden for boksen bruge den indbyggede Grafana-komponent — datasourceHttpSettings — til at konfigurere adgang til den endelige datakilde. Ved at bruge denne komponent kan vi konfigurere adgang til en http-datakilde ved at angive URL'en og grundlæggende godkendelses-/autorisationsindstillinger: login-adgangskode eller klient-cert/klient-nøgle. For at implementere muligheden for at konfigurere adgang ved hjælp af en bærer-token (de facto-standarden for k8'er), var vi nødt til at lave lidt fifler.

For at løse dette problem kan du bruge den indbyggede Grafana-mekanisme "Plugin Routes" (flere detaljer om officiel dokumentationsside). I vores datakildeindstillinger kan vi erklære et sæt routingregler, der vil blive behandlet af grafana proxy-serveren. For hvert enkelt slutpunkt er der for eksempel mulighed for at indstille overskrifter eller URL'er med mulighed for skabelon, hvor data kan hentes fra felterne jsonData og secureJsonData (til lagring af adgangskoder eller tokens i krypteret form). I vores eksempel, forespørgsler i formularen /__proxy/api/v1/navneområder vil blive proxy til formularens URL'er
/api/v8/namespaces med Autorisation: Bearer-headersæt.

Udvikling af et Grafana-plugin: A History of Mistakes

Udvikling af et Grafana-plugin: A History of Mistakes

For at arbejde med k8s api-serveren har vi naturligvis brug for en bruger med skrivebeskyttet adgang, manifesterne til oprettelse, som du også kan finde i plugin kildekode.

Del 5: Udgivelse

Udvikling af et Grafana-plugin: A History of Mistakes

Når du først har skrevet dit eget Grafana-plugin, vil du naturligvis gerne gøre det til open source. I Grafana er dette et bibliotek af plugins, tilgængeligt på dette link grafana.com/grafana/plugins

For at dit plugin kan være tilgængeligt i den officielle butik, skal du lave en PR ind dette depot, tilføjer indhold som dette til repo.json-filen:

Udvikling af et Grafana-plugin: A History of Mistakes

hvor version er versionen af ​​dit plugin, url er linket til repository, og commit er hashen af ​​commit, der vil gøre den specifikke version af plugin tilgængelig.

Og ved udgangen vil du se et vidunderligt billede som dette:

Udvikling af et Grafana-plugin: A History of Mistakes

Dataene for det vil automatisk blive hentet fra din Readme.md, Changelog.md og plugin.json fil med plugin-beskrivelsen.

Del 6: I stedet for konklusioner

Vi stoppede ikke med at udvikle vores plugin efter udgivelsen. Og nu arbejder vi på korrekt overvågning af brugen af ​​cluster node ressourcer, implementerer nye funktioner for at forbedre UX og sorterer også en stor mængde feedback fra efter installation af pluginnet både fra vores kunder og fra anmodninger på GitHub (hvis du forlader dit problem eller pull-anmodning, vil jeg blive meget glad 🙂 ).

Vi håber, at denne artikel vil hjælpe dig med at forstå et så vidunderligt værktøj som Grafana og måske skrive dit eget plugin.

Tak!)

Kilde: www.habr.com

Køb pålidelig hosting til websteder med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster