Bonjour à tous ! Il y a quelques mois, nous avons lancé en production notre nouveau projet open-source : un plugin Grafana pour surveiller Kubernetes, que nous avons nommé . Le code source du plugin est disponible dans un . Dans cet article, nous souhaitons partager avec vous l'histoire de la création du plugin, les outils que nous avons utilisés et les obstacles que nous avons rencontrés lors du développement. Allons-y !
Partie 0 - Introduction : comment en sommes-nous arrivés là ?
L'idée de créer notre propre plugin pour Grafana nous est venue complÚtement par hasard. Notre entreprise s'occupe de la surveillance de projets web de divers niveaux de complexité depuis plus de 10 ans. Au cours de cette période, nous avons accumulé une grande expertise, des cas intéressants et de l'expérience avec divers systÚmes de surveillance. à un moment donné, nous nous sommes posé la question : « Existe-t-il un outil magique pour surveiller Kubernetes, que l'on puisse simplement installer et oublier ? »... Le standard de fait pour surveiller k8s est bien sûr la combinaison Prometheus + Grafana. Et pour cette pile, il existe un grand nombre d'outils variés : prometheus-operator, un ensemble de dashboards kubernetes-mixin, grafana-kubernetes-app.
L'option qui nous a semblé la plus intéressante était le plugin grafana-kubernetes-app, mais celui-ci n'est plus supporté depuis plus d'un an et, en outre, ne fonctionne pas avec les nouvelles versions de node-exporter et kube-state-metrics. à un moment donné, nous avons décidé : « Pourquoi ne ferions-nous pas notre propre solution ? »
Voici les idĂ©es que nous avons dĂ©cidĂ© de mettre en Ćuvre dans notre plugin :
- visualisation d'« une carte d'application » : une représentation pratique des applications dans le cluster, regroupées par namespaces, déploiements⊠;
- visualisation des relations du type « dĂ©ploiement â service (+ports) ».
- visualisation de la rĂ©partition des applications du cluster par nĆuds du cluster.
- collecte de métriques et d'informations provenant de plusieurs sources : Prometheus et k8s api server.
- surveillance Ă la fois de la partie infrastructure (utilisation du temps processeur, mĂ©moire, sous-systĂšme de stockage, rĂ©seau) et de la logique des applications â Ă©tat de santĂ© des pods, nombre de rĂ©pliques disponibles, informations sur la rĂ©ussite des tests de liveness/readyness.
Partie 1 : Qu'est-ce qu'un « plugin pour Grafana » ?
D'un point de vue technique, un plugin pour Grafana est un contrĂŽleur Angular qui se trouve dans le rĂ©pertoire de donnĂ©es de Grafana (/var/grafana/plugins/<your_plugin_name>/dist/module.js) et peut ĂȘtre chargĂ© comme un module SystemJS. Un fichier plugin.json doit Ă©galement se trouver dans ce rĂ©pertoire, contenant toutes les mĂ©tadonnĂ©es de votre plugin : nom, version, type de plugin, liens vers le dĂ©pĂŽt/site/ licence, dĂ©pendances, etc.

module.ts

plugin.json
Comme indiquĂ© sur la capture d'Ă©cran, nous avons spĂ©cifiĂ© plugin.type = app. En effet, les plugins pour Grafana peuvent ĂȘtre de trois types :
panel: le type de plugin le plus rĂ©pandu â il reprĂ©sente un panneau pour visualiser certaines mĂ©triques, utilisĂ© pour construire diffĂ©rents tableaux de bord.
datasource: un plugin-connecteur vers une source de données (par exemple, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).
app: un plugin vous permettant de construire votre propre application frontale Ă lâintĂ©rieur de Grafana, de crĂ©er vos propres pages html et d'accĂ©der manuellement Ă la source de donnĂ©es pour visualiser diverses donnĂ©es. Des plugins d'autres types (datasource, panel) et diffĂ©rents tableaux de bord peuvent Ă©galement ĂȘtre utilisĂ©s comme dĂ©pendances.

Exemple de dĂ©pendances dâun plugin avec type = app.
Comme langage de programmation, vous pouvez utiliser Ă la fois JavaScript et TypeScript (nous avons choisi ce dernier). Vous pouvez trouver des templates pour des plugins hello-world de n'importe quel type : ce dĂ©pĂŽt prĂ©sente un grand nombre de starter-pack (il y a mĂȘme un exemple expĂ©rimental de plugin sur React) avec des build tools prĂ©installĂ©s et configurĂ©s.
Partie 2 : préparation de l'environnement local
Pour travailler sur le plugin, il nous faudra, bien sĂ»r, un cluster Kubernetes avec tous les outils prĂ©installĂ©s : prometheus, node-exporter, kube-state-metrics, grafana. L'environnement doit se mettre en place rapidement, facilement et de maniĂšre fluide, et pour assurer le hot-reload, le rĂ©pertoire de donnĂ©es de Grafana doit ĂȘtre montĂ© directement depuis la machine du dĂ©veloppeur.
à notre avis, le moyen le plus pratique de travailler localement avec Kubernetes est . La prochaine étape consiste à installer Prometheus + Grafana avec l'aide de prometheus-operator. Dans le processus d'installation de prometheus-operator sur minikube est décrit en détail. Pour activer la persistance, il est nécessaire de définir le paramÚtre persistence: true dans le fichier charts/grafana/values.yaml, d'ajouter votre propre PV et PVC et de les spécifier dans le paramÚtre persistence.existingClaim
Le script final pour le lancement de minikube ressemble Ă ceci :
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.LPartie 3 : développement proprement dit
ModĂšle d'objet
En prĂ©paration pour la mise en Ćuvre du plugin, nous avons dĂ©cidĂ© de dĂ©crire toutes les entitĂ©s de base de Kubernetes avec lesquelles nous allons travailler sous forme de classes TypeScript : pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Chacune de ces classes hĂ©rite de la classe de base BaseModel, qui dĂ©crit le constructeur, le destructeur, ainsi que des mĂ©thodes pour la mise Ă jour et le changement de visibilitĂ©. Dans chacune des classes, les relations imbriquĂ©es avec d'autres entitĂ©s sont dĂ©crites, par exemple, la liste des pods pour une entitĂ© de type 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 = [];
}
}Avec les getters et setters, nous pouvons afficher ou dĂ©finir les mĂ©triques nĂ©cessaires des entitĂ©s de maniĂšre pratique et lisible. Par exemple, la sortie formatĂ©e de l'allocatable cpu d'un nĆud :
get cpuAllocatableFormatted(){
let cpu = this.data.status.allocatable.cpu;
if(cpu.indexOf('m') > -1){
cpu = parseInt(cpu)/1000;
}
return cpu;
}Pages
La liste de toutes les pages de notre plugin est initialement décrite dans notre pluing.json dans la section des dépendances :

Dans le bloc pour chaque page, nous devons indiquer le NOM DE LA PAGE (qui sera ensuite converti en slug, par lequel cette page sera accessible) ; le nom du composant responsable du fonctionnement de cette page (la liste des composants est exportée dans module.ts) ; l'indication du rÎle de l'utilisateur pour lequel l'accÚs à cette page et aux paramÚtres de navigation de la barre latérale est disponible.
Dans le composant responsable du fonctionnement de la page, nous devons définir templateUrl, en passant le chemin vers le fichier html contenant le balisage. à l'intérieur du contrÎleur, par le biais de l'injection de dépendance, nous pouvons accéder à 2 services Angular importants :
- backendSrv â service permettant l'interaction avec l'API serveur de Grafana ;
- datasourceSrv â service permettant l'interaction locale avec toutes les sources de donnĂ©es installĂ©es dans votre Grafana (par exemple, la mĂ©thode .getAll() â renvoie la liste de toutes les sources de donnĂ©es installĂ©es ; .get() â retourne l'objet instance d'une source de donnĂ©es spĂ©cifique.



Partie 4 : datasource
D'un point de vue de Grafana, une source de données est un plugin comme tous les autres : elle a son propre point d'entrée module.js et un fichier de métadonnées plugin.json. Lors de la création d'un plugin avec type = app, nous pouvons interagir avec les sources de données existantes (par exemple, prometheus-datasource) ainsi qu'avec nos propres sources de données, que nous pouvons stocker directement dans le répertoire du plugin (dist/datasource/*) ou installer comme dépendance. Dans notre cas, la source de données est fournie avec le code du plugin. Il est également impératif d'avoir un modÚle config.html et un contrÎleur ConfigCtrl, qui seront utilisés pour la page de configuration de l'instance de la source de données et le contrÎleur Datasource, dans lequel la logique de fonctionnement de votre source de données est réalisée.
Dans le plugin KubeGraf, d'un point de vue interface utilisateur, une source de données représente une instance d'un cluster kubernetes, qui dispose des capacités suivantes (le code source est disponible ):
- rĂ©cupĂ©ration des donnĂ©es de l'api-server k8s (obtention de la liste des namespaces, des dĂ©ploiementsâŠ)
- proxy des requĂȘtes vers prometheus-datasource (qui est sĂ©lectionnĂ© dans les paramĂštres du plugin pour chaque cluster spĂ©cifique) et formatage des rĂ©ponses pour utiliser les donnĂ©es Ă la fois dans les pages statiques et dans les tableaux de bord.
- actualisation des données sur les pages statiques du plugin (avec un temps de rafraßchissement défini).
- traitement des requĂȘtes pour gĂ©nĂ©rer la liste des templates dans grafana-dashboards (mĂ©thode .metriFindQuery())



- test de connexion avec le cluster k8s cible.
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: "La source de données est OK", title: "SuccÚs"};
}else{
return {status: "error", message: "La source de données n'est pas OK", title: "Erreur"};
}
}, error => {
return {status: "error", message: "La source de données n'est pas OK", title: "Erreur"};
})
}Un point particuliĂšrement intĂ©ressant, Ă notre avis, est la mise en Ćuvre du mĂ©canisme d'authentification et d'autorisation pour le datasource. En gĂ©nĂ©ral, pour configurer l'accĂšs Ă la source de donnĂ©es finale, nous pouvons utiliser le composant intĂ©grĂ© de Grafana â datasourceHttpSettings. Avec ce composant, nous pouvons configurer l'accĂšs Ă la source de donnĂ©es http en spĂ©cifiant l'url et les paramĂštres d'authentification/autorisation de base : identifiant-mot de passe, ou client-cert/client-key. Pour permettre la configuration d'accĂšs Ă l'aide d'un jeton bearer (un standard de fait pour k8s), il a fallu bricoler un peu.
Pour rĂ©soudre ce problĂšme, nous pouvons utiliser le mĂ©canisme intĂ©grĂ© de Grafana «Plugin Routes» (voir plus sur ). Dans les paramĂštres de notre datasource, nous pouvons dĂ©clarer un ensemble de rĂšgles de routage qui seront traitĂ©es par le serveur proxy de Grafana. Par exemple, pour chaque endpoint, il est possible de spĂ©cifier des en-tĂȘtes ou des url avec des options de template, dont les donnĂ©es peuvent ĂȘtre extraites des champs jsonData et secureJsonData (pour stocker des mots de passe ou des jetons sous forme cryptĂ©e). Dans notre exemple, les requĂȘtes du type /__proxy/api/v1/namespaces seront proxyĂ©es vers une url du type
/api/v1/namespaces avec l'en-tĂȘte Authorization: Bearer.


Naturellement, pour travailler avec l'api-server k8s, nous avons besoin d'un utilisateur avec des accÚs en lecture seule, et vous pouvez également trouver les manifestes pour sa création dans .
Partie 5 : sortie

AprÚs avoir écrit votre propre plugin pour Grafana, vous voudrez naturellement le rendre public. Dans Grafana, il existe une bibliothÚque de plugins disponible à l'adresse
Pour que votre plugin soit disponible dans la boutique officielle, vous devez faire une PR dans , en ajoutant dans le fichier repo.json un contenu du type :

oĂč version â version de votre plugin, url â lien vers le dĂ©pĂŽt, et commit â hash du commit par lequel une version spĂ©cifique du plugin sera disponible.
Et Ă la fin, vous verrez une belle image du type :

Les données pour cela seront automatiquement extraites de votre Readme.md, Changelog.md et du fichier plugin.json décrivant le plugin.
Partie 6 : en guise de conclusion
Nous n'avons pas arrĂȘtĂ© le dĂ©veloppement de notre plugin aprĂšs sa sortie. Nous travaillons actuellement sur une surveillance prĂ©cise de l'utilisation des ressources des nĆuds du cluster, l'ajout de nouvelles fonctionnalitĂ©s pour amĂ©liorer l'expĂ©rience utilisateur, et nous traitons un grand nombre de retours reçus aprĂšs l'installation du plugin, tant de nos clients que des issues sur GitHub (si vous laissez votre issue ou pull request, je serai trĂšs heureux đ ).
Nous espĂ©rons que cet article vous aidera Ă mieux comprendre cet outil merveilleux qu'est Grafana et, peut-ĂȘtre, Ă rĂ©diger votre propre plugin.
Merci !)
Source : habr.com
