Udvikling af et plugin til Grafana: en historie med store skud

Hej alle! For et par måneder siden lancerede vi vores nye open source-projekt i produktion - Grafana-pluginet til overvågning af kubernetes, som vi kaldte DevOpsProdigy KubeGraf. Plugin-kildekoden er tilgængelig på offentligt lager på GitHub. Og 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 - indledende: hvordan kom vi til dette punkt?

Ideen til at skrive vores eget plugin til Grafan kom til os helt ved et tilfælde. Vores virksomhed har overvåget webprojekter af forskellige kompleksitetsniveauer i mere end 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å som man siger, "indstil det og glem det"?". Branchestandarden for overvågning af k8'er har selvfølgelig længe været Prometheus + Grafana kombination. Og som færdige løsninger til denne stak er der et stort sæt af forskellige slags værktøjer: prometheus-operator, et sæt kubernetes-mixin-dashboards, grafana-kubernetes-app.

Grafana-kubernetes-app-plugin'et så ud til at være den mest interessante mulighed for os, men det har ikke været understøttet i mere end et år og kan desuden ikke fungere med nye versioner af node-exporter og kube-state-metrics. Og på et tidspunkt besluttede vi: "Skal vi ikke træffe vores egen beslutning?"

Hvilke ideer besluttede vi at implementere i vores plugin:

  • visualisering af "applikationskortet": praktisk præsentation af applikationer i klyngen, grupperet efter navnerum, implementeringer...;
  • visualisering af forbindelser som "deployment - service (+ports)".
  • visualisering af fordelingen af ​​klyngeapplikationer på tværs af klynge noder.
  • indsamling af metrik og information fra flere kilder: Prometheus og k8s api server.
  • overvågning af både infrastrukturdelen (brug af CPU-tid, hukommelse, diskundersystem, netværk) og applikationslogik - sundhedsstatus-pods, antal tilgængelige replikaer, information om beståelse af liveness/readyness-tests.

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

Fra et teknisk synspunkt er plugin'et til Grafana en vinkelcontroller, som er gemt i Grafanas databibliotek (/var/grafana/plugins/ /dist/module.js) og kan indlæses som et SystemJS-modul. Også i denne mappe skulle der være en plugin.json-fil indeholdende alle metaoplysninger om dit plugin: navn, version, plugin-type, links til depotet/webstedet/licensen, afhængigheder og så videre.

Udvikling af et plugin til Grafana: en historie med store skud
modul.ts

Udvikling af et plugin til Grafana: en historie med store skud
plugin.json

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

panel: den mest almindelige type plugin - det er et panel til visualisering af enhver metrik, der bruges til at bygge forskellige dashboards.
datakilde: plugin-stik 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 plugin til Grafana: en historie med store skud
Eksempel på plugin-afhængigheder med type=app.

Du kan bruge både JavaScript og TypeScript som programmeringssprog (vi valgte det). Forberedelser til 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 i React) med forudinstallerede og konfigurerede buildere.

Del 2: forberede 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 bør opsættes hurtigt, nemt og naturligt, og for at sikre hot-reload bør Grafana-datamappen monteres direkte fra udviklerens maskine.

Den mest bekvemme måde, efter vores mening, at arbejde lokalt med kubernetes er minikube. Det næste trin er at installere Prometheus + Grafana kombinationen 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: faktisk udvikling

Objektmodel

Som forberedelse til implementering af pluginnet besluttede vi at beskrive alle de grundlæggende Kubernetes-entiteter, som vi vil arbejde med i form af TypeScript-klasser: pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Hver af disse klasser arver fra den fælles BaseModel-klasse, som beskriver konstruktøren, destruktoren, metoder til opdatering og skift af synlighed. Hver af klasserne beskriver indlejrede relationer med andre entiteter, for eksempel en liste over pods for en enhed af typen implementering.

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 formateret output af 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 vores plugin-sider er indledningsvis beskrevet i vores pluing.json i afsnittet om afhængigheder:

Udvikling af et plugin til Grafana: en historie med store skud

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); angiver den brugerrolle, som arbejde med denne side er tilgængeligt for, og navigationsindstillinger for sidebjælken.

I den komponent, der er ansvarlig for driften af ​​siden, skal vi indstille templateUrl, der passerer stien til html-filen med markup. Inde i controlleren kan vi gennem afhængighedsinjektion få adgang til op til 2 vigtige vinkeltjenester:

  • backendSrv - en tjeneste, der giver interaktion med Grafana API-serveren;
  • datasourceSrv - en tjeneste, der giver lokal interaktion med alle datakilder installeret i din Grafana (f.eks. metoden .getAll() - returnerer en liste over alle installerede datakilder; .get( ) - returnerer et instansobjekt af en specifik datakilde.

Udvikling af et plugin til Grafana: en historie med store skud

Udvikling af et plugin til Grafana: en historie med store skud

Udvikling af et plugin til Grafana: en historie med store skud

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 kommer 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 datakilden fra brugergrænsefladesynspunktet en forekomst af en kubernetes-klynge, der implementerer følgende funktioner (kildekode er tilgængelig по ссылке):

  • indsamler data fra k8s api-serveren (få 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 for at bruge data både på statiske sider og i dashboards.
  • opdatering af data på statiske plugin-sider (med en indstillet opdateringshastighed).
  • behandle forespørgsler for at generere et skabelonark i grafana-dashboards (metriFindQuery()-metoden)

Udvikling af et plugin til Grafana: en historie med store skud

Udvikling af et plugin til Grafana: en historie med store skud

Udvikling af et plugin til Grafana: en historie med store skud

  • forbindelsestest 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 ​​en godkendelses- og autorisationsmekanisme for datakilden. Typisk, ud af boksen, kan vi bruge den indbyggede Grafana-komponent datasourceHttpSettings til at konfigurere adgang til den endelige datakilde. Ved at bruge denne komponent kan vi konfigurere adgang til http-datakilden ved at angive url og grundlæggende godkendelses-/autorisationsindstillinger: login-adgangskode eller client-cert/client-key. For at implementere muligheden for at konfigurere adgang ved hjælp af en bærer-token (de facto-standarden for k8s), var vi nødt til at foretage en lille justering.

For at løse dette problem kan du bruge den indbyggede Grafana "Plugin Routes" mekanisme (flere detaljer på officiel dokumentationsside). I indstillingerne for vores datakilde kan vi erklære et sæt routingregler, som vil blive behandlet af grafana proxy-serveren. For eksempel er det for hvert enkelt slutpunkt muligt at indstille headers eller urls 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 som /__proxy/api/v1/navneområder vil blive proxy til formularens url
/api/v8/namespaces med Autorisation: Bearer-headeren.

Udvikling af et plugin til Grafana: en historie med store skud

Udvikling af et plugin til Grafana: en historie med store skud

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

Del 5: udgivelse

Udvikling af et plugin til Grafana: en historie med store skud

Når du har skrevet dit eget Grafana-plugin, vil du naturligvis gerne gøre det offentligt tilgængeligt. I Grafana er dette et bibliotek af plugins, der er tilgængeligt her grafana.com/grafana/plugins

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

Udvikling af et plugin til Grafana: en historie med store skud

hvor version er versionen af ​​dit plugin, url er et link til repository, og commit er hashen af ​​commit, som en specifik version af plugin vil være tilgængelig for.

Og ved udgangen vil du se et vidunderligt billede som:

Udvikling af et plugin til Grafana: en historie med store skud

Dataene til det vil automatisk blive hentet fra din Readme.md, Changelog.md og plugin.json-filen 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 at overvåge brugen af ​​ressourcer af klynge noder, introducere nye funktioner til at forbedre UX og også indhente en stor mængde feedback modtaget efter installation af plugin både af vores kunder og fra folk på GitHub (hvis du forlader dit problem eller pull anmodning, jeg vil 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

Tilføj en kommentar