{"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\/nl\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek","title":{"rendered":"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Hallo allemaal! Enkele maanden geleden hebben we ons nieuwe open-source project in productie genomen \u2014 een Grafana-plugin voor het monitoren van Kubernetes, die we hebben genoemd <b><noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/grafana\/plugins\/devopsprodigy-kubegraf-app\">DevOpsProdigy KubeGraf<\/a><\/noindex><\/b>. De broncode van de plugin is beschikbaar in <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\">het openbare repository op GitHub<\/a><\/noindex>. In dit artikel willen we onze ervaring delen over hoe we de plugin hebben ontwikkeld, welke tools we hebben gebruikt en met welke obstakels we tijdens het ontwikkelingsproces zijn geconfronteerd. Laten we beginnen!<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h4>Deel 0 \u2014 inleiding: hoe zijn we hier gekomen?<\/h4>\n<p>\nHet idee om onze eigen plugin voor Grafana te schrijven ontstond volkomen toevallig. Ons bedrijf houdt zich al meer dan 10 jaar bezig met het monitoren van webprojecten van verschillende complexiteitsniveaus. In de loop van deze tijd hebben we veel expertise, interessante casestudy's en ervaring met het gebruik van verschillende monitoringsystemen opgedaan. En op een gegeven moment vroegen we ons af: 'Bestaat er een magische tool voor het monitoren van Kubernetes, zodat je het kunt insteken en vergeten?'... De standaard voor het monitoren van k8s is uiteraard al lang de combinatie van Prometheus + Grafana. Voor deze stack bestaan verschillende kant-en-klare oplossingen: prometheus-operator, een set dashboards kubernetes-mixin, grafana-kubernetes-app. <\/p>\n<p>De meest interessante optie voor ons leek de grafana-kubernetes-app plugin, maar deze wordt al meer dan een jaar niet meer ondersteund en kan bovendien niet werken met nieuwe versies van node-exporter en kube-state-metrics. Op een gegeven moment besloten we: 'Waarom maken we niet onze eigen oplossing?'<\/p>\n<p>Welke idee\u00ebn hebben we besloten te implementeren in onze plugin:<\/p>\n<ul>\n<li>visualisatie van de 'applicatiekaart': een handige weergave van applicaties in de cluster, gegroepeerd op namespace, deployments\u2026;<\/li>\n<li>visualisatie van de relaties in de vorm van 'deployment \u2014 service (+ports)'.<\/li>\n<li>visualisatie van de distributie van applicaties in de cluster over de nodes van de cluster.<\/li>\n<li>verzamelen van metrics en informatie uit meerdere bronnen: Prometheus en k8s api server.<\/li>\n<li>monitoring van zowel de infrastructuur (gebruik van CPU-tijd, geheugen, opslag, netwerk) als de logica van de applicaties \u2014 health-status van pods, aantal beschikbare replicas, informatie over liveness\/readyness probes.<\/li>\n<\/ul>\n<p><\/p>\n<h4>Deel 1: Wat is een 'plugin voor Grafana'?<\/h4>\n<p>\nTechnisch gezien is een plugin voor Grafana een Angular-controller die wordt opgeslagen in de data-directory van Grafana (<i>\/var\/grafana\/plugins\/&lt;your_plugin_name&gt;\/dist\/module.js<\/i>) en kan worden geladen als een SystemJS-module. Daarnaast moet er in deze directory een bestand plugin.json aanwezig zijn, dat alle metadata van je plugin bevat: naam, versie, type plugin, links naar repository\/site\/licentie, afhankelijkheden, enzovoort.<\/p>\n<p><img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" 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=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/1e5b780837e5a9385e5e22a27d83f567.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>plugin.json<\/i><\/p>\n<p>Zoals te zien is op de screenshot, hebben we plugin.type = app opgegeven. Plugins voor Grafana kunnen van drie soorten zijn:<\/p>\n<p><b>paneel<\/b>: het meest voorkomende type plugin \u2013 stelt een paneel voor voor de visualisatie van bepaalde metrics, wordt gebruikt voor het maken van verschillende dashboards.<br \/>\n<b>datasource<\/b>: een plugin-connector naar een bepaalde dataset (bijvoorbeeld Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).<br \/>\n<b>app<\/b>: een plugin waarmee je je eigen frontend-applicatie binnen Grafana kunt bouwen, je eigen html-pagina's kunt maken en handmatig naar datasource kunt verwijzen voor de visualisatie van verschillende gegevens. Ook kunnen plugins van andere types (datasource, paneel) en verschillende dashboards als afhankelijkheden worden gebruikt.<\/p>\n<p><img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/3cd53c7c3a2635f3f20831d52198b5f0.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Voorbeeld van afhankelijkheden van een plugin met type = app<\/i>.<\/p>\n<p>Als programmeertaal kan zowel JavaScript als TypeScript worden gebruikt (wij hebben voor deze laatste gekozen). Vooraf gemaakte starters voor hello-world plugins van elk type kun je <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/CorpGlory\">vinden via de link<\/a><\/noindex>: in deze repository is een grote hoeveelheid starter-packs beschikbaar (er is zelfs een experimenteel voorbeeld van een plugin in React) met vooraf ge\u00efnstalleerde en geconfigureerde bundlers. <\/p>\n<h4>Deel 2: voorbereiding van de lokale omgeving<\/h4>\n<p>\nVoor het werken aan de plugin hebben we natuurlijk een Kubernetes-cluster nodig met alle vooraf ge\u00efnstalleerde tools: prometheus, node-exporter, kube-state-metrics, grafana. De omgeving moet snel, eenvoudig en ongecompliceerd worden opgezet, en om hot-reload mogelijk te maken, moet de data-directory van Grafana direct vanaf de ontwikkelaarsmachine worden gemonteerd.<\/p>\n<p>De meest handige manier om lokaal met Kubernetes te werken, naar onze mening, is <noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/tasks\/tools\/install-minikube\/\">minikube<\/a><\/noindex>. De volgende stap is het installeren van de koppeling Prometheus + Grafana met behulp van prometheus-operator. In <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/faun\/trying-prometheus-operator-with-helm-minikube-b617a2dccfa3\">dit artikel<\/a><\/noindex> wordt het installatieproces van prometheus-operator op minikube uitgebreid beschreven. Om persistentie in te schakelen, moet de parameter <i>persistence: true<\/i> in het bestand charts\/grafana\/values.yaml worden ingesteld, je eigen PV en PVC toevoegen en deze opgeven in de parameter persistence.existingClaim<\/p>\n<p>Het uiteindelijke script voor het opstarten van minikube ziet er als volgt uit:<\/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>Deel 3: ontwikkeling<\/h4>\n<p>\n<b>Objectmodel<\/b><\/p>\n<p>Als voorbereiding op de implementatie van de plugin hebben we besloten om alle basisentiteiten van Kubernetes te beschrijven waarmee we zullen werken in de vorm van TypeScript-klassen: pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Elke van deze klassen is afgeleid van de algemene klasse BaseModel, waarin de constructor, destructor, en methoden voor het bijwerken en wisselen van zichtbaarheid zijn beschreven. In elk van de klassen zijn geneste relaties met andere entiteiten beschreven, bijvoorbeeld de lijst van pods bij de entiteit van het 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>\nMet behulp van getters en setters kunnen we de benodigde metrics van entiteiten op een handige en leesbare manier weergeven of instellen. Bijvoorbeeld, de geformatteerde output van allocatable cpu van een node:<\/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>Pagina's<\/b><\/p>\n<p>De lijst van alle pagina's van onze plugin wordt in eerste instantie beschreven in ons pluing.json in het onderdeel afhankelijkheden:<\/p>\n<p><img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/b86c74839ad0d9c64b97ffc3f2a84bdf.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIn het blok voor elke pagina moeten we de NAAM VAN DE PAGINA opgeven (deze wordt vervolgens omgezet in een slug, waardoor deze pagina toegankelijk is); de naam van de component die verantwoordelijk is voor deze pagina (de lijst van componenten wordt ge\u00ebxporteerd in module.ts); de rol van de gebruiker voor wie toegang is tot het werken met deze pagina en de navigatie-instellingen voor de zijbalk. <\/p>\n<p>In de component die verantwoordelijk is voor de pagina moeten we templateUrl instellen, waarbij we het pad naar het html-bestand met de markup doorgeven. Binnen de controller hebben we via dependency injection toegang tot twee belangrijke Angular-diensten:<\/p>\n<ul>\n<li>backendSrv \u2014 de service die zorgt voor interactie met de Grafana API-server;<\/li>\n<li>datasourceSrv \u2014 de service die zorgt voor lokale interactie met alle datasource die in uw Grafana zijn ge\u00efnstalleerd (bijvoorbeeld, de methode .getAll() \u2014 retourneert een lijst van alle ge\u00efnstalleerde datasources; .get() \u2014 retourneert het object-instantie van een specifieke datasource.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/e616e74c2e31ca3a81b7ed91ca333208.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/5e1d45f570dda50774088405abb41c9f.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/c072867623824e799518984c7b1bee93.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<h4>Deel 4: datasource<\/h4>\n<p>\nVanuit het perspectief van Grafana is een datasource precies hetzelfde als elke andere plug-in: het heeft zijn eigen toegangspunt module.js en er is een bestand met meta-informatie plugin.json. Bij het ontwikkelen van een plug-in met type = app kunnen we zowel interfacing met bestaande datasources (zoals prometheus-datasource) als met onze eigen datasources, die we direct in de plug-in directory (dist\/datasource\/*) kunnen opslaan of als afhankelijkheid kunnen installeren. In ons geval wordt de datasource geleverd met de code van de plug-in. Ook is een sjabloon config.html en een controller ConfigCtrl vereist, die zullen worden gebruikt voor de configuratiepagina van de datasource-instantie en de datasource-controller, waarin de logica voor de werking van uw datasource is ge\u00efmplementeerd.<\/p>\n<p>In de KubeGraf-plug-in, vanuit het perspectief van de gebruikersinterface, is een datasource een instantie van een Kubernetes-cluster waarin de volgende mogelijkheden zijn ge\u00efmplementeerd (de broncode is beschikbaar <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\/tree\/master\/src\/datasource\">via de link<\/a><\/noindex>):<\/p>\n<ul>\n<li>gegevens ophalen uit de api-server van k8s (vermelding van namespaces, deployments...)<\/li>\n<li>verzoekproxies naar prometheus-datasource (die in de plug-in instellingen voor elk specifiek cluster wordt geselecteerd) en antwoorden formatteren voor gebruik van gegevens zowel in statische pagina's als in dashboards.<\/li>\n<li>gegevens bijwerken op statische pagina's van de plug-in (met een ingesteld refresh rate).<\/li>\n<li>verwerking van verzoeken voor het genereren van een template-lijst in grafana-dashboards (methode .metriFindQuery())<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/18159bbb41144ebceb457df28b35e7b1.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/a62665b4409566fbe24f56f88a376d5a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/7f2d61fce623ce2a3cf62feb6d0447ac.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ul>\n<li>test van de verbinding met het eindige k8s-cluster.<\/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: \"Databron is OK\", title: \"Succes\"};\n           }else{\n               return {status: \"error\", message: \"Databron is niet OK\", title: \"Fout\"};\n           }\n       }, error =&gt; {\n           return {status: \"error\", message: \"Databron is niet OK\", title: \"Fout\"};\n       })\n}<\/code><\/pre>\n<p>\nEen interessant aspect is naar onze mening de implementatie van het authenticatie- en autorisatiemechanisme voor de datasource. Gewoonlijk kunnen we voor de toegang tot de einddatabron de ingebouwde component van Grafana gebruiken - datasourceHttpSettings. Met deze component kunnen we de toegang tot de http-databron configureren door de url en basisinstellingen voor authenticatie\/autorisatie op te geven: gebruikersnaam-wachtwoord, of client-cert\/client-key. Om toegang met behulp van een bearer-token (de facto standaard voor k8s) mogelijk te maken, moest er een beetje 'geknutseld' worden.<\/p>\n<p>Voor het oplossen van deze taak kan de ingebouwde mechanismen van Grafana 'Plugin Routes' worden gebruikt (meer hierover op <noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/docs\/plugins\/developing\/auth-for-datasources\/\">de offici\u00eble documentatiepagina<\/a><\/noindex>). In de instellingen van onze datasource kunnen we een set routeringsregels declareren die door de proxyserver van Grafana zullen worden verwerkt. Bijvoorbeeld, voor elke afzonderlijke endpoint is er de mogelijkheid om headers of url's op te geven met sjablonering, waarbij de gegevens afkomstig kunnen zijn uit de jsonData en secureJsonData-velden (voor het veilig opslaan van wachtwoorden of tokens in versleutelde vorm). In ons voorbeeld zullen aanvragen van de vorm <i>\/__proxy\/api\/v1\/namespaces<\/i> worden geproxied naar een url van de vorm<br \/>\n\/api\/v1\/namespaces met de header Authorization: Bearer.<\/p>\n<p><img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/ca73ff0fef62fba3edab7907c83639b6.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/9dab7537d7ccba90c9fb62dfa70b9fe2.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNatuurlijk hebben we voor de interactie met de k8s api-server een gebruiker met alleen-lezen toegang nodig; de manifesten voor het cre\u00ebren daarvan kunt u ook vinden in <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\/tree\/master\/kubernetes\/\">de broncode van de plugin<\/a><\/noindex>.<\/p>\n<h4>Deel 5: release<\/h4>\n<p>\n<img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/3991a5d412f362770157fc567bb8009c.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNadat je je eigen plugin voor Grafana hebt geschreven, wil je deze uiteraard openbaar maken. In Grafana is er een pluginbibliotheek beschikbaar via de link <noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/grafana\/plugins\">grafana.com\/grafana\/plugins<\/a><\/noindex> <\/p>\n<p>Om je plugin beschikbaar te maken in de offici\u00eble winkel, moet je een PR indienen in <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/grafana\/grafana-plugin-repository\">deze repository<\/a><\/noindex>, door inhoud van de volgende vorm aan het bestand repo.json toe te voegen:<\/p>\n<p><img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/160650bf37f8f2f3e12ef7dd291e90eb.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nwaarbij version - de versie van je plugin is, url - de link naar de repository, en commit - de hash van de commit waarmee de specifieke versie van de plugin beschikbaar zal zijn. <\/p>\n<p>En aan het einde zul je een prachtig plaatje zien dat eruitziet als:<\/p>\n<p><img decoding=\"async\" alt=\"Ontwikkeling van een plugin voor Grafana: de geschiedenis van de uitdagingen die we tegenkwamen\" src=\"\/wp-content\/uploads\/2019\/11\/ff906396596e9c4c15c59f8d53555f52.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDe gegevens hiervoor worden automatisch verzameld uit je Readme.md, Changelog.md en het bestand plugin.json met de beschrijving van de plugin.<\/p>\n<h4>Deel 6: conclusie<\/h4>\n<p>\nWe hebben de ontwikkeling van onze plugin na de release niet stopgezet. Op dit moment werken we aan een correcte monitoring van het gebruik van de cluster nodes, de implementatie van nieuwe functies voor een verbeterde gebruikerservaring, en we verwerken een groot aantal reacties die we na de installatie van de plugin hebben ontvangen, zowel van onze klanten als via issues op GitHub (als je je issue of pull request achterlaat, ben ik daar erg blij mee \ud83d\ude42 ).<\/p>\n<p>We hopen dat dit artikel je helpt om de prachtige tool Grafana te begrijpen en mogelijk je eigen plugin te schrijven. <\/p>\n<p>Dank je! :)<br \/>\n<br \/>Bron: <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.3 - 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\/nl\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"nl_NL\" \/>\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\/nl\/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\udd47 Ontwikkeling van een plugin voor Grafana: het verhaal van de geleerde lessen | ProHoster","description":"Hallo allemaal!","canonical_url":"https:\/\/prohoster.info\/nl\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"nl_NL","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\/nl\/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\/nl\/wp-json\/wp\/v2\/posts\/52343","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/comments?post=52343"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts\/52343\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media?parent=52343"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/categories?post=52343"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/tags?post=52343"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}