Ciao a tutti! Qualche mese fa abbiamo lanciato in produzione il nostro nuovo progetto open-source: un plugin Grafana per il monitoraggio di Kubernetes, che abbiamo chiamato . Il codice sorgente del plugin è disponibile in . In questo articolo vogliamo condividere con voi la storia su come abbiamo creato il plugin, quali strumenti abbiamo utilizzato e quali insidie abbiamo incontrato nel processo di sviluppo. Iniziamo!
Parte 0 - Introduzione: come siamo arrivati qui?
L'idea di scrivere un nostro plugin per Grafana è nata completamente per caso. La nostra azienda si occupa di monitorare progetti web di varia complessità da oltre 10 anni. Nel corso di questo tempo, abbiamo accumulato un grande bagaglio di esperienza, casi interessanti e utilizzo di diversi sistemi di monitoraggio. Ad un certo punto ci siamo chiesti: "Esiste uno strumento magico per il monitoraggio di Kubernetes, per poter dire, come si suol dire, 'metti e dimentica'?". L'accoppiata Prometheus + Grafana è ormai lo standard per il monitoraggio di k8s. E per questo stack esiste un ampio insieme di strumenti di vario tipo: prometheus-operator, un insieme di dashboard kubernetes-mixin, grafana-kubernetes-app.
L'opzione che ci è sembrata più interessante è stata il plugin grafana-kubernetes-app, ma non è supportato da oltre un anno e, inoltre, non riesce a lavorare con le nuove versioni di node-exporter e kube-state-metrics. Così, a un certo punto, abbiamo deciso: "Perché non creare una nostra soluzione?"
Quali idee abbiamo deciso di realizzare nel nostro plugin:
- visualizzazione della 'mappa dell'applicazione': una rappresentazione comoda delle applicazioni nel cluster, raggruppate per namespace, deployment...
- visualizzazione delle relazioni di tipo 'deployment - service (+ports)'.
- visualizzazione della distribuzione delle applicazioni del cluster sui nodi del cluster.
- raccolta di metriche e informazioni da diverse fonti: Prometheus e k8s api server.
- monitoraggio sia della parte infrastrutturale (utilizzo del tempo di CPU, memoria, sottosistema di archiviazione, rete), sia della logica delle applicazioni: stato di salute dei pod, numero di repliche disponibili, informazioni sui controlli di liveness/readiness.
Parte 1: Che cos'è un 'plugin per Grafana'?
Dal punto di vista tecnico, un plugin per Grafana è un controller Angular, che viene memorizzato nella directory data di Grafana (/var/grafana/plugins/<your_plugin_name>/dist/module.js) e può essere caricato come modulo SystemJS. Inoltre, in questa directory deve trovarsi un file plugin.json, contenente tutte le informazioni meta sul tuo plugin: nome, versione, tipo di plugin, link al repository/sito/licenza, dipendenze e così via.

module.ts

plugin.json
Come visibile nello screenshot, abbiamo indicato plugin.type = app. Infatti, i plugin per Grafana possono essere di tre tipi:
panel: il tipo di plugin più comune — rappresenta un pannello per la visualizzazione di alcune metriche, utilizzato per costruire vari dashboard.
datasource: plugin connettore a una fonte di dati (ad esempio, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).
app: plugin che ti consente di costruire la tua applicazione frontend all'interno di Grafana, creare le tue pagine html e accedere manualmente a una datasource per visualizzare dati diversi. I plugin di altri tipi (datasource, panel) e vari dashboard possono essere utilizzati come dipendenze.

Esempio di dipendenze del plugin con type = app.
Come linguaggio di programmazione puoi utilizzare sia JavaScript che TypeScript (noi abbiamo scelto quest'ultimo). I template per i plugin hello-world di qualsiasi tipo puoi : in questo repository è presente un gran numero di starter-pack (c'è persino un esempio sperimentale di plugin su React) con bundler preinstallati e configurati.
Parte 2: preparazione dell'ambiente locale
Per lavorare al plugin, naturalmente, avremo bisogno di un cluster kubernetes con tutti gli strumenti preinstallati: prometheus, node-exporter, kube-state-metrics, grafana. L'ambiente deve essere impostato rapidamente, facilmente e senza problemi e, per garantire il caricamento a caldo, la directory dei dati di Grafana deve essere montata direttamente dalla macchina dello sviluppatore.
Il modo più conveniente, a nostro avviso, per lavorare localmente con kubernetes è . Il passo successivo è installare la combinazione Prometheus + Grafana utilizzando prometheus-operator. In è descritto in dettaglio il processo di installazione di prometheus-operator su minikube. Per attivare la persistenza è necessario impostare il parametro persistence: true nel file charts/grafana/values.yaml, aggiungere il proprio PV e PVC e specificarli nel parametro persistence.existingClaim
Lo script finale per avviare minikube appare così:
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.LParte 3: sviluppo diretto
Modello ad oggetti
Come preparazione per l'implementazione del plugin, abbiamo deciso di descrivere tutte le entità di base di Kubernetes con cui lavoreremo sotto forma di classi TypeScript: pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Ognuna di queste classi eredita da una classe comune BaseModel, che descrive il costruttore, il distruttore, i metodi per l'aggiornamento e il cambio di visibilità. In ciascuna delle classi, sono descritti i rapporti nidificati con altre entità, ad esempio, l'elenco dei pod dell'entità di tipo deployment.
import {Pod} from "./pod";
import {Service} from "./service";
import {BaseModel} from './traits/baseModel';
export class Deployment extends BaseModel{
pods: Array;
services: Array;
constructor(data: any){
super(data);
this.pods = [];
this.services = [];
}
}Con i getter e i setter possiamo visualizzare o impostare le metriche necessarie delle entità in modo comodo e leggibile. Ad esempio, la visualizzazione formattata dell'allocatable cpu del nodo:
get cpuAllocatableFormatted(){
let cpu = this.data.status.allocatable.cpu;
if(cpu.indexOf('m') > -1){
cpu = parseInt(cpu)/1000;
}
return cpu;
}Pagine
L'elenco di tutte le pagine del nostro plugin è inizialmente descritto nel nostro plugin.json nella sezione delle dipendenze:

Nel blocco per ogni pagina, dobbiamo specificare il NOME DELLA PAGINA (che sarà poi convertito in slug, tramite cui questa pagina sarà accessibile); il nome del componente responsabile del funzionamento di questa pagina (l'elenco dei componenti è esportato in module.ts); l'indicazione del ruolo utente per cui è disponibile l'interazione con questa pagina e le impostazioni di navigazione per la barra laterale.
Nel componente responsabile del funzionamento della pagina, dobbiamo impostare templateUrl, passando il percorso fino al file html con il markup. All'interno del controller, tramite dependency injection, possiamo accedere a due importanti servizi di angular:
- backendSrv — servizio che fornisce interazione con l'API server di Grafana;
- datasourceSrv — servizio che fornisce interazione locale con tutte le datasource installate nella tua Grafana (ad esempio, il metodo .getAll() restituisce l'elenco di tutte le datasource installate; .get() restituisce l'oggetto istanza di un datasource specifico.



Parte 4: datasource
Dal punto di vista di Grafana, il datasource è esattamente lo stesso tipo di plugin di tutti gli altri: ha il proprio punto di ingresso module.js e un file di metadati plugin.json. Durante lo sviluppo di un plugin con type = app possiamo interagire sia con datasource già esistenti (ad esempio, prometheus-datasource), sia con i nostri, che possiamo memorizzare direttamente nella directory del plugin (dist/datasource/*) o installare come dipendenza. Nel nostro caso, il datasource viene fornito insieme al codice del plugin. È inoltre necessario un modello config.html e un controller ConfigCtrl, che saranno utilizzati per la pagina di configurazione dell'istanza del datasource e del controller Datasource, dove viene implementata la logica di funzionamento del datasource.
Nel plugin KubeGraf, dal punto di vista dell'interfaccia utente, il datasource rappresenta un'istanza di un cluster kubernetes, che implementa le seguenti funzionalità (il codice sorgente è disponibile ):
- recupero dei dati dall'api-server di k8s (ottenimento dell'elenco dei namespace, dei deployment...)
- proxy delle richieste nel prometheus-datasource (che viene selezionato nelle impostazioni del plugin per ogni singolo cluster) e formattazione delle risposte per l'uso dei dati sia in pagine statiche che nei dashboard.
- aggiornamento dei dati nelle pagine statiche del plugin (con un tasso di refresh impostato).
- elaborazione delle richieste per la generazione di elenchi di template nei grafana-dashboards (metodo .metriFindQuery())



- test di connessione con il cluster k8s finale.
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"};
})
}Un aspetto particolarmente interessante, secondo noi, è l'implementazione del meccanismo di autenticazione e autorizzazione per il datasource. Di norma, per configurare l'accesso alla sorgente dati finale, possiamo utilizzare il componente integrato di Grafana: datasourceHttpSettings. Con questo componente possiamo configurare l'accesso a una sorgente dati http, specificando l'url e le impostazioni di base per l'autenticazione/autorization: login-password, oppure client-cert/client-key. Per implementare la possibilità di configurare l'accesso utilizzando un bearer token (de facto standard per k8s), è stato necessario fare un po' di "magia".
Per affrontare questa problematica, possiamo utilizzare il meccanismo integrato di Grafana "Plugin Routes" (maggiori dettagli su ). Nelle impostazioni del nostro datasource possiamo dichiarare un insieme di regole di routing che saranno elaborate dal server proxy di Grafana. Ad esempio, per ogni singolo endpoint, c'è la possibilità di impostare header o url con possibilità di templating, i cui dati possono essere presi dai campi jsonData e secureJsonData (per memorizzare password o token in forma crittografata). Nel nostro esempio, le richieste del tipo /__proxy/api/v1/namespaces saranno proxyate a un url del tipo
/api/v1/namespaces con l'impostazione dell'header Authorization: Bearer.


Naturalmente, per lavorare con il server api k8s abbiamo bisogno di un utente con accessi in sola lettura, i manifesti per creare il quale puoi trovare anche nel .
Parte 5: rilascio

Dopo aver scritto il tuo plugin per Grafana, vorrai naturalmente renderlo disponibile al pubblico. In Grafana c'è una libreria di plugin accessibile al link
Affinché il tuo plugin sia disponibile nello store ufficiale, devi fare un PR in , aggiungendo nel file repo.json un contenuto del tipo:

dove version è la versione del tuo plugin, url è un link al repository, e commit è l'hash del commit tramite cui sarà disponibile una specifica versione del plugin.
E alla fine vedrai una fantastica immagine del tipo:

I dati per essa saranno automaticamente estratti dal tuo Readme.md, Changelog.md e dal file plugin.json con la descrizione del plugin.
Parte 6: conclusioni
Non abbiamo interrotto lo sviluppo del nostro plugin dopo il rilascio. Stiamo attualmente lavorando sul monitoraggio corretto dell'utilizzo delle risorse dei nodi del cluster, sull'implementazione di nuove funzionalità per migliorare l'UX e stiamo anche gestendo un grande volume di feedback ricevuto dopo le installazioni del plugin, sia dai nostri clienti che dalle issue su GitHub (se lasci un tuo problema o una richiesta di pull, sarò molto felice 🙂 ).
Speriamo che questo articolo ti aiuti a capire uno strumento così fantastico come Grafana e, forse, a scrivere il tuo plugin.
Grazie!)
Fonte: habr.com
