Розробка плагіна для Grafana: історія набитих шишок

Всім привіт! Кілька місяців тому ми запустили в продакшн наш новий open-source проект Grafana-плагін для моніторингу kubernetes, який назвали DevOpsProdigy KubeGraf. Вихідний код плагіна доступний публічному репозиторії на GitHub. А в цій статті ми хочемо поділитися з вами історією про те, як ми створювали плагін, які інструменти використовували і з яким підводним камінням зіткнулися в процесі розробки. Погнали!

Частина 0 - вступна: як ми до цього докотилися?

Ідея написати свій власний плагін для Grafan'и у нас народилася зовсім випадково. Наша компанія вже понад 10 років займається моніторингом веб-проектів різного рівня складності. За цей час ми напрацювали великий багаж експертизи, цікавих кейсів, досвід використання різних систем моніторингу. І в якийсь момент ми запитали: «А чи існує чарівний інструмент для моніторингу 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 є мінікубе. Наступним кроком встановлюємо зв'язку 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-файлу з розміткою. Всередині контролера, через dependence 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 ми можемо оголосити набір правил роутингу, які будуть оброблятися proxy-сервером 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

Додати коментар або відгук