{"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: storia di errori e successi","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: 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 in <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 su come abbiamo creato il plugin, quali strumenti abbiamo utilizzato e quali insidie abbiamo incontrato nel processo di sviluppo. Iniziamo!<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h4>Parte 0 - Introduzione: come siamo arrivati qui?<\/h4>\n<p>\nL'idea di scrivere un nostro plugin per Grafana \u00e8 nata completamente per caso. La nostra azienda si occupa di monitorare progetti web di varia complessit\u00e0 da oltre 10 anni. Nel corso di questo tempo, abbiamo accumulato un grande bagaglio di esperienza, casi interessanti e utilizzo di diversi sistemi di monitoraggio. Ad un certo punto ci siamo chiesti: \"Esiste uno strumento magico per il monitoraggio di Kubernetes, per poter dire, come si suol dire, 'metti e dimentica'?\". L'accoppiata Prometheus + Grafana \u00e8 ormai lo standard per il monitoraggio di k8s. E per questo stack esiste un ampio insieme di strumenti di vario tipo: prometheus-operator, un insieme 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 riesce a lavorare con le nuove versioni di node-exporter e kube-state-metrics. Cos\u00ec, a un certo punto, abbiamo deciso: \"Perch\u00e9 non creare una nostra soluzione?\"<\/p>\n<p>Quali idee abbiamo deciso di realizzare nel nostro plugin:<\/p>\n<ul>\n<li>visualizzazione della 'mappa dell'applicazione': una rappresentazione comoda delle applicazioni nel cluster, raggruppate per namespace, deployment...<\/li>\n<li>visualizzazione delle relazioni di tipo 'deployment - service (+ports)'.<\/li>\n<li>visualizzazione della distribuzione delle applicazioni del cluster sui nodi del cluster.<\/li>\n<li>raccolta di metriche e informazioni da diverse fonti: Prometheus e k8s api server.<\/li>\n<li>monitoraggio sia della parte infrastrutturale (utilizzo del tempo di CPU, memoria, sottosistema di archiviazione, rete), sia della logica delle applicazioni: 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: Che cos'\u00e8 un 'plugin per Grafana'?<\/h4>\n<p>\nDal punto di vista tecnico, un plugin per Grafana \u00e8 un controller Angular, che viene 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. Inoltre, in questa directory deve trovarsi un file plugin.json, contenente tutte le informazioni meta 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: storia di errori e successi\" 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: storia di errori e successi\" src=\"\/wp-content\/uploads\/2019\/11\/1e5b780837e5a9385e5e22a27d83f567.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>plugin.json<\/i><\/p>\n<p>Come visibile nello screenshot, abbiamo indicato plugin.type = app. Infatti, i plugin per Grafana possono essere di tre tipi:<\/p>\n<p><b>panel<\/b>: il tipo di plugin pi\u00f9 comune \u2014 rappresenta un pannello per la visualizzazione di alcune metriche, utilizzato per costruire vari dashboard.<br \/>\n<b>datasource<\/b>: plugin connettore a una fonte di dati (ad esempio, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).<br \/>\n<b>app<\/b>: plugin che ti consente di costruire la tua applicazione frontend all'interno di Grafana, creare le tue pagine html e accedere manualmente a una datasource per visualizzare dati diversi. I plugin di altri tipi (datasource, panel) e vari dashboard possono essere utilizzati come dipendenze.<\/p>\n<p><img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: storia di errori e successi\" src=\"\/wp-content\/uploads\/2019\/11\/3cd53c7c3a2635f3f20831d52198b5f0.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Esempio di dipendenze del plugin con type = app<\/i>.<\/p>\n<p>Come linguaggio di programmazione puoi utilizzare sia JavaScript che TypeScript (noi abbiamo scelto quest'ultimo). I template per i plugin hello-world di qualsiasi tipo puoi <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/CorpGlory\">trovare al link<\/a><\/noindex>: in questo repository \u00e8 presente un gran numero di starter-pack (c'\u00e8 persino un esempio sperimentale di plugin su React) con bundler preinstallati e configurati. <\/p>\n<h4>Parte 2: preparazione dell'ambiente locale<\/h4>\n<p>\nPer lavorare al plugin, naturalmente, avremo bisogno di un cluster kubernetes con tutti gli strumenti preinstallati: prometheus, node-exporter, kube-state-metrics, grafana. L'ambiente deve essere impostato rapidamente, facilmente e senza problemi e, per garantire il caricamento a caldo, la directory dei dati di Grafana deve essere montata direttamente dalla macchina dello sviluppatore.<\/p>\n<p>Il modo pi\u00f9 conveniente, a nostro avviso, 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 la combinazione 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 in dettaglio il processo di installazione di prometheus-operator su minikube. Per attivare 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>Come 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. Ognuna di queste classi eredita da una classe comune BaseModel, che descrive il costruttore, il distruttore, i metodi per l'aggiornamento e il cambio di visibilit\u00e0. In ciascuna delle classi, sono descritti i rapporti nidificati con altre entit\u00e0, ad esempio, l'elenco dei pod dell'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 i getter e i setter possiamo visualizzare o impostare le metriche necessarie delle entit\u00e0 in modo comodo e leggibile. Ad esempio, la visualizzazione formattata 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 inizialmente descritto nel nostro plugin.json nella sezione delle dipendenze:<\/p>\n<p><img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: storia di errori e successi\" src=\"\/wp-content\/uploads\/2019\/11\/b86c74839ad0d9c64b97ffc3f2a84bdf.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNel blocco per ogni pagina, dobbiamo specificare il NOME DELLA PAGINA (che sar\u00e0 poi convertito in slug, tramite cui questa pagina sar\u00e0 accessibile); il nome del componente responsabile del funzionamento di questa pagina (l'elenco dei componenti \u00e8 esportato in module.ts); l'indicazione del ruolo utente per cui \u00e8 disponibile l'interazione con questa pagina e le impostazioni di navigazione per la barra laterale. <\/p>\n<p>Nel componente responsabile del funzionamento della pagina, dobbiamo impostare templateUrl, passando il percorso fino al file html con il markup. All'interno del controller, tramite dependency injection, possiamo accedere a due importanti servizi di angular:<\/p>\n<ul>\n<li>backendSrv \u2014 servizio che fornisce interazione con l'API server di Grafana;<\/li>\n<li>datasourceSrv \u2014 servizio che fornisce interazione locale con tutte le datasource installate nella tua Grafana (ad esempio, il metodo .getAll() restituisce l'elenco di tutte le datasource installate; .get() restituisce l'oggetto istanza di un datasource specifico.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: storia di errori e successi\" 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: storia di errori e successi\" 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: storia di errori e successi\" 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, il datasource \u00e8 esattamente lo stesso tipo di plugin di tutti gli altri: ha il proprio punto di ingresso module.js e un file di metadati plugin.json. Durante lo sviluppo di un plugin con type = app possiamo interagire sia con datasource gi\u00e0 esistenti (ad esempio, prometheus-datasource), sia 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 inoltre necessario un modello config.html e un controller ConfigCtrl, che saranno utilizzati per la pagina di configurazione dell'istanza del datasource e del controller Datasource, dove viene implementata la logica di funzionamento del datasource.<\/p>\n<p>Nel plugin KubeGraf, dal punto di vista dell'interfaccia utente, il datasource rappresenta un'istanza di un cluster kubernetes, che implementa 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 dei dati dall'api-server di k8s (ottenimento dell'elenco dei namespace, dei deployment...)<\/li>\n<li>proxy delle richieste nel prometheus-datasource (che viene selezionato nelle impostazioni del plugin per ogni singolo cluster) e formattazione delle risposte per l'uso dei dati sia in pagine statiche che nei dashboard.<\/li>\n<li>aggiornamento dei dati nelle pagine statiche del plugin (con un tasso di refresh impostato).<\/li>\n<li>elaborazione delle richieste per la generazione di elenchi di template nei grafana-dashboards (metodo .metriFindQuery())<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: storia di errori e successi\" 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: storia di errori e successi\" 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: storia di errori e successi\" 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 particolarmente interessante, secondo noi, \u00e8 l'implementazione del meccanismo di autenticazione e autorizzazione per il datasource. Di norma, per configurare l'accesso alla sorgente dati finale, possiamo utilizzare il componente integrato di Grafana: datasourceHttpSettings. Con questo componente possiamo configurare l'accesso a una sorgente dati http, specificando l'url e le impostazioni di base per l'autenticazione\/autorization: login-password, oppure client-cert\/client-key. Per implementare la possibilit\u00e0 di configurare l'accesso utilizzando un bearer token (de facto standard per k8s), \u00e8 stato necessario fare un po' di \"magia\".<\/p>\n<p>Per affrontare questa problematica, possiamo utilizzare il meccanismo integrato di Grafana \"Plugin Routes\" (maggiori dettagli su <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, c'\u00e8 la possibilit\u00e0 di impostare header o url con possibilit\u00e0 di templating, i cui dati possono essere presi dai campi jsonData e secureJsonData (per memorizzare password o token in forma crittografata). Nel nostro esempio, le richieste del tipo <i>\/__proxy\/api\/v1\/namespaces<\/i> saranno proxyate a un url del tipo<br \/>\n\/api\/v1\/namespaces con l'impostazione dell'header Authorization: Bearer.<\/p>\n<p><img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: storia di errori e successi\" 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: storia di errori e successi\" src=\"\/wp-content\/uploads\/2019\/11\/9dab7537d7ccba90c9fb62dfa70b9fe2.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNaturalmente, per lavorare con il server api k8s abbiamo bisogno di un utente con accessi in sola lettura, i manifesti per creare il quale puoi trovare 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: storia di errori e successi\" 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, vorrai naturalmente renderlo disponibile al pubblico. In Grafana c'\u00e8 una libreria di plugin accessibile al link <noindex><a rel=\"nofollow\" href=\"https:\/\/grafana.com\/grafana\/plugins\">grafana.com\/grafana\/plugins<\/a><\/noindex> <\/p>\n<p>Affinch\u00e9 il tuo plugin sia disponibile nello store ufficiale, devi fare un PR in <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/grafana\/grafana-plugin-repository\">questo repository<\/a><\/noindex>, aggiungendo nel file repo.json un contenuto del tipo:<\/p>\n<p><img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: storia di errori e successi\" 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 un link al repository, e commit \u00e8 l'hash del commit tramite cui sar\u00e0 disponibile una specifica versione del plugin. <\/p>\n<p>E alla fine vedrai una fantastica immagine del tipo:<\/p>\n<p><img decoding=\"async\" alt=\"Sviluppo di un plugin per Grafana: storia di errori e successi\" 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 dal file plugin.json con la descrizione del plugin.<\/p>\n<h4>Parte 6: conclusioni<\/h4>\n<p>\nNon abbiamo interrotto lo sviluppo del nostro plugin dopo il rilascio. Stiamo attualmente lavorando sul monitoraggio corretto dell'utilizzo delle risorse dei nodi del cluster, sull'implementazione di nuove funzionalit\u00e0 per migliorare l'UX e stiamo anche gestendo un grande volume di feedback ricevuto dopo le installazioni del plugin, sia dai nostri clienti che dalle issue su GitHub (se lasci un tuo problema o una richiesta di pull, sar\u00f2 molto felice \ud83d\ude42 ).<\/p>\n<p>Speriamo che questo articolo ti aiuti a capire uno strumento cos\u00ec fantastico come Grafana e, forse, 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.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\/it\/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=\"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 di un plugin per Grafana: storia di errori e lezioni imparate | 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}]}}