{"id":52343,"date":"2019-11-06T00:00:00","date_gmt":"2019-11-05T21:00:00","guid":{"rendered":"https:\/\/prohoster.info\/blog\/blog_prohoster\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek"},"modified":"2020-02-18T14:00:02","modified_gmt":"2020-02-18T11:00:02","slug":"razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek","status":"publish","type":"post","link":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek","title":{"rendered":"D\u00e9veloppement d'un plugin pour Grafana : histoire des obstacles rencontr\u00e9s","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Bonjour \u00e0 tous ! Il y a quelques mois, nous avons lanc\u00e9 en production notre nouveau projet open-source : un plugin Grafana pour surveiller Kubernetes, que nous avons nomm\u00e9 <b><noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/grafana\/plugins\/devopsprodigy-kubegraf-app\">DevOpsProdigy KubeGraf<\/a><\/noindex><\/b>. Le code source du plugin est disponible dans un <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\">d\u00e9p\u00f4t public sur GitHub<\/a><\/noindex>. Dans cet article, nous souhaitons partager avec vous l'histoire de la cr\u00e9ation du plugin, les outils que nous avons utilis\u00e9s et les obstacles que nous avons rencontr\u00e9s lors du d\u00e9veloppement. Allons-y !<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h4>Partie 0 - Introduction : comment en sommes-nous arriv\u00e9s l\u00e0 ?<\/h4>\n<p>\nL'id\u00e9e de cr\u00e9er notre propre plugin pour Grafana nous est venue compl\u00e8tement par hasard. Notre entreprise s'occupe de la surveillance de projets web de divers niveaux de complexit\u00e9 depuis plus de 10 ans. Au cours de cette p\u00e9riode, nous avons accumul\u00e9 une grande expertise, des cas int\u00e9ressants et de l'exp\u00e9rience avec divers syst\u00e8mes de surveillance. \u00c0 un moment donn\u00e9, nous nous sommes pos\u00e9 la question : \u00ab Existe-t-il un outil magique pour surveiller Kubernetes, que l'on puisse simplement installer et oublier ? \u00bb... Le standard de fait pour surveiller k8s est bien s\u00fbr la combinaison Prometheus + Grafana. Et pour cette pile, il existe un grand nombre d'outils vari\u00e9s : prometheus-operator, un ensemble de dashboards kubernetes-mixin, grafana-kubernetes-app. <\/p>\n<p>L'option qui nous a sembl\u00e9 la plus int\u00e9ressante \u00e9tait le plugin grafana-kubernetes-app, mais celui-ci n'est plus support\u00e9 depuis plus d'un an et, en outre, ne fonctionne pas avec les nouvelles versions de node-exporter et kube-state-metrics. \u00c0 un moment donn\u00e9, nous avons d\u00e9cid\u00e9 : \u00ab Pourquoi ne ferions-nous pas notre propre solution ? \u00bb<\/p>\n<p>Voici les id\u00e9es que nous avons d\u00e9cid\u00e9 de mettre en \u0153uvre dans notre plugin :<\/p>\n<ul>\n<li>visualisation d'\u00ab une carte d'application \u00bb : une repr\u00e9sentation pratique des applications dans le cluster, regroup\u00e9es par namespaces, d\u00e9ploiements\u2026 ;<\/li>\n<li>visualisation des relations du type \u00ab d\u00e9ploiement \u2014 service (+ports) \u00bb.<\/li>\n<li>visualisation de la r\u00e9partition des applications du cluster par n\u0153uds du cluster.<\/li>\n<li>collecte de m\u00e9triques et d'informations provenant de plusieurs sources : Prometheus et k8s api server.<\/li>\n<li>surveillance \u00e0 la fois de la partie infrastructure (utilisation du temps processeur, m\u00e9moire, sous-syst\u00e8me de stockage, r\u00e9seau) et de la logique des applications \u2014 \u00e9tat de sant\u00e9 des pods, nombre de r\u00e9pliques disponibles, informations sur la r\u00e9ussite des tests de liveness\/readyness.<\/li>\n<\/ul>\n<p><\/p>\n<h4>Partie 1 : Qu'est-ce qu'un \u00ab plugin pour Grafana \u00bb ?<\/h4>\n<p>\nD'un point de vue technique, un plugin pour Grafana est un contr\u00f4leur Angular qui se trouve dans le r\u00e9pertoire de donn\u00e9es de Grafana (<i>\/var\/grafana\/plugins\/&lt;your_plugin_name&gt;\/dist\/module.js<\/i>) et peut \u00eatre charg\u00e9 comme un module SystemJS. Un fichier plugin.json doit \u00e9galement se trouver dans ce r\u00e9pertoire, contenant toutes les m\u00e9tadonn\u00e9es de votre plugin : nom, version, type de plugin, liens vers le d\u00e9p\u00f4t\/site\/ licence, d\u00e9pendances, etc.<\/p>\n<p><img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/5993fec77a3904ed8b9e19a5addbb6ea.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>module.ts<\/i><\/p>\n<p><img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/1e5b780837e5a9385e5e22a27d83f567.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>plugin.json<\/i><\/p>\n<p>Comme indiqu\u00e9 sur la capture d'\u00e9cran, nous avons sp\u00e9cifi\u00e9 plugin.type = app. En effet, les plugins pour Grafana peuvent \u00eatre de trois types :<\/p>\n<p><b>panel<\/b>: le type de plugin le plus r\u00e9pandu \u2014 il repr\u00e9sente un panneau pour visualiser certaines m\u00e9triques, utilis\u00e9 pour construire diff\u00e9rents tableaux de bord.<br \/>\n<b>datasource<\/b>: un plugin-connecteur vers une source de donn\u00e9es (par exemple, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).<br \/>\n<b>app<\/b>: un plugin vous permettant de construire votre propre application frontale \u00e0 l\u2019int\u00e9rieur de Grafana, de cr\u00e9er vos propres pages html et d'acc\u00e9der manuellement \u00e0 la source de donn\u00e9es pour visualiser diverses donn\u00e9es. Des plugins d'autres types (datasource, panel) et diff\u00e9rents tableaux de bord peuvent \u00e9galement \u00eatre utilis\u00e9s comme d\u00e9pendances.<\/p>\n<p><img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/3cd53c7c3a2635f3f20831d52198b5f0.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Exemple de d\u00e9pendances d\u2019un plugin avec type = app<\/i>.<\/p>\n<p>Comme langage de programmation, vous pouvez utiliser \u00e0 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 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/CorpGlory\">\u00e0 l'adresse suivante<\/a><\/noindex>: ce d\u00e9p\u00f4t pr\u00e9sente un grand nombre de starter-pack (il y a m\u00eame un exemple exp\u00e9rimental de plugin sur React) avec des build tools pr\u00e9install\u00e9s et configur\u00e9s. <\/p>\n<h4>Partie 2 : pr\u00e9paration de l'environnement local<\/h4>\n<p>\nPour travailler sur le plugin, il nous faudra, bien s\u00fbr, un cluster Kubernetes avec tous les outils pr\u00e9install\u00e9s : prometheus, node-exporter, kube-state-metrics, grafana. L'environnement doit se mettre en place rapidement, facilement et de mani\u00e8re fluide, et pour assurer le hot-reload, le r\u00e9pertoire de donn\u00e9es de Grafana doit \u00eatre mont\u00e9 directement depuis la machine du d\u00e9veloppeur.<\/p>\n<p>\u00c0 notre avis, le moyen le plus pratique de travailler localement avec Kubernetes est <noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/tasks\/tools\/install-minikube\/\">minikube<\/a><\/noindex>. La prochaine \u00e9tape consiste \u00e0 installer Prometheus + Grafana avec l'aide de prometheus-operator. Dans <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/faun\/trying-prometheus-operator-with-helm-minikube-b617a2dccfa3\">cet article<\/a><\/noindex> le processus d'installation de prometheus-operator sur minikube est d\u00e9crit en d\u00e9tail. Pour activer la persistance, il est n\u00e9cessaire de d\u00e9finir le param\u00e8tre <i>persistence: true<\/i> dans le fichier charts\/grafana\/values.yaml, d'ajouter votre propre PV et PVC et de les sp\u00e9cifier dans le param\u00e8tre persistence.existingClaim<\/p>\n<p>Le script final pour le lancement de minikube ressemble \u00e0 ceci :<\/p>\n<pre><code class=\"plaintext\">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\nminikube mount \n\/home\/sergeisporyshev\/Projects\/Grafana:\/var\/grafana --gid=472 --uid=472 --9p-version=9p2000.L<\/code><\/pre>\n<p><\/p>\n<h4>Partie 3 : d\u00e9veloppement proprement dit<\/h4>\n<p>\n<b>Mod\u00e8le d'objet<\/b><\/p>\n<p>En pr\u00e9paration pour la mise en \u0153uvre du plugin, nous avons d\u00e9cid\u00e9 de d\u00e9crire toutes les entit\u00e9s 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\u00e9rite de la classe de base BaseModel, qui d\u00e9crit le constructeur, le destructeur, ainsi que des m\u00e9thodes pour la mise \u00e0 jour et le changement de visibilit\u00e9. Dans chacune des classes, les relations imbriqu\u00e9es avec d'autres entit\u00e9s sont d\u00e9crites, par exemple, la liste des pods pour une entit\u00e9 de type deployment.<\/p>\n<pre><code class=\"plaintext\">import {Pod} from '.\/pod';\nimport {Service} from '.\/service';\nimport {BaseModel} from '.\/traits\/baseModel';\n\nexport class Deployment extends BaseModel{\n   pods: Array;\n   services: Array;\n\n   constructor(data: any){\n       super(data);\n       this.pods = [];\n       this.services = [];\n   }\n}<\/code><\/pre>\n<p>\nAvec les getters et setters, nous pouvons afficher ou d\u00e9finir les m\u00e9triques n\u00e9cessaires des entit\u00e9s de mani\u00e8re pratique et lisible. Par exemple, la sortie format\u00e9e de l'allocatable cpu d'un n\u0153ud :<\/p>\n<pre><code class=\"plaintext\">get cpuAllocatableFormatted(){\n   let cpu = this.data.status.allocatable.cpu;\n   if(cpu.indexOf('m') &gt; -1){\n       cpu = parseInt(cpu)\/1000;\n   }\n   return cpu;\n}<\/code><\/pre>\n<p>\n<b>Pages<\/b><\/p>\n<p>La liste de toutes les pages de notre plugin est initialement d\u00e9crite dans notre pluing.json dans la section des d\u00e9pendances :<\/p>\n<p><img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/b86c74839ad0d9c64b97ffc3f2a84bdf.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDans 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\u00e9e dans module.ts) ; l'indication du r\u00f4le de l'utilisateur pour lequel l'acc\u00e8s \u00e0 cette page et aux param\u00e8tres de navigation de la barre lat\u00e9rale est disponible. <\/p>\n<p>Dans le composant responsable du fonctionnement de la page, nous devons d\u00e9finir templateUrl, en passant le chemin vers le fichier html contenant le balisage. \u00c0 l'int\u00e9rieur du contr\u00f4leur, par le biais de l'injection de d\u00e9pendance, nous pouvons acc\u00e9der \u00e0 2 services Angular importants :<\/p>\n<ul>\n<li>backendSrv \u2014 service permettant l'interaction avec l'API serveur de Grafana ;<\/li>\n<li>datasourceSrv \u2014 service permettant l'interaction locale avec toutes les sources de donn\u00e9es install\u00e9es dans votre Grafana (par exemple, la m\u00e9thode .getAll() \u2014 renvoie la liste de toutes les sources de donn\u00e9es install\u00e9es ; .get() \u2014 retourne l'objet instance d'une source de donn\u00e9es sp\u00e9cifique.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/e616e74c2e31ca3a81b7ed91ca333208.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/5e1d45f570dda50774088405abb41c9f.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/c072867623824e799518984c7b1bee93.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<h4>Partie 4 : datasource<\/h4>\n<p>\nD'un point de vue de Grafana, une source de donn\u00e9es est un plugin comme tous les autres : elle a son propre point d'entr\u00e9e module.js et un fichier de m\u00e9tadonn\u00e9es plugin.json. Lors de la cr\u00e9ation d'un plugin avec type = app, nous pouvons interagir avec les sources de donn\u00e9es existantes (par exemple, prometheus-datasource) ainsi qu'avec nos propres sources de donn\u00e9es, que nous pouvons stocker directement dans le r\u00e9pertoire du plugin (dist\/datasource\/*) ou installer comme d\u00e9pendance. Dans notre cas, la source de donn\u00e9es est fournie avec le code du plugin. Il est \u00e9galement imp\u00e9ratif d'avoir un mod\u00e8le config.html et un contr\u00f4leur ConfigCtrl, qui seront utilis\u00e9s pour la page de configuration de l'instance de la source de donn\u00e9es et le contr\u00f4leur Datasource, dans lequel la logique de fonctionnement de votre source de donn\u00e9es est r\u00e9alis\u00e9e.<\/p>\n<p>Dans le plugin KubeGraf, d'un point de vue interface utilisateur, une source de donn\u00e9es repr\u00e9sente une instance d'un cluster kubernetes, qui dispose des capacit\u00e9s suivantes (le code source est disponible <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\/tree\/master\/src\/datasource\">via le lien<\/a><\/noindex>):<\/p>\n<ul>\n<li>r\u00e9cup\u00e9ration des donn\u00e9es de l'api-server k8s (obtention de la liste des namespaces, des d\u00e9ploiements\u2026)<\/li>\n<li>proxy des requ\u00eates vers prometheus-datasource (qui est s\u00e9lectionn\u00e9 dans les param\u00e8tres du plugin pour chaque cluster sp\u00e9cifique) et formatage des r\u00e9ponses pour utiliser les donn\u00e9es \u00e0 la fois dans les pages statiques et dans les tableaux de bord.<\/li>\n<li>actualisation des donn\u00e9es sur les pages statiques du plugin (avec un temps de rafra\u00eechissement d\u00e9fini).<\/li>\n<li>traitement des requ\u00eates pour g\u00e9n\u00e9rer la liste des templates dans grafana-dashboards (m\u00e9thode .metriFindQuery())<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/18159bbb41144ebceb457df28b35e7b1.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/a62665b4409566fbe24f56f88a376d5a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/7f2d61fce623ce2a3cf62feb6d0447ac.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ul>\n<li>test de connexion avec le cluster k8s cible.<\/li>\n<\/ul>\n<pre><code class=\"plaintext\">testDatasource(){\n   let url = '\/api\/v1\/namespaces';\n   let _url = this.url;\n   if(this.accessViaToken)\n       _url += '\/__proxy';\n   _url += url;\n   return this.backendSrv.datasourceRequest({\n       url: _url,\n       method: \"GET\",\n       headers: {\"Content-Type\": 'application\/json'}\n   })\n       .then(response =&gt; {\n           if (response.status === 200) {\n               return {status: \"success\", message: \"La source de donn\u00e9es est OK\", title: \"Succ\u00e8s\"};\n           }else{\n               return {status: \"error\", message: \"La source de donn\u00e9es n'est pas OK\", title: \"Erreur\"};\n           }\n       }, error =&gt; {\n           return {status: \"error\", message: \"La source de donn\u00e9es n'est pas OK\", title: \"Erreur\"};\n       })\n}<\/code><\/pre>\n<p>\nUn point particuli\u00e8rement int\u00e9ressant, \u00e0 notre avis, est la mise en \u0153uvre du m\u00e9canisme d'authentification et d'autorisation pour le datasource. En g\u00e9n\u00e9ral, pour configurer l'acc\u00e8s \u00e0 la source de donn\u00e9es finale, nous pouvons utiliser le composant int\u00e9gr\u00e9 de Grafana \u2014 datasourceHttpSettings. Avec ce composant, nous pouvons configurer l'acc\u00e8s \u00e0 la source de donn\u00e9es http en sp\u00e9cifiant l'url et les param\u00e8tres d'authentification\/autorisation de base : identifiant-mot de passe, ou client-cert\/client-key. Pour permettre la configuration d'acc\u00e8s \u00e0 l'aide d'un jeton bearer (un standard de fait pour k8s), il a fallu bricoler un peu.<\/p>\n<p>Pour r\u00e9soudre ce probl\u00e8me, nous pouvons utiliser le m\u00e9canisme int\u00e9gr\u00e9 de Grafana \u00abPlugin Routes\u00bb (voir plus sur <noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/docs\/plugins\/developing\/auth-for-datasources\/\">la page officielle de documentation<\/a><\/noindex>). Dans les param\u00e8tres de notre datasource, nous pouvons d\u00e9clarer un ensemble de r\u00e8gles de routage qui seront trait\u00e9es par le serveur proxy de Grafana. Par exemple, pour chaque endpoint, il est possible de sp\u00e9cifier des en-t\u00eates ou des url avec des options de template, dont les donn\u00e9es peuvent \u00eatre extraites des champs jsonData et secureJsonData (pour stocker des mots de passe ou des jetons sous forme crypt\u00e9e). Dans notre exemple, les requ\u00eates du type <i>\/__proxy\/api\/v1\/namespaces<\/i> seront proxy\u00e9es vers une url du type<br \/>\n\/api\/v1\/namespaces avec l'en-t\u00eate Authorization: Bearer.<\/p>\n<p><img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/ca73ff0fef62fba3edab7907c83639b6.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/9dab7537d7ccba90c9fb62dfa70b9fe2.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNaturellement, pour travailler avec l'api-server k8s, nous avons besoin d'un utilisateur avec des acc\u00e8s en lecture seule, et vous pouvez \u00e9galement trouver les manifestes pour sa cr\u00e9ation dans <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\/tree\/master\/kubernetes\/\">le code source du plugin<\/a><\/noindex>.<\/p>\n<h4>Partie 5 : sortie<\/h4>\n<p>\n<img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/3991a5d412f362770157fc567bb8009c.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nApr\u00e8s avoir \u00e9crit votre propre plugin pour Grafana, vous voudrez naturellement le rendre public. Dans Grafana, il existe une biblioth\u00e8que de plugins disponible \u00e0 l'adresse <noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/grafana\/plugins\">grafana.com\/grafana\/plugins<\/a><\/noindex> <\/p>\n<p>Pour que votre plugin soit disponible dans la boutique officielle, vous devez faire une PR dans <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/grafana\/grafana-plugin-repository\">ce d\u00e9p\u00f4t<\/a><\/noindex>, en ajoutant dans le fichier repo.json un contenu du type :<\/p>\n<p><img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/160650bf37f8f2f3e12ef7dd291e90eb.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\no\u00f9 version \u2014 version de votre plugin, url \u2014 lien vers le d\u00e9p\u00f4t, et commit \u2014 hash du commit par lequel une version sp\u00e9cifique du plugin sera disponible. <\/p>\n<p>Et \u00e0 la fin, vous verrez une belle image du type :<\/p>\n<p><img decoding=\"async\" alt=\"D\u00e9veloppement d&#039;un plugin pour Grafana : histoire des obstacles rencontr\u00e9s\" src=\"\/wp-content\/uploads\/2019\/11\/ff906396596e9c4c15c59f8d53555f52.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nLes donn\u00e9es pour cela seront automatiquement extraites de votre Readme.md, Changelog.md et du fichier plugin.json d\u00e9crivant le plugin.<\/p>\n<h4>Partie 6 : en guise de conclusion<\/h4>\n<p>\nNous n'avons pas arr\u00eat\u00e9 le d\u00e9veloppement de notre plugin apr\u00e8s sa sortie. Nous travaillons actuellement sur une surveillance pr\u00e9cise de l'utilisation des ressources des n\u0153uds du cluster, l'ajout de nouvelles fonctionnalit\u00e9s pour am\u00e9liorer l'exp\u00e9rience utilisateur, et nous traitons un grand nombre de retours re\u00e7us apr\u00e8s l'installation du plugin, tant de nos clients que des issues sur GitHub (si vous laissez votre issue ou pull request, je serai tr\u00e8s heureux \ud83d\ude42 ).<\/p>\n<p>Nous esp\u00e9rons que cet article vous aidera \u00e0 mieux comprendre cet outil merveilleux qu'est Grafana et, peut-\u00eatre, \u00e0 r\u00e9diger votre propre plugin. <\/p>\n<p>Merci !)<br \/>\n<br \/>Source : <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/itsumma\/blog\/474500\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0435\u0441\u044f\u0446\u0435\u0432 \u043d\u0430\u0437\u0430\u0434 \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043b\u0438 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d \u043d\u0430\u0448 \u043d\u043e\u0432\u044b\u0439 open-source \u043f\u0440\u043e\u0435\u043a\u0442 \u2014 Grafana-\u043f\u043b\u0430\u0433\u0438\u043d \u0434\u043b\u044f \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 kubernetes, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u0437\u0432\u0430\u043b\u0438 DevOpsProdigy KubeGraf. \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u043d\u0430 GitHub. \u0410 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u0432\u0430\u043c\u0438 \u0438\u0441\u0442\u043e\u0440\u0438\u0435\u0439 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u043b\u0438 \u043f\u043b\u0430\u0433\u0438\u043d, \u043a\u0430\u043a\u0438\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0438 \u0441 \u043a\u0430\u043a\u0438\u043c\u0438 \u043f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u043c\u0438 \u043a\u0430\u043c\u043d\u044f\u043c\u0438 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-52343","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442!\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"fr_FR\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0434\u043b\u044f Grafana: \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u043d\u0430\u0431\u0438\u0442\u044b\u0445 \u0448\u0438\u0448\u0435\u043a | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442!\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-11-05T21:00:00+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-02-18T11:00:02+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47D\u00e9veloppement d'un plugin pour Grafana : histoire des erreurs rencontr\u00e9es | ProHoster","description":"Bonjour \u00e0 tous !","canonical_url":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"fr_FR","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0434\u043b\u044f Grafana: \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u043d\u0430\u0431\u0438\u0442\u044b\u0445 \u0448\u0438\u0448\u0435\u043a | ProHoster","og:description":"\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442!","og:url":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-11-05T21:00:00+00:00","article:modified_time":"2020-02-18T11:00:02+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"52343","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-24 03:18:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 20:45:28","updated":"2026-01-24 03:18:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts\/52343","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/comments?post=52343"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts\/52343\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media?parent=52343"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/categories?post=52343"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/tags?post=52343"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}