Salut tout le monde! Il y a quelques mois, nous avons lancé en production notre nouveau projet open source - le plugin Grafana pour surveiller Kubernetes, que nous avons appelé . Le code source du plugin est disponible sur . Et dans cet article, nous souhaitons partager avec vous l'histoire de la façon dont nous avons créé le plugin, les outils que nous avons utilisés et les piÚges que nous avons rencontrés au cours du processus de développement. Allons-y!
Partie 0 - introduction : comment en sommes-nous arrivés là ?
LâidĂ©e dâĂ©crire notre propre plugin pour Grafan nous est venue par hasard. Notre entreprise suit depuis plus de 10 ans des projets web de diffĂ©rents niveaux de complexitĂ©. Au cours de cette pĂ©riode, nous avons accumulĂ© une grande expertise, des cas intĂ©ressants et une expĂ©rience dans lâutilisation de divers systĂšmes de surveillance. Et Ă un moment donnĂ©, nous nous sommes demandĂ©s : « Existe-t-il un outil magique pour surveiller Kubernetes, de sorte que, comme on dit, « configurez-le et oubliez-le » ? Combinaison PromĂ©thĂ©e + Grafana. Et comme solutions prĂȘtes Ă l'emploi pour cette pile, il existe un large Ă©ventail d'outils de diffĂ©rents types : prometheus-operator, un ensemble de tableaux de bord kubernetes-mixin, grafana-kubernetes-app.
Le plugin grafana-kubernetes-app nous a semblĂ© ĂȘtre l'option la plus intĂ©ressante, mais il n'est plus supportĂ© depuis plus d'un an et, de plus, ne peut pas fonctionner avec les nouvelles versions de node-exporter et kube-state-metrics. Et Ă un moment donnĂ©, nous avons dĂ©cidĂ© : « Ne devrions-nous pas prendre notre propre dĂ©cision ?
Quelles idĂ©es nous avons dĂ©cidĂ© de mettre en Ćuvre dans notre plugin :
- visualisation de la « carte des applications » : présentation pratique des applications du cluster, regroupées par espaces de noms, déploiements... ;
- visualisation des connexions comme « déploiement - service (+ports) ».
- visualisation de la distribution des applications de cluster sur les nĆuds du cluster.
- collecte de métriques et d'informations provenant de plusieurs sources : Prometheus et le serveur api k8s.
- surveillance à la fois de la partie infrastructure (utilisation du temps CPU, de la mémoire, du sous-systÚme de disque, du réseau) et de la logique de l'application - pods d'état de santé, nombre de répliques disponibles, informations sur la réussite des tests d'activité/préparation.
Partie 1 : Qu'est-ce qu'un « plugin Grafana » ?
D'un point de vue technique, le plugin pour Grafana est un contrĂŽleur angulaire, qui est stockĂ© dans le rĂ©pertoire de donnĂ©es Grafana (/var/grafana/plugins/ /dist/module.js) et peut ĂȘtre chargĂ© en tant que module SystemJS. Ce rĂ©pertoire devrait Ă©galement contenir un fichier plugin.json contenant toutes les mĂ©ta-informations sur votre plugin : nom, version, type de plugin, liens vers le rĂ©fĂ©rentiel/site/licence, dĂ©pendances, etc.

module.ts

plugin.json
Comme vous pouvez le voir sur la capture d'Ă©cran, nous avons spĂ©cifiĂ© plugin.type = app. Car les plugins pour Grafana peuvent ĂȘtre de trois types :
panneau: le type de plugin le plus courant - il s'agit d'un panneau permettant de visualiser toutes les métriques, utilisé pour créer divers tableaux de bord.
source de données: connecteur de plugin vers une source de données (par exemple, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).
appli: Un plugin qui vous permet de crĂ©er votre propre application front-end dans Grafana, de crĂ©er vos propres pages html et d'accĂ©der manuellement Ă la source de donnĂ©es pour visualiser diverses donnĂ©es. De plus, des plugins d'autres types (source de donnĂ©es, panneau) et divers tableaux de bord peuvent ĂȘtre utilisĂ©s comme dĂ©pendances.

Exemples de dépendances de plugin avec type=app.
Vous pouvez utiliser Ă la fois JavaScript et TypeScript comme langage de programmation (nous l'avons choisi). PrĂ©parations pour les plugins hello-world de tout type que vous pouvez : ce rĂ©fĂ©rentiel contient un grand nombre de starter-packs (il existe mĂȘme un exemple expĂ©rimental de plugin dans React) avec des builders prĂ©installĂ©s et configurĂ©s.
Partie 2 : prĂ©parer lâenvironnement local
Pour travailler sur le plugin, nous avons naturellement besoin d'un cluster kubernetes avec tous les outils prĂ©installĂ©s : prometheus, node-exporter, kube-state-metrics, grafana. L'environnement doit ĂȘtre configurĂ© rapidement, facilement et naturellement, et pour garantir le rechargement Ă chaud, le rĂ©pertoire de donnĂ©es Grafana doit ĂȘtre montĂ© directement depuis la machine du dĂ©veloppeur.
Le moyen le plus pratique, à notre avis, de travailler localement avec Kubernetes est . L'étape suivante consiste à installer la combinaison Prometheus + Grafana à 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, vous devez définir le paramÚtre persistance : vrai dans le fichier charts/grafana/values.yaml, ajoutez vos propres PV et PVC et spécifiez-les dans le paramÚtre persistence.existingClaim
Notre script de lancement final du 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 réel
ModĂšle objet
En prĂ©paration de l'implĂ©mentation du plugin, nous avons dĂ©cidĂ© de dĂ©crire toutes les entitĂ©s de base Kubernetes avec lesquelles nous travaillerons sous forme de classes TypeScript : pod, dĂ©ploiement, dĂ©monset, statefulset, travail, cronjob, service, nĆud, espace de noms. Chacune de ces classes hĂ©rite de la classe commune BaseModel, qui dĂ©crit le constructeur, le destructeur, les mĂ©thodes de mise Ă jour et de changement de visibilitĂ©. Chacune des classes dĂ©crit des relations imbriquĂ©es avec d'autres entitĂ©s, par exemple une liste de pods pour une entitĂ© de type dĂ©ploiement.
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 = [];
}
}Avec l'aide des getters et des setters, nous pouvons afficher ou dĂ©finir les mĂ©triques d'entitĂ© dont nous avons besoin sous une forme pratique et lisible. Par exemple, sortie formatĂ©e des nĆuds CPU allouables :
get cpuAllocatableFormatted(){
let cpu = this.data.status.allocatable.cpu;
if(cpu.indexOf('m') > -1){
cpu = parseInt(cpu)/1000;
}
return cpu;
}Pages
Une liste de toutes nos pages de plugins est initialement décrite dans notre pluing.json dans la section dépendances :

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



Partie 4 : source de données
Du point de vue de Grafana, datasource est exactement le mĂȘme plugin que tous les autres : il a son propre point d'entrĂ©e module.js, il y a un fichier avec des mĂ©ta informations plugin.json. Lors du dĂ©veloppement d'un plugin avec type = app, nous pouvons interagir Ă la fois avec les sources de donnĂ©es existantes (par exemple, prometheus-datasource) et les nĂŽtres, que nous pouvons stocker directement dans le rĂ©pertoire du plugin (dist/datasource/*) ou installer en tant que dĂ©pendance. Dans notre cas, la source de donnĂ©es est livrĂ©e avec le code du plugin. Il est Ă©galement nĂ©cessaire 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 source de donnĂ©es et le contrĂŽleur Datasource, qui implĂ©mente la logique de votre source de donnĂ©es.
Dans le plugin KubeGraf, du point de vue de l'interface utilisateur, la source de données est une instance d'un cluster Kubernetes qui implémente les fonctionnalités suivantes (le code source est disponible ):
- collecte de données à partir du serveur API k8s (obtention d'une liste d'espaces de noms, de 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.
- mise à jour des données sur les pages de plugin statiques (avec un taux de rafraßchissement défini).
- traitement des requĂȘtes pour gĂ©nĂ©rer une feuille de modĂšle dans grafana-dashboards (mĂ©thode metriFindQuery())



- test de connexion avec le cluster k8s final.
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 autre point intĂ©ressant, Ă notre avis, est la mise en Ćuvre d'un mĂ©canisme d'authentification et d'autorisation pour la source de donnĂ©es. En rĂšgle gĂ©nĂ©rale, nous pouvons utiliser le composant Grafana intĂ©grĂ© datasourceHttpSettings pour configurer l'accĂšs Ă la source de donnĂ©es finale. Ă l'aide de 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 : login-password ou client-cert/client-key. Afin d'implĂ©menter la possibilitĂ© de configurer l'accĂšs Ă l'aide d'un jeton de porteur (la norme de facto pour les k8), nous avons dĂ» procĂ©der Ă quelques ajustements.
Pour rĂ©soudre ce problĂšme, vous pouvez utiliser le mĂ©canisme intĂ©grĂ© Grafana « Plugin Routes » (plus de dĂ©tails sur ). Dans les paramĂštres de notre source de donnĂ©es, nous pouvons dĂ©clarer un ensemble de rĂšgles de routage qui seront traitĂ©es par le serveur proxy grafana. Par exemple, pour chaque point de terminaison individuel, il est possible de dĂ©finir des en-tĂȘtes ou des URL avec possibilitĂ© de modĂšle, 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, des requĂȘtes telles que /__proxy/api/v1/espaces de noms sera proxy vers l'url du formulaire
/api/v8/namespaces avec l'en-tĂȘte Authorization: Bearer.


Naturellement, pour travailler avec le serveur API k8s, nous avons besoin d'un utilisateur avec un accÚs en lecture seule, des manifestes de création que vous pouvez également trouver dans .
Partie 5 : sortie

Une fois que vous aurez écrit votre propre plugin Grafana, vous souhaiterez naturellement le rendre public. Dans Grafana, c'est une bibliothÚque de plugins disponible ici
Pour que votre plugin soit disponible sur la boutique officielle, vous devez faire un PR en en ajoutant du contenu comme celui-ci au fichier repo.json :

oĂč version est la version de votre plugin, url est un lien vers le rĂ©fĂ©rentiel et commit est le hachage du commit pour lequel une version spĂ©cifique du plugin sera disponible.
Et Ă la sortie, vous verrez une magnifique image comme :

Les données correspondantes seront automatiquement extraites de votre fichier Readme.md, Changelog.md et du fichier plugin.json avec la description du plugin.
Partie 6 : au lieu de conclusions
Nous n'avons pas arrĂȘtĂ© de dĂ©velopper notre plugin aprĂšs sa sortie. Et maintenant, nous travaillons Ă surveiller correctement l'utilisation des ressources des nĆuds de cluster, Ă introduire de nouvelles fonctionnalitĂ©s pour amĂ©liorer l'UX, et Ă©galement Ă rĂ©colter une grande quantitĂ© de commentaires reçus aprĂšs l'installation du plugin Ă la fois par nos clients et par les personnes sur GitHub (si vous quittez votre problĂšme ou pull request, je serai trĂšs heureux :)
Nous espĂ©rons que cet article vous aidera Ă comprendre un outil aussi merveilleux que Grafana et, peut-ĂȘtre, Ă Ă©crire votre propre plugin.
Merci!)
Source: habr.com
