{"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\/pt\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek","title":{"rendered":"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Ol\u00e1 a todos! H\u00e1 alguns meses, lan\u00e7amos em produ\u00e7\u00e3o nosso novo projeto open-source \u2014 um plugin Grafana para monitoramento de Kubernetes, que chamamos de <b><noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/grafana\/plugins\/devopsprodigy-kubegraf-app\">DevOpsProdigy KubeGraf<\/a><\/noindex><\/b>. O c\u00f3digo-fonte do plugin est\u00e1 dispon\u00edvel em <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\">um reposit\u00f3rio p\u00fablico no GitHub<\/a><\/noindex>. Neste artigo, queremos compartilhar com voc\u00eas a hist\u00f3ria de como criamos o plugin, quais ferramentas utilizamos e com quais dificuldades nos deparamos no processo de desenvolvimento. Vamos l\u00e1!<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h4>Parte 0 \u2014 Introdu\u00e7\u00e3o: como chegamos at\u00e9 aqui?<\/h4>\n<p>\nA ideia de escrever nosso pr\u00f3prio plugin para Grafana surgiu completamente por acaso. Nossa empresa trabalha h\u00e1 mais de 10 anos com monitoramento de projetos web de diferentes niveis de complexidade. Ao longo do tempo, acumulamos uma vasta experi\u00eancia, casos interessantes e conhecimento sobre o uso de diversos sistemas de monitoramento. Em um certo momento, nos perguntamos: \"Ser\u00e1 que existe uma ferramenta m\u00e1gica para monitoramento de Kubernetes, que, como se diz, 'basta instalar e esquecer'?\". O padr\u00e3o da ind\u00fastria para monitoramento de k8s \u00e9, naturalmente, a combina\u00e7\u00e3o Prometheus + Grafana. Existem v\u00e1rias ferramentas prontas para esse stack: prometheus-operator, um conjunto de dashboards kubernetes-mixin, grafana-kubernetes-app. <\/p>\n<p>A op\u00e7\u00e3o que nos parecia mais interessante foi o plugin grafana-kubernetes-app, mas ele n\u00e3o \u00e9 mantido h\u00e1 mais de um ano e, al\u00e9m disso, n\u00e3o funciona com as novas vers\u00f5es do node-exporter e kube-state-metrics. Em um certo ponto, decidimos: \"Por que n\u00e3o criar nossa pr\u00f3pria solu\u00e7\u00e3o?\"<\/p>\n<p>Quais ideias decidimos implementar em nosso plugin:<\/p>\n<ul>\n<li>visualiza\u00e7\u00e3o de 'mapa de aplica\u00e7\u00e3o': uma representa\u00e7\u00e3o conveniente das aplica\u00e7\u00f5es no cluster, agrupadas por namespaces, deploys\u2026;<\/li>\n<li>visualiza\u00e7\u00e3o de rela\u00e7\u00f5es do tipo 'deployment \u2014 service (+ports)'.<\/li>\n<li>visualiza\u00e7\u00e3o da distribui\u00e7\u00e3o das aplica\u00e7\u00f5es do cluster pelos n\u00f3s do cluster.<\/li>\n<li>coleta de m\u00e9tricas e informa\u00e7\u00f5es de v\u00e1rias fontes: Prometheus e servidor API do k8s.<\/li>\n<li>monitoramento tanto da parte de infraestrutura (uso do tempo de CPU, mem\u00f3ria, subsistema de disco, rede), quanto da l\u00f3gica das aplica\u00e7\u00f5es \u2014 status de sa\u00fade dos pods, quantidade de r\u00e9plicas dispon\u00edveis, informa\u00e7\u00f5es sobre a execu\u00e7\u00e3o de sondagens de liveness\/readyness.<\/li>\n<\/ul>\n<p><\/p>\n<h4>Parte 1: O que \u00e9 'um plugin para Grafana'?<\/h4>\n<p>\nDo ponto de vista t\u00e9cnico, um plugin para Grafana \u00e9 um controlador Angular, armazenado na diret\u00f3rio de dados do Grafana (<i>\/var\/grafana\/plugins\/&lt;your_plugin_name&gt;\/dist\/module.js<\/i>) e pode ser carregado como um m\u00f3dulo SystemJS. Al\u00e9m disso, deve haver um arquivo plugin.json neste diret\u00f3rio, contendo todas as metainforma\u00e7\u00f5es sobre o seu plugin: nome, vers\u00e3o, tipo de plugin, links para o reposit\u00f3rio\/site\/licen\u00e7a, depend\u00eancias e assim por diante.<\/p>\n<p><img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" 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=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/1e5b780837e5a9385e5e22a27d83f567.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>plugin.json<\/i><\/p>\n<p>Como mostrado na captura de tela, n\u00f3s especificamos plugin.type = app. Os plugins para o Grafana podem ser de tr\u00eas tipos:<\/p>\n<p><b>painel<\/b>: o tipo mais comum de plugins \u2014 representa um painel para visualiza\u00e7\u00e3o de m\u00e9tricas, usado para criar v\u00e1rios dashboards.<br \/>\n<b>datasource<\/b>: plugin-conector para algum fonte de dados (por exemplo, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).<br \/>\n<b>app<\/b>: plugin que permite construir seu pr\u00f3prio aplicativo frontend dentro do Grafana, criar suas pr\u00f3prias p\u00e1ginas html e acessar manualmente a fonte de dados para visualizar diferentes dados. Plugins de outros tipos (datasource, panel) e diferentes dashboards tamb\u00e9m podem ser usados como depend\u00eancias.<\/p>\n<p><img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/3cd53c7c3a2635f3f20831d52198b5f0.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Exemplo de depend\u00eancias de um plugin com type = app<\/i>.<\/p>\n<p>Como linguagem de programa\u00e7\u00e3o, voc\u00ea pode usar tanto JavaScript quanto TypeScript (optamos por este \u00faltimo). Voc\u00ea pode encontrar templates para plugins hello-world de qualquer tipo <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/CorpGlory\">neste link<\/a><\/noindex>: este reposit\u00f3rio apresenta uma grande quantidade de starter-pack's (h\u00e1 at\u00e9 um exemplo experimental de plugin em React) com construtores pr\u00e9-instalados e configurados. <\/p>\n<h4>Parte 2: prepara\u00e7\u00e3o do ambiente local<\/h4>\n<p>\nPara trabalhar no plugin, precisamos, naturalmente, de um cluster Kubernetes com todas as ferramentas pr\u00e9-instaladas: prometheus, node-exporter, kube-state-metrics, grafana. O ambiente deve ser configurado rapidamente, de forma f\u00e1cil e descontra\u00edda, e para garantir a recarga din\u00e2mica, o diret\u00f3rio de dados do Grafana deve ser montado diretamente da m\u00e1quina do desenvolvedor.<\/p>\n<p>Na nossa opini\u00e3o, a maneira mais conveniente de trabalhar localmente com Kubernetes \u00e9 <noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/tasks\/tools\/install-minikube\/\">minikube<\/a><\/noindex>. Em seguida, instalamos a combina\u00e7\u00e3o Prometheus + Grafana usando prometheus-operator. No <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/faun\/trying-prometheus-operator-with-helm-minikube-b617a2dccfa3\">este artigo<\/a><\/noindex> \u00e9 descrito detalhadamente o processo de instala\u00e7\u00e3o do prometheus-operator no minikube. Para incluir persist\u00eancia, \u00e9 necess\u00e1rio definir o par\u00e2metro <i>persistence: true<\/i> no arquivo charts\/grafana\/values.yaml, adicionar seu pr\u00f3prio PV e PVC e especific\u00e1-los no par\u00e2metro persistence.existingClaim<\/p>\n<p>O script final para iniciar o minikube fica assim:<\/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>Parte 3: desenvolvimento propriamente dito<\/h4>\n<p>\n<b>Modelo de Objeto<\/b><\/p>\n<p>Como prepara\u00e7\u00e3o para a implementa\u00e7\u00e3o do plugin, decidimos descrever todas as entidades b\u00e1sicas do Kubernetes com as quais iremos trabalhar na forma de classes TypeScript: pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Cada uma dessas classes herda da classe base BaseModel, onde est\u00e3o descritos o construtor, o destrutor, m\u00e9todos para atualiza\u00e7\u00e3o e altern\u00e2ncia de visibilidade. Em cada uma das classes est\u00e3o descritas as rela\u00e7\u00f5es internas com outras entidades, por exemplo, a lista de pods da entidade do tipo 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>\nCom os getters e setters, podemos exibir ou definir as m\u00e9tricas necess\u00e1rias das entidades de forma conveniente e leg\u00edvel. Por exemplo, sa\u00edda formatada do allocatable cpu de um n\u00f3:<\/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>P\u00e1ginas<\/b><\/p>\n<p>A lista de todas as p\u00e1ginas do nosso plugin \u00e9 inicialmente descrita no nosso pluing.json na se\u00e7\u00e3o de depend\u00eancias:<\/p>\n<p><img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/b86c74839ad0d9c64b97ffc3f2a84bdf.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNo bloco para cada p\u00e1gina, devemos especificar o NOME DA P\u00c1GINA (que ser\u00e1 convertido em slug, pelo qual esta p\u00e1gina estar\u00e1 acess\u00edvel); o nome do componente respons\u00e1vel pelo funcionamento desta p\u00e1gina (a lista de componentes \u00e9 exportada em module.ts); a indica\u00e7\u00e3o do papel do usu\u00e1rio para o qual esta p\u00e1gina est\u00e1 acess\u00edvel e as configura\u00e7\u00f5es de navega\u00e7\u00e3o para a barra lateral. <\/p>\n<p>No componente respons\u00e1vel pelo funcionamento da p\u00e1gina, devemos definir templateUrl, passando o caminho para o arquivo html com a marca\u00e7\u00e3o. Dentro do controlador, atrav\u00e9s da inje\u00e7\u00e3o de depend\u00eancia, podemos acessar 2 servi\u00e7os importantes do angular:<\/p>\n<ul>\n<li>backendSrv \u2014 servi\u00e7o que garante intera\u00e7\u00e3o com o servidor API do Grafana;<\/li>\n<li>datasourceSrv \u2014 servi\u00e7o que garante a intera\u00e7\u00e3o local com todas as fontes de dados instaladas no seu Grafana (por exemplo, o m\u00e9todo .getAll() \u2014 retorna a lista de todas as fontes de dados instaladas; .get() \u2014 retorna o objeto-inst\u00e2ncia de uma fonte de dados espec\u00edfica.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/e616e74c2e31ca3a81b7ed91ca333208.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/5e1d45f570dda50774088405abb41c9f.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/c072867623824e799518984c7b1bee93.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<h4>Parte 4: datasource<\/h4>\n<p>\nDo ponto de vista do Grafana, a fonte de dados representa um plugin exatamente como todos os outros: ela possui seu ponto de entrada module.js e um arquivo de metainforma\u00e7\u00e3o plugin.json. Ao desenvolver um plugin com type = app, podemos interagir tanto com fontes de dados j\u00e1 existentes (por exemplo, prometheus-datasource) quanto com nossas pr\u00f3prias, que podemos armazenar diretamente no diret\u00f3rio do plugin (dist\/datasource\/*) ou instalar como depend\u00eancia. No nosso caso, a fonte de dados \u00e9 fornecida junto com o c\u00f3digo do plugin. Tamb\u00e9m \u00e9 imprescind\u00edvel ter um template config.html e um controlador ConfigCtrl, que ser\u00e3o utilizados para a p\u00e1gina de configura\u00e7\u00e3o da inst\u00e2ncia da fonte de dados e o controlador Datasource, no qual a l\u00f3gica de funcionamento da sua fonte de dados \u00e9 implementada.<\/p>\n<p>No plugin KubeGraf, do ponto de vista da interface do usu\u00e1rio, a fonte de dados representa uma inst\u00e2ncia de um cluster Kubernetes, que implementa as seguintes funcionalidades (o c\u00f3digo-fonte est\u00e1 dispon\u00edvel <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\/tree\/master\/src\/datasource\">no link<\/a><\/noindex>):<\/p>\n<ul>\n<li>coleta de dados do api-server do k8s (obten\u00e7\u00e3o da lista de namespaces, deployments\u2026)<\/li>\n<li>proxy de requisi\u00e7\u00f5es para o prometheus-datasource (que \u00e9 selecionado nas configura\u00e7\u00f5es do plugin para cada cluster espec\u00edfico) e formata\u00e7\u00e3o de respostas para utilizar os dados tanto em p\u00e1ginas est\u00e1ticas quanto em dashboards.<\/li>\n<li>atualiza\u00e7\u00e3o de dados em p\u00e1ginas est\u00e1ticas do plugin (com um tempo de refresh rate definido).<\/li>\n<li>processamento de requisi\u00e7\u00f5es para forma\u00e7\u00e3o da lista de templates nos grafana-dashboards (m\u00e9todo .metriFindQuery())<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/18159bbb41144ebceb457df28b35e7b1.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/a62665b4409566fbe24f56f88a376d5a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/7f2d61fce623ce2a3cf62feb6d0447ac.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ul>\n<li>teste de conex\u00e3o com o cluster k8s final.<\/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: \"Fonte de dados est\u00e1 OK\", title: \"Sucesso\"};\n           }else{\n               return {status: \"error\", message: \"Fonte de dados n\u00e3o est\u00e1 OK\", title: \"Erro\"};\n           }\n       }, error =&gt; {\n           return {status: \"error\", message: \"Fonte de dados n\u00e3o est\u00e1 OK\", title: \"Erro\"};\n       })\n}<\/code><\/pre>\n<p>\nUm ponto interessante, na nossa opini\u00e3o, \u00e9 a implementa\u00e7\u00e3o do mecanismo de autentica\u00e7\u00e3o e autoriza\u00e7\u00e3o para o datasource. Geralmente, de forma padr\u00e3o, para a configura\u00e7\u00e3o de acesso ao fonte de dados final, podemos utilizar o componente integrado do Grafana \u2014 datasourceHttpSettings. Com este componente, podemos configurar o acesso ao fonte de dados http, especificando a URL e as configura\u00e7\u00f5es b\u00e1sicas de autentica\u00e7\u00e3o\/autoriza\u00e7\u00e3o: login-senha ou client-cert\/client-key. Para implementar a possibilidade de configura\u00e7\u00e3o de acesso utilizando um token bearer (o padr\u00e3o de fato para k8s), foi necess\u00e1rio fazer algumas \"m\u00e1gicas\".<\/p>\n<p>Para resolver esta tarefa, podemos usar o mecanismo integrado do Grafana \"Plugin Routes\" (mais detalhes em <noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/docs\/plugins\/developing\/auth-for-datasources\/\">p\u00e1gina oficial da documenta\u00e7\u00e3o<\/a><\/noindex>). Nas configura\u00e7\u00f5es do nosso datasource, podemos declarar um conjunto de regras de roteamento que ser\u00e3o processadas pelo servidor proxy do Grafana. Por exemplo, para cada endpoint espec\u00edfico, h\u00e1 a possibilidade de definir cabe\u00e7alhos ou URLs com a capacidade de templating, cujos dados podem ser extra\u00eddos dos campos jsonData e secureJsonData (para armazenar senhas ou tokens de forma criptografada). No nosso exemplo, os pedidos do tipo <i>\/__proxy\/api\/v1\/namespaces<\/i> ser\u00e3o proxyados para uma URL do tipo<br \/>\n\/api\/v1\/namespaces com a adi\u00e7\u00e3o do cabe\u00e7alho Authorization: Bearer.<\/p>\n<p><img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/ca73ff0fef62fba3edab7907c83639b6.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/9dab7537d7ccba90c9fb62dfa70b9fe2.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNaturalmente, para trabalhar com o servidor api do k8s, precisamos de um usu\u00e1rio com permiss\u00f5es somente leitura, cujos manifests para a cria\u00e7\u00e3o voc\u00ea pode encontrar em <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\/tree\/master\/kubernetes\/\">c\u00f3digo fonte do plugin<\/a><\/noindex>.<\/p>\n<h4>Parte 5: lan\u00e7amento<\/h4>\n<p>\n<img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/3991a5d412f362770157fc567bb8009c.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDepois que voc\u00ea escrever seu pr\u00f3prio plugin para o Grafana, naturalmente, voc\u00ea vai querer disponibiliz\u00e1-lo publicamente. No Grafana, h\u00e1 uma biblioteca de plugins acess\u00edvel pelo link <noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/grafana\/plugins\">grafana.com\/grafana\/plugins<\/a><\/noindex> <\/p>\n<p>Para que seu plugin esteja dispon\u00edvel na loja oficial, \u00e9 necess\u00e1rio fazer um PR em <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/grafana\/grafana-plugin-repository\">este reposit\u00f3rio<\/a><\/noindex>, adicionando ao arquivo repo.json um conte\u00fado do tipo:<\/p>\n<p><img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/160650bf37f8f2f3e12ef7dd291e90eb.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nonde version \u2014 vers\u00e3o do seu plugin, url \u2014 link para o reposit\u00f3rio, e commit \u2014 hash do commit pelo qual uma vers\u00e3o espec\u00edfica do plugin estar\u00e1 dispon\u00edvel. <\/p>\n<p>E no final, voc\u00ea ver\u00e1 uma imagem maravilhosa como esta:<\/p>\n<p><img decoding=\"async\" alt=\"Desenvolvimento de plugin para Grafana: a hist\u00f3ria de aprendizados\" src=\"\/wp-content\/uploads\/2019\/11\/ff906396596e9c4c15c59f8d53555f52.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nOs dados para isso ser\u00e3o automaticamente extra\u00eddos do seu Readme.md, Changelog.md e do arquivo plugin.json com a descri\u00e7\u00e3o do plugin.<\/p>\n<h4>Parte 6: em vez de conclus\u00f5es<\/h4>\n<p>\nN\u00e3o paramos o desenvolvimento do nosso plugin ap\u00f3s o lan\u00e7amento. Atualmente, estamos trabalhando para monitorar corretamente o uso dos recursos das nodos do cluster, implementando novos recursos para melhorar a experi\u00eancia do usu\u00e1rio e lidando com uma grande quantidade de feedback recebido ap\u00f3s as instala\u00e7\u00f5es do plugin, tanto de nossos clientes quanto de issues no GitHub (se voc\u00ea deixar seu issue ou pull request, eu ficarei muito feliz \ud83d\ude42 ).<\/p>\n<p>Esperamos que este artigo ajude voc\u00ea a entender essa ferramenta maravilhosa chamada Grafana e, quem sabe, a escrever seu pr\u00f3prio plugin. <\/p>\n<p>Obrigado!)<br \/>\n<br \/>Fonte: <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\/pt\/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=\"pt_PT\" \/>\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\/pt\/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 Desenvolvimento de plugin para Grafana: hist\u00f3ria de erros | ProHoster","description":"Ol\u00e1 a todos!","canonical_url":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pt_PT","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\/pt\/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\/pt\/wp-json\/wp\/v2\/posts\/52343","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/comments?post=52343"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/52343\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media?parent=52343"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/categories?post=52343"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/tags?post=52343"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}