Hallo zusammen! Vor einigen Monaten haben wir unser neues Open-Source-Projekt in die Produktion gebracht – ein Grafana-Plugin zur Überwachung von Kubernetes, das wir genannt haben. Der Quellcode des Plugins ist in einem verfügbar. In diesem Artikel möchten wir unsere Geschichte über die Entwicklung des Plugins teilen, welche Werkzeuge wir verwendet haben und auf welche Stolpersteine wir während der Entwicklung gestoßen sind. Los geht's!
Teil 0 – Einleitung: Wie sind wir dazu gekommen?
Die Idee, ein eigenes Plugin für Grafana zu entwickeln, entstand bei uns ganz zufällig. Unser Unternehmen beschäftigt sich seit über 10 Jahren mit der Überwachung von Webprojekten unterschiedlicher Komplexität. In dieser Zeit haben wir ein umfangreiches Fachwissen, interessante Fallstudien und Erfahrungen mit verschiedenen Überwachungssystemen gesammelt. Irgendwann stellten wir uns die Frage: „Gibt es ein magisches Werkzeug zur Überwachung von Kubernetes, das, wie man so schön sagt, ‚einfach eingerichtet und vergessen‘ werden kann?“ Der Standard für die Überwachung von k8s ist seit langem die Kombination aus Prometheus und Grafana. Für dieses Stack gibt es bereits eine Vielzahl von Werkzeugen, wie den prometheus-operator, eine Sammlung von Dashboards aus kubernetes-mixin und die grafana-kubernetes-app.
Die interessanteste Option für uns schien das Plugin grafana-kubernetes-app zu sein, allerdings wird es seit mehr als einem Jahr nicht mehr unterstützt und kann zudem nicht mit den neuen Versionen von node-exporter und kube-state-metrics arbeiten. Irgendwann beschlossen wir: „Warum nicht eine eigene Lösung entwickeln?“
Welche Ideen haben wir in unserem Plugin umgesetzt:
- Visualisierung der „Anwendungskarte“: eine benutzerfreundliche Darstellung von Anwendungen im Cluster, gruppiert nach Namespaces, Deployments…;
- Visualisierung von Verbindungen zwischen „Deployment – Service (+Ports)“.
- Visualisierung der Verteilung der Clusteranwendungen auf die Cluster-Nodes.
- Sammlung von Metriken und Informationen aus mehreren Quellen: Prometheus und der K8s API-Server.
- Überwachung sowohl des Infrastrukturteils (Nutzung von CPU-Zeit, RAM, Speichersystem, Netzwerk) als auch der Anwendungslogik – Health-Status von Pods, Anzahl der verfügbaren Replikate, Informationen über das Durchlaufen von Liveness-/Readyness-Checks.
Teil 1: Was ist ein „Grafana-Plugin“?
Technisch gesehen ist ein Grafana-Plugin ein Angular-Controller, der im Datenverzeichnis von Grafana gespeichert wird (/var/grafana/plugins/<your_plugin_name>/dist/module.js) und als SystemJS-Modul geladen werden kann. Ebenso sollte sich in diesem Verzeichnis eine Datei plugin.json befinden, die alle Meta-Informationen über Ihr Plugin enthält: Name, Version, Plugin-Typ, Links zu Repository/Website/Lizenz, Abhängigkeiten usw.

module.ts

plugin.json
Wie im Screenshot zu sehen ist, haben wir plugin.type = app angegeben. Plugins für Grafana können drei Arten haben:
panel: Der am weitesten verbreitete Plug-in-Typ ist ein Dashboard-Panel zur Visualisierung von Metriken und wird für die Erstellung verschiedener Dashboards verwendet.
Datenquelle: Ein Plug-in-Connector zu einer Datenquelle (z. B. Prometheus-Datenquelle, ClickHouse-Datenquelle, ElasticSearch-Datenquelle).
app: Ein Plug-in, mit dem Sie Ihre eigene Frontend-Anwendung innerhalb von Grafana erstellen, eigene HTML-Seiten generieren und manuell auf die Datenquelle zugreifen können, um verschiedene Daten zu visualisieren. Auch andere Plug-in-Typen (Datenquelle, Panel) und verschiedene Dashboards können als Abhängigkeiten genutzt werden.

Beispiel für Plug-in-Abhängigkeiten mit type = app.
Als Programmiersprache können sowohl JavaScript als auch TypeScript verwendet werden (wir haben uns für Letzteres entschieden). Vorlagen für Hello-World-Plugins jeglicher Art finden Sie : In diesem Repository finden Sie eine Vielzahl von Starter-Packs (es gibt sogar ein experimentelles Beispiel für ein Plug-in auf React) mit vorinstallierten und konfigurierten Build-Tools.
Teil 2: Vorbereitung der lokalen Umgebung
Um mit dem Plugin zu arbeiten, benötigen wir selbstverständlich ein Kubernetes-Cluster mit allen vorinstallierten Tools: Prometheus, Node-Exporter, Kube-State-Metrics, Grafana. Die Umgebung sollte schnell, einfach und unproblematisch eingerichtet werden, und um Hot-Reload zu gewährleisten, muss das Datenverzeichnis von Grafana direkt vom Entwicklergerät gemountet werden.
Nach unserer Auffassung ist der bequemste Weg, lokal mit Kubernetes zu arbeiten, . Der nächste Schritt besteht darin, das Prometheus + Grafana-Bündel mithilfe des Prometheus-Operators zu installieren. In wird der Installationsprozess des Prometheus-Operators auf Minikube ausführlich beschrieben. Um Persistenz zu aktivieren, muss der Parameter persistence: true in der Datei charts/grafana/values.yaml gesetzt werden, ein eigenes PV und PVC hinzugefügt und diese im Parameter persistence.existingClaim angegeben werden.
Das finale Skript zum Starten von Minikube sieht so aus:
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.LTeil 3: direkte Entwicklung
Objektmodell
Zur Vorbereitung der Implementierung des Plugins haben wir entschieden, alle grundlegenden Entitäten von Kubernetes in Form von TypeScript-Klassen zu beschreiben: Pod, Deployment, DaemonSet, StatefulSet, Job, CronJob, Service, Node, Namespace. Jede dieser Klassen erbt von der übergeordneten Klasse BaseModel, in der Konstruktor, Destruktor sowie Methoden zum Aktualisieren und Wechseln der Sichtbarkeit beschrieben sind. In jeder Klasse werden die eingebetteten Beziehungen zu anderen Entitäten, wie zum Beispiel die Liste der Pods bei der Entität vom Typ Deployment, beschrieben.
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 = [];
}
}Mit Hilfe von Getter- und Setter-Methoden können wir die benötigten Metriken der Entitäten in einem übersichtlichen und lesbaren Format ausgeben oder setzen. Zum Beispiel die formatierte Ausgabe des allocatable CPU eines Nodes:
get cpuAllocatableFormatted(){
let cpu = this.data.status.allocatable.cpu;
if(cpu.indexOf('m') > -1){
cpu = parseInt(cpu)/1000;
}
return cpu;
}Seiten
Die Liste aller Seiten unseres Plugins wird anfänglich in unserer pluing.json im Abschnitt Abhängigkeiten beschrieben:

Für jeden Seite müssen wir den SEITENNAME angeben (dies wird dann in einen Slug umgewandelt, unter dem die Seite erreichbar ist); den Namen der Komponente, die für diese Seite zuständig ist (die Liste der Komponenten wird in module.ts exportiert); die Benutzerrolle angeben, für die die Arbeit mit dieser Seite zugänglich ist, sowie die Navigationseinstellungen für die Seitenleiste.
In der Komponente, die für die Seite zuständig ist, müssen wir templateUrl festlegen und den Pfad zur HTML-Datei mit dem Markup übergeben. Innerhalb des Controllers können wir über Dependency Injection auf zwei wichtige Angular-Dienste zugreifen:
- backendSrv — der Dienst, der die Interaktion mit dem API-Server von Grafana ermöglicht;
- datasourceSrv — der Dienst, der die lokale Interaktion mit allen in Ihrer Grafana installierten Datenquellen ermöglicht (zum Beispiel gibt die Methode .getAll() eine Liste aller installierten Datenquellen zurück; .get() gibt das Objekt-Instance einer bestimmten Datenquelle zurück.



Teil 4: Datenquelle
Aus der Perspektive von Grafana ist die Datenquelle ein Plugin wie jedes andere: Sie hat ihren eigenen Einstiegspunkt module.js und eine Meta-Informationsdatei plugin.json. Bei der Entwicklung eines Plugins mit type = app können wir sowohl mit bereits bestehenden Datenquellen (wie z.B. prometheus-datasource) als auch mit unseren eigenen interagieren, die wir direkt im Verzeichnis des Plugins (dist/datasource/*) speichern oder als Abhängigkeit installieren können. In unserem Fall wird die Datenquelle zusammen mit dem Code des Plugins geliefert. Außerdem sind das Template config.html und der Controller ConfigCtrl erforderlich, die für die Konfigurationsseite der Datenquelleninstanz und den Controller Datasource verwendet werden, in dem die Logik Ihrer Datenquelle implementiert ist.
Im KubeGraf-Plugin stellt die Datenquelle aus der Sicht der Benutzeroberfläche eine Instanz des Kubernetes-Clusters dar, in dem folgende Funktionen implementiert sind (der Quellcode ist verfügbar ):
- Abrufen von Daten vom API-Server k8s (Abrufen der Liste von Namespaces, Deployments…)
- Proxy-Anfragen an die Prometheus-Datenquelle (die in den Plugin-Einstellungen für jeden bestimmten Cluster ausgewählt wird) und Formatierung der Antworten zur Nutzung der Daten sowohl auf statischen Seiten als auch in Dashboards.
- Aktualisierung der Daten auf statischen Seiten des Plugins (mit festgelegtem Aktualisierungsintervall).
- Verarbeitung von Anfragen zur Erstellung einer Template-Liste in Grafana-Dashboards (Methode .metriFindQuery()).



- Test der Verbindung zum endgültigen k8s-Cluster.
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: "Datenquelle ist OK", title: "Erfolg"};
}else{
return {status: "error", message: "Datenquelle ist nicht OK", title: "Fehler"};
}
}, error => {
return {status: "error", message: "Datenquelle ist nicht OK", title: "Fehler"};
})
}Ein besonders interessantes Thema ist für uns die Implementierung des Authentifizierungs- und Autorisierungsmechanismus für Datasources. In der Regel können wir für die Konfiguration des Zugangs zu einer Datenquelle das integrierte Grafana-Komponenten datasourceHttpSettings verwenden. Mit dieser Komponente können wir den Zugang zu http-Datenquellen konfigurieren, indem wir die URL und die grundlegenden Authentifizierungs-/Autorisierungseinstellungen angeben: Benutzername-Passwort oder client-cert/client-key. Um die Möglichkeit zur Zugangskonfiguration mit einem Bearer-Token (de-facto Standard für k8s) zu implementieren, war jedoch ein wenig „Tüftelei“ erforderlich.
Zur Lösung dieser Aufgabe kann der integrierte Grafana-Mechanismus „Plugin-Routen“ verwendet werden (siehe auch ). In den Einstellungen unseres Datasources können wir eine Reihe von Routing-Regeln definieren, die vom Grafana-Proxy-Server verarbeitet werden. Für jeden einzelnen Endpoint besteht die Möglichkeit, Headers oder URLs mit Template-Funktionalität zu vergeben, wobei die Daten aus den Feldern jsonData und secureJsonData (zur Speicherung von Passwörtern oder Tokens in verschlüsselter Form) stammen können. In unserem Beispiel enthalten Anfragen der Art /__proxy/api/v1/namespaces werden auf eine URL wie
/api/v1/namespaces mit dem Header Authorization: Bearer.


Natürlich benötigen wir für die Arbeit mit dem k8s-APIServer einen Benutzer mit Readonly-Rechten, dessen Manifest Sie ebenfalls finden können in .
Teil 5: Veröffentlichung

Nachdem Sie Ihr eigenes Plugin für Grafana geschrieben haben, möchten Sie es natürlich öffentlich zugänglich machen. In Grafana gibt es eine Plugin-Bibliothek, die über folgende Link zugänglich ist:
Damit Ihr Plugin im offiziellen Store verfügbar ist, müssen Sie einen PR in indem Sie den Inhalt folgendermaßen in die Datei repo.json hinzufügen:

wobei version — die Version Ihres Plugins, url — der Link zum Repository und commit — der Hash des Commits ist, unter dem die spezifische Version des Plugins verfügbar ist.
Und am Ende sehen Sie ein schönes Bild in dieser Form:

Die Daten hierfür werden automatisch von Ihrem Readme.md, Changelog.md und der Datei plugin.json mit der Beschreibung des Plugins gesammelt.
Teil 6: anstelle von Schlussfolgerungen
Wir haben die Entwicklung unseres Plugins nach dem Release nicht eingestellt. Derzeit arbeiten wir an einem präzisen Monitoring der Ressourcennutzung der Clusterknoten, implementieren neue Funktionen zur Verbesserung der Benutzererfahrung und bearbeiten ein großes Feedback, das wir nach den Installationen des Plugins sowohl von unseren Kunden als auch aus Issues auf GitHub erhalten haben (wenn Sie Ihr Issue oder Pull-Request hinterlassen, würde ich mich sehr freuen 🙂 ).
Wir hoffen, dass dieser Artikel Ihnen hilft, sich in diesem großartigen Werkzeug namens Grafana zurechtzufinden und möglicherweise sogar Ihr eigenes Plugin zu schreiben.
Danke!)
Quelle: habr.com
