{"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\/pl\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek","title":{"rendered":"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Cze\u015b\u0107 wszystkim! Kilka miesi\u0119cy temu uruchomili\u015bmy w produkcji nasz nowy projekt open-source \u2014 plugin Grafana do monitorowania Kubernetes, kt\u00f3ry nazwali\u015bmy <b><noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/grafana\/plugins\/devopsprodigy-kubegraf-app\">DevOpsProdigy KubeGraf<\/a><\/noindex><\/b>. \u0179r\u00f3d\u0142o kodu pluginu jest dost\u0119pne w <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\">publicznym repozytorium na GitHubie<\/a><\/noindex>. A w tym artykule chcemy podzieli\u0107 si\u0119 z wami histori\u0105 o tym, jak stworzyli\u015bmy plugin, jakich narz\u0119dzi u\u017cywali\u015bmy i z jakimi problemami napotkali\u015bmy si\u0119 w procesie rozwoju. Zaczynajmy!<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h4>Cz\u0119\u015b\u0107 0 \u2014 wprowadzenie: jak doszli\u015bmy do tego momentu?<\/h4>\n<p>\nPomys\u0142 napisania w\u0142asnego pluginu dla Grafany wpad\u0142 nam zupe\u0142nie przypadkowo. Nasza firma od ponad 10 lat zajmuje si\u0119 monitorowaniem projekt\u00f3w internetowych o r\u00f3\u017cnym stopniu z\u0142o\u017cono\u015bci. W tym czasie zdobyt\u0105 du\u017c\u0105 wiedz\u0119 oraz ciekawe przypadki u\u017cycia r\u00f3\u017cnych system\u00f3w monitorowania. I w pewnym momencie zacz\u0119li\u015bmy si\u0119 zastanawia\u0107: \u201eCzy istnieje magiczne narz\u0119dzie do monitorowania Kubernetes, kt\u00f3re mo\u017cna by po prostu \u201ezainstalowa\u0107 i zapomnie\u0107\u201d?\u201d.. Standardem przemys\u0142owym dla monitorowania k8s jest oczywi\u015bcie zestaw Prometheus + Grafana. Jako gotowe rozwi\u0105zania dla tego stosu istnieje wiele r\u00f3\u017cnorodnych narz\u0119dzi: prometheus-operator, zestaw dashboard\u00f3w kubernetes-mixin, grafana-kubernetes-app. <\/p>\n<p>Najbardziej interesuj\u0105c\u0105 opcj\u0105 dla nas wydawa\u0142 si\u0119 plugin grafana-kubernetes-app, ale nie jest on ju\u017c wspierany od ponad roku i, co wi\u0119cej, nie dzia\u0142a z nowymi wersjami node-exportera i kube-state-metrics. I w pewnym momencie postanowili\u015bmy: \u201eA mo\u017ce stworzy\u0107 w\u0142asne rozwi\u0105zanie?\u201d<\/p>\n<p>Jakie pomys\u0142y postanowili\u015bmy zrealizowa\u0107 w naszym pluginie:<\/p>\n<ul>\n<li>wizualizacja \u201emapy aplikacji\u201d: wygodne przedstawienie aplikacji w klastrze, pogrupowanych wed\u0142ug namespace\u2019\u00f3w, deployment\u2019\u00f3w\u2026;<\/li>\n<li>wizualizacja powi\u0105za\u0144 typu \u201edeployment \u2014 service (+ports)\u201d.<\/li>\n<li>wizualizacja rozmieszczenia aplikacji klastra na nodach klastra.<\/li>\n<li>zbieranie metryk i informacji z kilku \u017ar\u00f3de\u0142: Prometheus i k8s API server.<\/li>\n<li>monitorowanie zar\u00f3wno cz\u0119\u015bci infrastrukturalnej (u\u017cycie czasu procesora, pami\u0119ci, subsystemu dyskowego, sieci), jak i logiki aplikacji \u2014 stan zdrowia pod\u00f3w, liczba dost\u0119pnych replik, informacje o przeprowadzeniu test\u00f3w liveness\/readiness.<\/li>\n<\/ul>\n<p><\/p>\n<h4>Cz\u0119\u015b\u0107 1: Czym jest \u201eplugin do Grafany\u201d?<\/h4>\n<p>\nZ technicznego punktu widzenia, plugin do Grafany to kontroler Angular, kt\u00f3ry jest przechowywany w katalogu data Grafany (<i>\/var\/grafana\/plugins\/&lt;your_plugin_name&gt;\/dist\/module.js<\/i>) i mo\u017ce by\u0107 za\u0142adowany jako modu\u0142 SystemJS. R\u00f3wnie\u017c w tym katalogu powinien znajdowa\u0107 si\u0119 plik plugin.json, zawieraj\u0105cy wszystkie metainformacje o twoim wtyczce: nazwa, wersja, typ wtyczki, linki do repozytorium\/strony\/licencji, zale\u017cno\u015bci i tak dalej.<\/p>\n<p><img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" 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=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/1e5b780837e5a9385e5e22a27d83f567.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>plugin.json<\/i><\/p>\n<p>Jak wida\u0107 na zrzucie ekranu, wskazali\u015bmy plugin.type = app. Poniewa\u017c wtyczki dla Grafany mog\u0105 by\u0107 trzech rodzaj\u00f3w:<\/p>\n<p><b>panel<\/b>: najpopularniejszy typ wtyczek \u2014 stanowi panel do wizualizacji r\u00f3\u017cnych metryk, u\u017cywany do tworzenia r\u00f3\u017cnych dashboard\u00f3w.<br \/>\n<b>\u017ar\u00f3d\u0142o danych<\/b>: wtyczka-konektor do jakiego\u015b \u017ar\u00f3d\u0142a danych (na przyk\u0142ad, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).<br \/>\n<b>app<\/b>: wtyczka, kt\u00f3ra pozwala ci zbudowa\u0107 w\u0142asn\u0105 aplikacj\u0119 front-endow\u0105 w Grafanie, tworzy\u0107 w\u0142asne strony html i r\u0119cznie odwo\u0142ywa\u0107 si\u0119 do datasource w celu wizualizacji r\u00f3\u017cnych danych. Jako zale\u017cno\u015bci mog\u0105 by\u0107 r\u00f3wnie\u017c u\u017cywane wtyczki innych typ\u00f3w (datasource, panel) oraz r\u00f3\u017cne dashboardy.<\/p>\n<p><img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/3cd53c7c3a2635f3f20831d52198b5f0.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Przyk\u0142ad zale\u017cno\u015bci wtyczki typu app<\/i>.<\/p>\n<p>Jako j\u0119zyka programowania mo\u017cna u\u017cywa\u0107 zar\u00f3wno JavaScript, jak i TypeScript (my wybrali\u015bmy ten drugi). Szablony dla wtyczek hello-world ka\u017cdego typu mo\u017cna <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/CorpGlory\">znale\u017a\u0107 pod tym linkiem<\/a><\/noindex>: w tym repozytorium znajduje si\u0119 wiele starter-pack\u00f3w (jest nawet eksperymentalny przyk\u0142ad wtyczki w React) z preinstalowanymi i skonfigurowanymi narz\u0119dziami buduj\u0105cymi. <\/p>\n<h4>Cz\u0119\u015b\u0107 2: przygotowanie lokalnego \u015brodowiska<\/h4>\n<p>\nDo pracy nad wtyczk\u0105 potrzebujemy oczywi\u015bcie klastra kubernetes z wszystkimi preinstalowanymi narz\u0119dziami: prometheus, node-exporter, kube-state-metrics, grafana. \u015arodowisko powinno by\u0107 konfigurowane szybko, \u0142atwo i bezproblemowo, a dla zapewnienia hot-reload katalog danych Grafany powinien by\u0107 montowany bezpo\u015brednio z maszyny dewelopera.<\/p>\n<p>Najwygodniejszym, naszym zdaniem, sposobem lokalnej pracy z kubernetes jest <noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/tasks\/tools\/install-minikube\/\">minikube<\/a><\/noindex>. Nast\u0119pnym krokiem jest zainstalowanie zestawu Prometheus + Grafana za pomoc\u0105 prometheus-operator. W <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/faun\/trying-prometheus-operator-with-helm-minikube-b617a2dccfa3\">tym artykule<\/a><\/noindex> dok\u0142adnie opisano proces instalacji prometheus-operator na minikube. Aby w\u0142\u0105czy\u0107 persistentness, nale\u017cy ustawi\u0107 parametr <i>persistence: true<\/i> w pliku charts\/grafana\/values.yaml, doda\u0107 w\u0142asny PV i PVC oraz wskaza\u0107 je w parametrze persistence.existingClaim<\/p>\n<p>Ostateczny skrypt uruchomienia minikube wygl\u0105da nast\u0119puj\u0105co:<\/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>Cz\u0119\u015b\u0107 3: rozw\u00f3j<\/h4>\n<p>\n<b>Model obiektowy<\/b><\/p>\n<p>W ramach przygotowa\u0144 do realizacji wtyczki postanowili\u015bmy opisa\u0107 wszystkie podstawowe encje Kubernetes, z kt\u00f3rymi b\u0119dziemy pracowa\u0107, jako klasy TypeScript: pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Ka\u017cda z tych klas dziedziczy po wsp\u00f3lnej klasie BaseModel, w kt\u00f3rej opisano konstruktor, destruktor, metody do aktualizacji i zmiany widoczno\u015bci. W ka\u017cdej z klas opisano zagnie\u017cd\u017cone relacje z innymi encjami, na przyk\u0142ad lista pod\u00f3w w encji typu 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>\nDzi\u0119ki getterom i setterom mo\u017cemy wy\u015bwietla\u0107 lub ustawia\u0107 potrzebne nam metryki encji w wygodny i czytelny spos\u00f3b. Na przyk\u0142ad sformatowane wyj\u015bcie allocatable cpu w\u0119z\u0142a:<\/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>Strony<\/b><\/p>\n<p>Lista wszystkich stron naszej wtyczki jest pocz\u0105tkowo opisana w naszym pluing.json w sekcji zale\u017cno\u015bci:<\/p>\n<p><img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/b86c74839ad0d9c64b97ffc3f2a84bdf.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nW bloku dla ka\u017cdej strony musimy okre\u015bli\u0107 NAZW\u0118 STRONY (kt\u00f3ra p\u00f3\u017aniej zostanie skonwertowana na slug, pod kt\u00f3rym ta strona b\u0119dzie dost\u0119pna); nazw\u0119 komponentu odpowiedzialnego za dzia\u0142anie tej strony (lista komponent\u00f3w jest eksportowana w module.ts); wskazanie roli u\u017cytkownika, dla kt\u00f3rego dost\u0119pna jest praca z t\u0105 stron\u0105 oraz ustawienia nawigacji dla paska bocznego. <\/p>\n<p>W komponencie odpowiedzialnym za prac\u0119 strony musimy ustawi\u0107 templateUrl, przekazuj\u0105c tam \u015bcie\u017ck\u0119 do pliku html z szablonem. Wewn\u0105trz kontrolera, za pomoc\u0105 dependency injection, mo\u017cemy uzyska\u0107 dost\u0119p do 2 wa\u017cnych serwis\u00f3w angular:<\/p>\n<ul>\n<li>backendSrv \u2014 serwis zapewniaj\u0105cy interakcj\u0119 z api-serwerem Grafany;<\/li>\n<li>datasourceSrv \u2014 serwis zapewniaj\u0105cy lokaln\u0105 interakcj\u0119 ze wszystkimi datasource, kt\u00f3re s\u0105 zainstalowane w twojej Grafanie (na przyk\u0142ad metoda .getAll() \u2014 zwraca list\u0119 wszystkich zainstalowanych datasource'\u00f3w; .get() \u2014 zwraca obiekt-instancj\u0119 konkretnego datasource.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/e616e74c2e31ca3a81b7ed91ca333208.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/5e1d45f570dda50774088405abb41c9f.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/c072867623824e799518984c7b1bee93.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<h4>Cz\u0119\u015b\u0107 4: datasource<\/h4>\n<p>\nZ perspektywy Grafana, \u017ar\u00f3d\u0142o danych (datasource) jest dok\u0142adnie takim samym wtyczk\u0105 jak wszystkie inne: ma sw\u00f3j punkt wej\u015bcia module.js oraz plik z metainformacjami plugin.json. Podczas opracowywania wtyczki z type = app, mo\u017cemy wsp\u00f3\u0142dzia\u0142a\u0107 zar\u00f3wno z ju\u017c istniej\u0105cymi \u017ar\u00f3d\u0142ami danych (takimi jak prometheus-datasource), jak i naszymi w\u0142asnymi, kt\u00f3re mo\u017cemy przechowywa\u0107 bezpo\u015brednio w katalogu wtyczki (dist\/datasource\/*) lub instalowa\u0107 jako zale\u017cno\u015b\u0107. W naszym przypadku \u017ar\u00f3d\u0142o danych dostarczane jest razem z kodem wtyczki. Obowi\u0105zkowo musi by\u0107 tak\u017ce obecny szablon config.html oraz kontroler ConfigCtrl, kt\u00f3re b\u0119d\u0105 u\u017cywane na stronie konfiguracji instancji \u017ar\u00f3d\u0142a danych oraz kontrolera Datasource, w kt\u00f3rym realizuje si\u0119 logika dzia\u0142ania naszego \u017ar\u00f3d\u0142a danych.<\/p>\n<p>W wtyczce KubeGraf, z punktu widzenia interfejsu u\u017cytkownika, \u017ar\u00f3d\u0142o danych (datasource) stanowi instancj\u0119 klastra kubernetes, w kt\u00f3rym zrealizowane s\u0105 nast\u0119puj\u0105ce mo\u017cliwo\u015bci (kod \u017ar\u00f3d\u0142owy jest dost\u0119pny) <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\/tree\/master\/src\/datasource\">pod tym linkiem<\/a><\/noindex>):<\/p>\n<ul>\n<li>pobieranie danych z api-servera k8s (uzyskiwanie listy namespace'\u00f3w, deployment\u00f3w\u2026)<\/li>\n<li>przekazywanie zapyta\u0144 do prometheus-datasource (kt\u00f3ry jest wybierany w ustawieniach wtyczki dla ka\u017cdego konkretnego klastra) oraz formatowanie odpowiedzi w celu wykorzystania danych zar\u00f3wno na stronach statycznych, jak i w pulpitach nawigacyjnych.<\/li>\n<li>aktualizacja danych na statycznych stronach wtyczki (z ustalonym czasem od\u015bwie\u017cania).<\/li>\n<li>przetwarzanie zapyta\u0144 w celu utworzenia listy szablon\u00f3w w grafana-dashboards (metoda .metriFindQuery())<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/18159bbb41144ebceb457df28b35e7b1.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/a62665b4409566fbe24f56f88a376d5a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/7f2d61fce623ce2a3cf62feb6d0447ac.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ul>\n<li>testowanie po\u0142\u0105czenia z ko\u0144cowym klastrem k8s.<\/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: \"\u0179r\u00f3d\u0142o danych jest OK\", title: \"Sukces\"};\n           }else{\n               return {status: \"error\", message: \"\u0179r\u00f3d\u0142o danych nie jest OK\", title: \"B\u0142\u0105d\"};\n           }\n       }, error =&gt; {\n           return {status: \"error\", message: \"\u0179r\u00f3d\u0142o danych nie jest OK\", title: \"B\u0142\u0105d\"};\n       })\n}<\/code><\/pre>\n<p>\nCiekawym aspektem, naszym zdaniem, jest realizacja mechanizmu uwierzytelniania i autoryzacji dla \u017ar\u00f3d\u0142a danych. Zazwyczaj, aby skonfigurowa\u0107 dost\u0119p do ko\u0144cowego \u017ar\u00f3d\u0142a danych, mo\u017cemy u\u017cy\u0107 wbudowanego komponentu Grafana \u2014 datasourceHttpSettings. Dzi\u0119ki temu komponentowi mo\u017cemy skonfigurowa\u0107 dost\u0119p do http-\u017ar\u00f3d\u0142a danych, wskazuj\u0105c adres URL oraz podstawowe ustawienia uwierzytelniania\/autoryzacji: login-has\u0142o lub client-cert\/client-key. Aby zrealizowa\u0107 mo\u017cliwo\u015b\u0107 skonfigurowania dost\u0119pu za pomoc\u0105 tokena typu bearer (de facto standard dla k8s), musieli\u015bmy nieco \u201epohimichowa\u0107\u201d.<\/p>\n<p>Aby rozwi\u0105za\u0107 ten problem, mo\u017cna u\u017cy\u0107 wbudowanego mechanizmu Grafana \u201ePlugin Routes\u201d (wi\u0119cej na <noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/docs\/plugins\/developing\/auth-for-datasources\/\">oficjalnej stronie dokumentacji<\/a><\/noindex>). W ustawieniach naszego \u017ar\u00f3d\u0142a danych mo\u017cemy zadeklarowa\u0107 zestaw regu\u0142 routingu, kt\u00f3re b\u0119d\u0105 przetwarzane przez serwer proxy Grafana. Na przyk\u0142ad dla ka\u017cdego pojedynczego endpointu istnieje mo\u017cliwo\u015b\u0107 ustawienia nag\u0142\u00f3wk\u00f3w lub adresu URL z mo\u017cliwo\u015bci\u0105 szablonowania, a dane do nich mog\u0105 by\u0107 pobierane z p\u00f3l jsonData i secureJsonData (do przechowywania hase\u0142 lub token\u00f3w w zaszyfrowanej postaci). W naszym przyk\u0142adzie zapytania typu <i>\/__proxy\/api\/v1\/namespaces<\/i> b\u0119d\u0105 proxy\u2019owane do adresu URL typu<br \/>\n\/api\/v1\/namespaces z ustawieniem nag\u0142\u00f3wka Authorization: Bearer.<\/p>\n<p><img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/ca73ff0fef62fba3edab7907c83639b6.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/9dab7537d7ccba90c9fb62dfa70b9fe2.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nOczywi\u015bcie, aby m\u00f3c pracowa\u0107 z serwerem API k8s, potrzebujemy u\u017cytkownika z uprawnieniami do odczytu. Manifesty do jego utworzenia mo\u017cna r\u00f3wnie\u017c znale\u017a\u0107 w <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\/tree\/master\/kubernetes\/\">\u017ar\u00f3d\u0142owym kodzie wtyczki<\/a><\/noindex>.<\/p>\n<h4>Cz\u0119\u015b\u0107 5: wydanie<\/h4>\n<p>\n<img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/3991a5d412f362770157fc567bb8009c.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPo tym, jak napiszesz w\u0142asn\u0105 wtyczk\u0119 do Grafana, naturalnie zechcesz udost\u0119pni\u0107 j\u0105 publicznie. W Grafana dost\u0119pna jest biblioteka wtyczek, dost\u0119pna pod linkiem <noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/grafana\/plugins\">grafana.com\/grafana\/plugins<\/a><\/noindex> <\/p>\n<p>Aby twoja wtyczka by\u0142a dost\u0119pna w oficjalnym sklepie, musisz zrobi\u0107 PR w <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/grafana\/grafana-plugin-repository\">ten repozytorium<\/a><\/noindex>, dodaj\u0105c do pliku repo.json zawarto\u015b\u0107 w postaci:<\/p>\n<p><img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/160650bf37f8f2f3e12ef7dd291e90eb.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\ngdzie version \u2014 wersja twojej wtyczki, url \u2014 link do repozytorium, a commit \u2014 hash commit, pod kt\u00f3rym dost\u0119pna b\u0119dzie konkretna wersja wtyczki. <\/p>\n<p>I na ko\u0144cu zobaczysz wspania\u0142\u0105 grafik\u0119 typu:<\/p>\n<p><img decoding=\"async\" alt=\"Tworzenie wtyczki dla Grafany: historia na nabicie kant\u00f3w\" src=\"\/wp-content\/uploads\/2019\/11\/ff906396596e9c4c15c59f8d53555f52.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDane do niej b\u0119d\u0105 automatycznie pobierane z twojego Readme.md, Changelog.md i pliku plugin.json z opisem wtyczki.<\/p>\n<h4>Cz\u0119\u015b\u0107 6: zamiast wniosk\u00f3w<\/h4>\n<p>\nNie przerwali\u015bmy prac nad naszym wtyczk\u0105 po jej wydaniu. Obecnie pracujemy nad poprawnym monitorowaniem wykorzystania zasob\u00f3w w\u0119z\u0142\u00f3w klastra, wprowadzaniem nowych funkcji dla lepszego UX, a tak\u017ce przetwarzamy du\u017c\u0105 ilo\u015b\u0107 feedbacku, kt\u00f3ry otrzymali\u015bmy po instalacjach wtyczki zar\u00f3wno od naszych klient\u00f3w, jak i z zg\u0142osze\u0144 na GitHubie (b\u0119d\u0119 bardzo szcz\u0119\u015bliwy, je\u015bli zostawisz swoje zg\u0142oszenie lub pull request \ud83d\ude42 ).<\/p>\n<p>Mamy nadziej\u0119, \u017ce ten artyku\u0142 pomo\u017ce Ci zrozumie\u0107 tak wspania\u0142e narz\u0119dzie jak Grafana i by\u0107 mo\u017ce napiszesz swoj\u0105 w\u0142asn\u0105 wtyczk\u0119. <\/p>\n<p>Dzi\u0119kuj\u0119!)<br \/>\n<br \/>\u0179r\u00f3d\u0142o: <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.1.1 - 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\/pl\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\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\/pl\/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\udd47Rozw\u00f3j wtyczki dla Grafana: historia nabitych otar\u0107 | ProHoster","description":"Cze\u015b\u0107 wszystkim!","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pl_PL","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\/pl\/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\/pl\/wp-json\/wp\/v2\/posts\/52343","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=52343"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/52343\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=52343"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=52343"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=52343"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}