{"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\/it\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek","title":{"rendered":"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Ciao a tutti! Qualche mese fa abbiamo lanciato in produzione il nostro nuovo progetto open-source \u2014 un plugin Grafana per il monitoraggio di Kubernetes, che abbiamo chiamato <b><noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/grafana\/plugins\/devopsprodigy-kubegraf-app\">DevOpsProdigy KubeGraf<\/a><\/noindex><\/b>. Il codice sorgente del plugin \u00e8 disponibile su <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\">un repository pubblico su GitHub<\/a><\/noindex>. In questo articolo vogliamo condividere con voi la storia di come abbiamo creato il plugin, quali strumenti abbiamo utilizzato e quali insidie abbiamo incontrato durante lo sviluppo. Andiamo!<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h4>Parte 0 \u2014 introduzione: come siamo arrivati fin qui?<\/h4>\n<p>\nL'idea di creare un nostro plugin per Grafana \u00e8 nata del tutto per caso. La nostra azienda \u00e8 attiva da oltre 10 anni nel monitoraggio di progetti web di varia complessit\u00e0. In questo tempo abbiamo accumulato una vasta esperienza, interessanti casi studio e competenze nell'uso di diversi sistemi di monitoraggio. A un certo punto ci siamo posti la domanda: \u00abEsiste uno strumento magico per monitorare Kubernetes, che si possa semplicemente installare e dimenticare?\u00bb... Lo standard del settore per il monitoraggio di k8s \u00e8 da tempo la combinazione Prometheus + Grafana. Ci sono molti strumenti di diversi tipi disponibili come soluzioni pronte per questo stack: prometheus-operator, una serie di dashboard kubernetes-mixin, grafana-kubernetes-app. <\/p>\n<p>L'opzione che ci \u00e8 sembrata pi\u00f9 interessante \u00e8 stata il plugin grafana-kubernetes-app, ma non \u00e8 supportato da oltre un anno e, inoltre, non \u00e8 compatibile con le nuove versioni di node-exporter e kube-state-metrics. A un certo punto abbiamo deciso: \u00abPerch\u00e9 non creare una nostra soluzione?\u00bb<\/p>\n<p>Quali idee abbiamo deciso di implementare nel nostro plugin:<\/p>\n<ul>\n<li>visualizzazione della \u00abmappa dell'applicazione\u00bb: una rappresentazione conveniente delle applicazioni nel cluster, raggruppate per namespace, deployment\u2026;<\/li>\n<li>visualizzazione delle relazioni del tipo \u00abdeployment \u2014 service (+ports)\u00bb.<\/li>\n<li>visualizzazione della distribuzione delle applicazioni del cluster sui nodi del cluster.<\/li>\n<li>raccolta di metriche e informazioni da pi\u00f9 fonti: Prometheus e k8s api server.<\/li>\n<li>monitoraggio sia della parte infrastrutturale (utilizzo del tempo della CPU, memoria, sottosistema disco, rete), sia della logica delle applicazioni \u2014 stato di salute dei pod, numero di repliche disponibili, informazioni sui controlli di liveness\/readiness.<\/li>\n<\/ul>\n<p><\/p>\n<h4>Parte 1: Cos'\u00e8 un \u00abplugin per Grafana\u00bb?<\/h4>\n<p>\nDal punto di vista tecnico, un plugin per Grafana \u00e8 un controller Angular memorizzato nella directory data di Grafana (<i>\/var\/grafana\/plugins\/&lt;your_plugin_name&gt;\/dist\/module.js<\/i>) e pu\u00f2 essere caricato come modulo SystemJS. In questa directory deve trovarsi anche un file plugin.json, che contiene tutte le metainformazioni sul tuo plugin: nome, versione, tipo di plugin, link al repository\/sito\/licenza, dipendenze e cos\u00ec via.<\/p>\n<p><img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" 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=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" src=\"\/wp-content\/uploads\/2019\/11\/1e5b780837e5a9385e5e22a27d83f567.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>plugin.json<\/i><\/p>\n<p>Come si pu\u00f2 vedere nello screenshot, abbiamo specificato plugin.type = app. Poich\u00e9 i plugin per Grafana possono essere di tre tipi:<\/p>\n<p><b>panel<\/b>: il tipo di plugin pi\u00f9 comune \u2014 consiste in un pannello per visualizzare metriche, utilizzato per costruire vari dashboard.<br \/>\n<b>datasource<\/b>: plugin connettore per una fonte di dati (ad esempio, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).<br \/>\n<b>app<\/b>: plugin che consente di costruire la propria applicazione frontend all'interno di Grafana, creare le proprie pagine html e accedere manualmente alla fonte dati per visualizzare diverse informazioni. Possono essere utilizzati anche plugin di altri tipi (datasource, panel) e vari dashboard come dipendenze.<\/p>\n<p><img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" src=\"\/wp-content\/uploads\/2019\/11\/3cd53c7c3a2635f3f20831d52198b5f0.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Esempio di dipendenze di un plugin con type = app<\/i>.<\/p>\n<p>Come linguaggio di programmazione si possono usare sia JavaScript che TypeScript (noi abbiamo scelto quest'ultimo). I template per i plugin hello-world di qualsiasi tipo possono essere <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/CorpGlory\">trovati al link<\/a><\/noindex>: in questo repository \u00e8 presente un'ampia gamma di starter-pack (c'\u00e8 persino un esempio sperimentale di plugin in React) con builder preinstallati e configurati. <\/p>\n<h4>Parte 2: preparazione dell'ambiente locale<\/h4>\n<p>\nPer lavorare con il plugin, avremo naturalmente bisogno di un cluster Kubernetes con tutti gli strumenti preinstallati: Prometheus, Node Exporter, Kube State Metrics e Grafana. L'ambiente deve configurarsi in modo rapido, semplice e senza sforzo, e per garantire il hot-reload, la directory dei dati di Grafana deve essere montata direttamente dalla macchina dello sviluppatore.<\/p>\n<p>A nostro avviso, il modo pi\u00f9 comodo per lavorare localmente con Kubernetes \u00e8 <noindex><a rel=\"nofollow\" href=\"https:\/\/kubernetes.io\/docs\/tasks\/tools\/install-minikube\/\">minikube<\/a><\/noindex>. Il passo successivo \u00e8 installare l'insieme Prometheus + Grafana utilizzando prometheus-operator. In <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/faun\/trying-prometheus-operator-with-helm-minikube-b617a2dccfa3\">questo articolo<\/a><\/noindex> \u00e8 descritto dettagliatamente il processo di installazione di prometheus-operator su Minikube. Per abilitare la persistenza, \u00e8 necessario impostare il parametro <i>persistence: true<\/i> nel file charts\/grafana\/values.yaml, aggiungere il proprio PV e PVC e specificarli nel parametro persistence.existingClaim.<\/p>\n<p>Lo script finale per avviare Minikube appare cos\u00ec:<\/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: sviluppo diretto<\/h4>\n<p>\n<b>Modello ad oggetti<\/b><\/p>\n<p>In preparazione per l'implementazione del plugin, abbiamo deciso di descrivere tutte le entit\u00e0 di base di Kubernetes, con cui lavoreremo sotto forma di classi TypeScript: pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Ciascuna di queste classi eredita da una classe comune BaseModel, in cui sono descritti il costruttore, il distruttore e i metodi per l'aggiornamento e la commutazione della visibilit\u00e0. In ogni classe sono descritte le relazioni incorporate con altre entit\u00e0, ad esempio, l'elenco dei pod per l'entit\u00e0 di 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>\nCon gettori e settori possiamo visualizzare o impostare le metriche necessarie delle entit\u00e0 in un modo semplice e leggibile. Ad esempio, l'output formattato dell'allocatable cpu del nodo:<\/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>Pagine<\/b><\/p>\n<p>L'elenco di tutte le pagine del nostro plugin \u00e8 descritto inizialmente nel nostro pluing.json nella sezione dipendenze:<\/p>\n<p><img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" src=\"\/wp-content\/uploads\/2019\/11\/b86c74839ad0d9c64b97ffc3f2a84bdf.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNella sezione dedicata a ciascuna pagina, dobbiamo specificare il NOME DELLA PAGINA (che verr\u00e0 poi convertito in uno slug tramite cui sar\u00e0 accessibile); il nome del componente responsabile di questa pagina (l'elenco dei componenti \u00e8 esportato in module.ts); e l'indicazione del ruolo utente per il quale \u00e8 disponibile il lavoro con questa pagina e le impostazioni di navigazione per la barra laterale. <\/p>\n<p>Nel componente responsabile della pagina, dobbiamo impostare templateUrl, passando l\u00ec il percorso del file html con il markup. All'interno del controller, tramite dependency injection, possiamo accedere a 2 importanti servizi Angular:<\/p>\n<ul>\n<li>backendSrv \u2014 il servizio che consente l'interazione con l'api-server di Grafana;<\/li>\n<li>datasourceSrv \u2014 il servizio che consente l'interazione locale con tutte le datasource installate nella vostra Grafana (ad esempio, il metodo .getAll() restituisce un elenco di tutte le datasource installate; .get() restituisce l'oggetto istanza della specifica datasource.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" src=\"\/wp-content\/uploads\/2019\/11\/e616e74c2e31ca3a81b7ed91ca333208.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" src=\"\/wp-content\/uploads\/2019\/11\/5e1d45f570dda50774088405abb41c9f.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" 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>\nDal punto di vista di Grafana, un datasource \u00e8 un plugin come tutti gli altri: ha il proprio punto d'ingresso module.js e un file di metainformazione plugin.json. Quando sviluppiamo un plugin con type = app, possiamo interagire sia con i datasource gi\u00e0 esistenti (come prometheus-datasource) che con i nostri, che possiamo memorizzare direttamente nella directory del plugin (dist\/datasource\/*) o installare come dipendenza. Nel nostro caso, il datasource viene fornito insieme al codice del plugin. \u00c8 essenziale avere anche un modello config.html e un controller ConfigCtrl, che saranno utilizzati per la pagina di configurazione dell'istanza del datasource e del controller Datasource, in cui viene implementata la logica operativa del tuo datasource.<\/p>\n<p>Nel plugin KubeGraf, dal punto di vista dell'interfaccia utente, un datasource rappresenta un'istanza di un cluster Kubernetes, in cui sono implementate le seguenti funzionalit\u00e0 (il codice sorgente \u00e8 disponibile <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\/tree\/master\/src\/datasource\">al link<\/a><\/noindex>):<\/p>\n<ul>\n<li>recupero dati dall'api-server k8s (ottenimento dell'elenco dei namespace, dei deployment...)<\/li>\n<li>il proxy delle richieste in prometheus-datasource (che viene selezionato nelle impostazioni del plugin per ogni singolo cluster) e la formattazione delle risposte per utilizzare i dati sia nelle pagine statiche che nei dashboard.<\/li>\n<li>l'aggiornamento dei dati nelle pagine statiche del plugin (con un tempo di refresh rate impostato).<\/li>\n<li>la gestione delle richieste per la formazione della lista dei template nei grafana-dashboards (metodo .metriFindQuery())<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" src=\"\/wp-content\/uploads\/2019\/11\/18159bbb41144ebceb457df28b35e7b1.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" src=\"\/wp-content\/uploads\/2019\/11\/a62665b4409566fbe24f56f88a376d5a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" src=\"\/wp-content\/uploads\/2019\/11\/7f2d61fce623ce2a3cf62feb6d0447ac.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ul>\n<li>test di connessione con il cluster k8s finale.<\/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: \"Data source is OK\", title: \"Success\"};\n           }else{\n               return {status: \"error\", message: \"Data source is not OK\", title: \"Error\"};\n           }\n       }, error =&gt; {\n           return {status: \"error\", message: \"Data source is not OK\", title: \"Error\"};\n       })\n}<\/code><\/pre>\n<p>\nUn aspetto interessante, a nostro avviso, \u00e8 l'implementazione del meccanismo di autenticazione e autorizzazione per il datasource. Di norma, per configurare l'accesso alla fonte di dati finale, possiamo utilizzare il componente integrato di Grafana \u2014 datasourceHttpSettings. Grazie a questo componente, possiamo impostare l'accesso a una fonte di dati http, specificando l'url e le impostazioni di autenticazione\/ autorizzazione: nome utente-password, oppure client-cert\/client-key. Per realizzare la possibilit\u00e0 di configurare l'accesso tramite bearer token (de facto standard per k8s), \u00e8 stato necessario fare un po' di \"manipolazione\".<\/p>\n<p>Per risolvere questo problema, si pu\u00f2 utilizzare il meccanismo integrato di Grafana \"Plugin Routes\" (maggiori dettagli sulla <noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/docs\/plugins\/developing\/auth-for-datasources\/\">pagina ufficiale della documentazione<\/a><\/noindex>). Nelle impostazioni del nostro datasource, possiamo dichiarare un insieme di regole di routing che saranno elaborate dal server proxy di Grafana. Ad esempio, per ogni singolo endpoint esiste la possibilit\u00e0 di impostare intestazioni o url con la possibilit\u00e0 di templating, i dati per i quali possono essere prelevati dai campi jsonData e secureJsonData (per memorizzare password o token in forma criptata). Nel nostro esempio, le richieste di tipo <i>\/__proxy\/api\/v1\/namespaces<\/i> saranno proxied su un URL del tipo<br \/>\n\/api\/v1\/namespaces impostando l'intestazione Authorization: Bearer .<\/p>\n<p><img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" src=\"\/wp-content\/uploads\/2019\/11\/ca73ff0fef62fba3edab7907c83639b6.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" src=\"\/wp-content\/uploads\/2019\/11\/9dab7537d7ccba90c9fb62dfa70b9fe2.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNaturalmente, per interagire con l'api-server k8s \u00e8 necessario un utente con accessi readonly, dei manifesti per crearne uno puoi trovarli anche nel <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/kubegraf\/tree\/master\/kubernetes\/\">codice sorgente del plugin<\/a><\/noindex>.<\/p>\n<h4>Parte 5: rilascio<\/h4>\n<p>\n<img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" src=\"\/wp-content\/uploads\/2019\/11\/3991a5d412f362770157fc567bb8009c.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDopo aver scritto il tuo plugin per Grafana, naturalmente, vorrai pubblicarlo in modalit\u00e0 open source. In Grafana esiste una libreria di plugin, disponibile al link <noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/grafana\/plugins\">grafana.com\/grafana\/plugins<\/a><\/noindex> <\/p>\n<p>Per rendere il tuo plugin disponibile nel negozio ufficiale, \u00e8 necessario fare una PR in <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/grafana\/grafana-plugin-repository\">questo repository<\/a><\/noindex>, aggiungendo al file repo.json un contenuto del tipo:<\/p>\n<p><img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" src=\"\/wp-content\/uploads\/2019\/11\/160650bf37f8f2f3e12ef7dd291e90eb.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\ndove version \u00e8 la versione del tuo plugin, url \u00e8 il link al repository, e commit \u00e8 l'hash del commit che render\u00e0 disponibile una specifica versione del plugin. <\/p>\n<p>E alla fine vedrai un'immagine meravigliosa del tipo:<\/p>\n<p><img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: la storia delle difficolt\u00e0 affrontate\" src=\"\/wp-content\/uploads\/2019\/11\/ff906396596e9c4c15c59f8d53555f52.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nI dati per essa saranno automaticamente estratti dal tuo Readme.md, Changelog.md e file plugin.json con la descrizione del plugin.<\/p>\n<h4>Parte 6: invece delle conclusioni<\/h4>\n<p>\nNon abbiamo interrotto lo sviluppo del nostro plugin dopo il rilascio. Attualmente stiamo lavorando su un monitoraggio accurato dell'utilizzo delle risorse dei nodi del cluster, implementando nuove funzionalit\u00e0 per migliorare l'UX e gestendo un gran numero di feedback ricevuti dopo le installazioni del plugin sia dai nostri clienti che attraverso le segnalazioni su GitHub (se lasci un tuo issue o pull request, sar\u00f2 molto felice \ud83d\ude42 ).<\/p>\n<p>Speriamo che questo articolo ti aiuti a capire questo straordinario strumento chiamato Grafana e, magari, a scrivere il tuo plugin. <\/p>\n<p>Grazie!)<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.0.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\/it\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\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\/it\/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\udd47Sviluppo del plugin per Grafana: storia di errori e successi | ProHoster","description":"Ciao a tutti!","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/razrabotka-plagina-dlya-grafana-istoriya-nabityh-shishek","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","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\/it\/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\/it\/wp-json\/wp\/v2\/posts\/52343","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=52343"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/52343\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=52343"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=52343"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=52343"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}