Développement d'un plugin pour Grafana : histoire des obstacles rencontrés

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é DevOpsProdigy KubeGraf. Le code source du plugin est disponible dans un dépÎt public sur GitHub. 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.

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés
module.ts

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés
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.

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés
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 Ă  l'adresse suivante: 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 minikube. La prochaine Ă©tape consiste Ă  installer Prometheus + Grafana avec l'aide de prometheus-operator. Dans cet article 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.L

Partie 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 :

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés

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.

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés

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 via le lien):

  • 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())

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés

  • 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 la page officielle de documentation). 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.

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés

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 le code source du plugin.

Partie 5 : sortie

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés

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 grafana.com/grafana/plugins

Pour que votre plugin soit disponible dans la boutique officielle, vous devez faire une PR dans ce dépÎt, en ajoutant dans le fichier repo.json un contenu du type :

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés

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 :

Développement d'un plugin pour Grafana : histoire des obstacles rencontrés

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

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster