Распрацоўка плагіна для Grafana: гісторыя набітых шышак

Ўсім прывітанне! Некалькі месяцаў таму мы запусцілі ў прадакшн наш новы open-source праект — Grafana-плагін для маніторынгу kubernetes, які назвалі DevOpsProdigy KubeGraf. Зыходны код плагіна даступны ў публічным рэпазітары на GitHub. А ў гэтым артыкуле мы хочам падзяліцца з вамі гісторыяй аб тым, як мы стваралі плягін, якія інструменты выкарыстоўвалі і з якімі падводнымі камянямі сутыкнуліся ў працэсе распрацоўкі. Пагналі!

Частка 0 - уступная: як мы да гэтага дакаціліся?

Ідэя напісаць свой уласны плягін для Grafan'ы ў нас нарадзілася зусім выпадкова. Наша кампанія ўжо больш за 10 гадоў займаецца маніторынгам web-праектаў рознага ўзроўню складанасці. За гэты час мы напрацавалі вялікі багаж экспертызы, цікавых кейсаў, досведу выкарыстання розных сістэм маніторынгу. І ў нейкі момант мы задаліся пытаннем: «А ці існуе чароўная прылада для маніторынгу Kubernetes, каб, як той казаў, “паставіў і забыўся”»?.. Прамстандартам для маніторынгу k8s, натуральна, даўно з'яўляецца звязак Prometheus + Grafana. І ў якасці гатовых рашэнняў для дадзенага стэка існуе вялікі набор рознага роду прылад: prometheus-operator, набор дашбордаў kubernetes-mixin, grafana-kubernetes-app.

Найбольш цікавым варыянтам для нас здаўся плягін grafana-kubernetes-app, але ён не падтрымліваецца ўжо больш за год і, да таго ж, не ўмее працаваць з новымі версіямі node-exporter'а і kube-state-metrics'а. І ў нейкі момант мы вырашылі: "А ці не зрабіць нам сваё ўласнае рашэнне?"

Якія ідэі мы вырашылі рэалізаваць у сваёй убудове:

  • візуалізацыя «карты дадатку»: зручнае прадстаўленне дадаткаў у кластары, згрупаваных па namespace'ах, deployment'ах…;
  • візуалізацыя сувязяў выгляду "deployment - service (+ports)".
  • візуалізацыя размеркавання прыкладанняў кластара па nod'ах кластара.
  • збор метрык і інфармацыі з некалькіх крыніц: Prometheus і k8s api server.
  • маніторынг як інфраструктурнай часткі (выкарыстанне працэсарнага часу, памяці, дыскавай падсістэмы, сеткі), так і логікі прыкладанняў - health-status pod'ов, колькасць даступных рэплік, інфармацыя аб праходжанні liveness / readyness-проб.

Частка 1: Што такое «плягін для Grafana»?

З тэхнічнага пункту гледжання, убудова для Grafana – гэта angular-кантролер, які захоўваецца ў data-дырэкторыі Grafan'ы (/var/grafana/plugins/ /dist/module.js) і можа быць загружаны як SystemJS-модуль. Таксама ў гэтай дырэкторыі павінен знаходзіцца файл plugin.json, які змяшчае ў сабе ўсю метаінфармацыю аб вашай убудове: назва, версія, тып убудовы, спасылкі на рэпазітар/сайт/ліцэнзію, залежнасці і гэтак далей.

Распрацоўка плагіна для Grafana: гісторыя набітых шышак
module.ts

Распрацоўка плагіна для Grafana: гісторыя набітых шышак
plugin.json

Як бачна на скрыншоце, мы паказалі plugin.type = app. Бо плагіны для Grafana могуць быць трох відаў:

панэль: самы распаўсюджаны тып плагінаў - уяўляе сабой панэль для візуалізацыі якіх-небудзь метрык, выкарыстоўваецца для пабудовы розных дашбордаў.
крыніца даных: убудова-канектар да якой-небудзь крыніцы дадзеных (напрыклад, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).
прыкладанне: убудова, які дазваляе вам пабудаваць сваё ўласнае фронтэнд-дадатак ўнутры Grafana, ствараць свае ўласныя html-старонкі і ўручную звяртацца да datasource для візуалізацыі розных дадзеных. Таксама ў якасці залежнасцяў могуць выкарыстоўвацца плагіны іншых тыпаў (datasource, panel) і розныя дашборды.

Распрацоўка плагіна для Grafana: гісторыя набітых шышак
Прыклад залежнасцяў плагіна з type = app.

У якасці мовы праграмавання можна выкарыстоўваць як JavaScript, так і TypeScript (мы свой выбар спынілі на ім). Нарыхтоўкі для hello-world плагінаў любога тыпу вы можаце знайсці па спасылцы: у дадзеным рэпазітары прадстаўлена вялікая колькасць starter-pack'аў (ёсць нават эксперыментальны прыклад плагіна на React) з прадусталяванымі і настроенымі зборшчыкамі.

Частка 2: падрыхтоўка лакальнага асяроддзя

Для працы над убудовай нам, натуральна, спатрэбіцца kubernetes-кластар са ўсімі прадусталяванымі прыладамі: prometheus, node-exporter, kube-state-metrics, grafana. Асяроддзе павінна сетапиться хутка, лёгка і нязмушана, а для забеспячэння hot-reload data-дырэкторыя Grafana павінна манціравацца непасрэдна з машыны распрацоўніка.

Самым зручным, на наш погляд, спосабам лакальнай працы з kubernetes з'яўляецца minikube. Наступным крокам усталёўваны звязак Prometheus + Grafana з дапамогай prometheus-operator. У дадзеным артыкуле падрабязна апісаны працэс усталёўкі prometheus-operator на minikube. Для ўключэння персістэнтнасці неабходна ўсталяваць параметр persistence: true у файле charts/grafana/values.yaml, дадаць свой уласны PV і PVC і паказаць іх у параметры persistence.existingClaim

Выніковы скрыпт запуску minikube у нас выглядае вось так:

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
minikube mount 
/home/sergeisporyshev/Projects/Grafana:/var/grafana --gid=472 --uid=472 --9p-version=9p2000.L

Частка 3: непасрэдна распрацоўка

Аб'ектная мадэль

У якасці падрыхтоўкі да рэалізацыі плагіна мы вырашылі апісаць усе базавыя сутнасці Kubernetes, з якімі мы будзем працаваць у выглядзе TypeScript-класаў: pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Кожны з гэтых класаў успадкоўваецца ад агульнага класа BaseModel, у якім апісаны канструктар, дэструктар, метады для абнаўлення і пераключэнні бачнасці. У кожным з класаў апісаны ўкладзеныя адносіны з іншымі сутнасцямі, напрыклад, спіс pod'ов у сутнасці тыпу deployment.

import {Pod} from "./pod";
import {Service} from "./service";
import {BaseModel} from './traits/baseModel';

export class Deployment extends BaseModel{
   pods: Array<Pod>;
   services: Array<Service>;

   constructor(data: any){
       super(data);
       this.pods = [];
       this.services = [];
   }
}

З дапамогай getter'аў і setter'аў мы можам выводзіць ці ўсталёўваць патрэбныя нам метрыкі сутнасцяў у зручным і чытэльным выглядзе. Напрыклад адфарматаваная выснова allocatable cpu nod'ы:

get cpuAllocatableFormatted(){
   let cpu = this.data.status.allocatable.cpu;
   if(cpu.indexOf('m') > -1){
       cpu = parseInt(cpu)/1000;
   }
   return cpu;
}

старонкі

Спіс усіх старонак нашага плагіна першапачаткова апісваецца ў нашым pluing.json у раздзеле залежнасцяў:

Распрацоўка плагіна для Grafana: гісторыя набітых шышак

У блоку для кожнай старонкі мы павінны паказаць НАЗВА СТАРОНКІ (яно затым будзе сканвертавана ў slug, па якім гэтая старонка будзе даступная); назва кампанента, які адказвае за працу гэтай старонцы (спіс кампанентаў экспартуецца ў module.ts); указанне ролі карыстача, для якога даступная праца з гэтай старонкай і налады навігацыі для бакавой панэлі.

У кампаненце, які адказвае за працу старонкі, мы павінны ўсталяваць templateUrl, перадаўшы туды шлях да html-файла з разметкай. Унутры кантролера, праз dependency injection, мы можам атрымаць доступ да 2-х важных angular-сэрвісаў:

  • backendSrv - сэрвіс, які забяспечвае ўзаемадзеянне з api-серверам графаны;
  • datasourceSrv - сэрвіс, які забяспечвае лакальнае ўзаемадзеянне з усімі datasource, усталяванымі ў вашай Grafana (напрыклад, метад .getAll() - вяртае спіс усіх усталяваных datasource'аў; .get( ) - вяртае аб'ект-інстанс канкрэтнага datasource.

Распрацоўка плагіна для Grafana: гісторыя набітых шышак

Распрацоўка плагіна для Grafana: гісторыя набітых шышак

Распрацоўка плагіна для Grafana: гісторыя набітых шышак

Частка 4: datasource

З пункту гледжання Grafana, datasource уяўляе сабою сапраўды такая ж убудова, як і ўсе астатнія: у яго ёсць свой пункт уваходу module.js, ёсць файл з метаінфармацыяй plugin.json. Пры распрацоўцы плагіна з type = app мы можам узаемадзейнічаць як з ужо існуючымі datasource'амі (напрыклад, prometheus-datasource), так і сваімі ўласнымі, якія мы можам захоўваць непасрэдна ў дырэкторыі плагіна (dist/datasource/*) або ўсталёўваць як залежнасць. У нашым выпадку datasource пастаўляецца разам з кодам плагіна. Таксама абавязкова наяўнасць шаблону config.html і кантролера ConfigCtrl, якія будуць выкарыстоўвацца для старонкі канфігуравання асобніка datasource'а і кантролера Datasource, у якім рэалізуецца логіка працы вашага datasource'а.

У плагіне KubeGraf, з пункта гледжання карыстацкага інтэрфейсу, datasource уяўляе сабой асобнік kubernetes-кластара, у якім рэалізаваны наступныя магчымасці (зыходны код даступны па спасылцы):

  • плот дадзеных з api-server'а k8s (атрыманне спісу namespace'ов, deployment'ов…)
  • праксіраванне запытаў у prometheus-datasource (які выбіраецца ў наладах плагіна для кожнага канкрэтнага кластара) і фарматаванне адказаў для выкарыстання дадзеных як у статычных старонках, так і ў дашбордах.
  • абнаўленне даных на статычных старонках плагіна (з устаноўленым часам refresh rate).
  • апрацоўка запытаў для фармавання template-ліста ў grafana-dashboards (метад .metriFindQuery())

Распрацоўка плагіна для Grafana: гісторыя набітых шышак

Распрацоўка плагіна для Grafana: гісторыя набітых шышак

Распрацоўка плагіна для Grafana: гісторыя набітых шышак

  • тэст злучэння з канчатковым k8s-кластарам.
testDatasource(){
   let url = '/api/v1/namespaces';
   let _url = this.url;
   if(this.accessViaToken)
       _url += '/__proxy';
   _url += url;
   return this.backendSrv.datasourceRequest({
       url: _url,
       method: "GET",
       headers: {"Content-Type": 'application/json'}
   })
       .then(response => {
           if (response.status === 200) {
               return {status: "success", message: "Data source is OK", title: "Success"};
           }else{
               return {status: "error", message: "Data source is not OK", title: "Error"};
           }
       }, error => {
           return {status: "error", message: "Data source is not OK", title: "Error"};
       })
}

Асобным цікавым момантам, на наш погляд, з'яўляецца рэалізацыя механізму аўтэнтыфікацыі і аўтарызацыі для datasource. Як правіла, са скрынкі для канфігурацыі доступу да канчатковай крыніцы дадзеных мы можам выкарыстоўваць убудаваны кампанент Grafana - datasourceHttpSettings. З дапамогай гэтага кампанента мы можам наладзіць доступ да http-крыніцы дадзеных, паказаўшы url і базавыя налады аўтэнтыфікацыі/аўтарызацыі: лагін-пароль, або client-cert/client-key. Для таго каб рэалізаваць магчымасць налады доступу з дапамогай bearer-токена (дэфакта стандарт для k8s), прыйшлося крыху "пахімічыць".

Для рашэння гэтай задачы можна выкарыстоўваць убудаваны механізм Grafana "Plugin Routes" (падрабязней на афіцыйнай старонцы дакументацыі). У наладах нашага datasource'а мы можам аб'явіць набор правілаў роўтынгу, якія будуць апрацоўвацца проксі-сэрверам grafana. Напрыклад, для кожнага асобнага endpoint'а існуе магчымасць прастаўлення загалоўкаў ці url з магчымасцю шаблонізавання, дадзеныя для якіх могуць брацца з палёў jsonData і secureJsonData (для захоўвання пароляў ці токенаў у шыфраваным выглядзе). У нашым прыкладзе запыты віду /__proxy/api/v1/namespaces будуць праксіравацца на url выгляду
/api/v8/namespaces з прастаўленнем загалоўка Authorization: Bearer .

Распрацоўка плагіна для Grafana: гісторыя набітых шышак

Распрацоўка плагіна для Grafana: гісторыя набітых шышак

Натуральна, для працы з api-серверам k8s нам неабходны карыстач з readonly доступамі, маніфесты для стварэння якога вы можаце таксама знайсці ў зыходным кодзе плагіна.

Частка 5: рэліз

Распрацоўка плагіна для Grafana: гісторыя набітых шышак

Пасля таго, як вы напішаце свой уласны плягін для Grafana, вам, натуральна, захочацца выкласці яго ў адкрыты доступ. У Grafana гэта бібліятэка плагінаў, даступная па спасылцы grafana.com/grafana/plugins

Для таго каб ваша плягін быў даступны ў афіцыйным старонцы, вам неабходна зрабіць PR у гэты рэпазітар, дадаўшы ў файл repo.json змесціва выгляду:

Распрацоўка плагіна для Grafana: гісторыя набітых шышак

дзе version - версія вашага плагіна, url - спасылка на рэпазітар, а commit - hash коміта, па якім будзе даступная канкрэтная версія плагіна.

І на выхадзе вы ўбачыце цудоўную карцінку віду:

Распрацоўка плагіна для Grafana: гісторыя набітых шышак

Дадзеныя для яе будуць аўтаматычна зрабаваны з вашага Readme.md, Changelog.md і файла plugin.json з апісаннем плагіна.

Частка 6: замест высноў

Мы не спынілі распрацоўку нашага плагіна пасля рэлізу. І цяпер працуем над карэктным маніторынгам выкарыстання рэсурсаў нод кластара, укараненнем новых фіч для павышэння UX, а таксама разграбаем вялікую колькасць фідбэка, атрыманага пасля ўстановак плагіна як нашымі кліентамі, так і з ишшуев на гітхабе (калі вы пакінеце сваё issue або pull request, я буду вельмі шчаслівы 🙂 ).

Спадзяемся што дадзены артыкул дапаможа вам разабрацца ў такой выдатнай прыладзе як Grafana і, магчыма, напісаць сваю ўласную ўбудову.

Дзякуй!)

Крыніца: habr.com

Дадаць каментар