Всім привіт! Кілька місяців тому ми запустили в продакшн наш новий open-source проект Grafana-плагін для моніторингу kubernetes, який назвали
Частина 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, що містить у собі всю метаінформацію про вашу плагін: назву, версія, тип плагіна, посилання на репозиторій/сайт/ліцензію, залежності тощо.
module.ts
plugin.json
Як видно на скріншоті ми вказали plugin.type = app. Бо плагіни для Grafana можуть бути трьох видів:
панель: найпоширеніший тип плагінів - є панель для візуалізації будь-яких метрик, використовується для побудови різних дашбордів.
джерело даних: плагін-конектор до будь-якого джерела даних (наприклад, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).
додаток: плагін, що дозволяє вам побудувати свій власний фронтенд-додаток всередині Grafana, створювати свої власні html-сторінки та вручну звертатися до datasource для візуалізації різних даних. Також як залежність можуть використовуватися плагіни інших типів (datasource, panel) і різні дашборди.
Приклад залежностей плагіна з type = app.
Як мову програмування можна використовувати як JavaScript, так і TypeScript (ми свій вибір зупинили на ньому). Заготівлі для hello-world плагінів будь-якого типу ви можете
Частина 2: підготовка локального оточення
Для роботи над плагіном нам, природно, знадобиться kubernetes-кластер з усіма встановленими інструментами: prometheus, node-exporter, kube-state-metrics, grafana. Оточення має стулятися швидко, легко і невимушено, а для забезпечення hot-reload data-директорія Grafana повинна монтуватися безпосередньо з машини розробника.
Найзручнішим, на наш погляд, способом локальної роботи з kubernetes є
Підсумковий скрипт запуску 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 у розділі залежностей:
У блоці кожної сторінки ми повинні вказати НАЗВА СТОРІНКИ (вона потім буде конвертовано в slug, яким ця сторінка буде доступна); назва компонента, який відповідає за роботу цієї сторінки (список компонентів експортується до module.ts); вказує роль користувача, для якого доступна робота з цією сторінкою та налаштування навігації для бічної панелі.
У компоненті, який відповідає за роботу сторінки, ми повинні встановити templateUrl, передавши туди шлях до html-файлу з розміткою. Всередині контролера, через dependence injection, ми можемо отримати доступ до 2-х важливих angular-сервісів:
- backendSrv - сервіс, що забезпечує взаємодію з api-сервером графани;
- datasourceSrv — сервіс, що забезпечує локальну взаємодію з усіма datasource, встановленими у Grafana (наприклад, метод .getAll() — повертає список усіх встановлених datasource'ів; .get( ) - Повертає об'єкт-інстанс конкретного datasource.
Частина 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())
- тест з'єднання з кінцевим 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" (докладніше на
/api/v8/namespaces із проставленням заголовка Authorization: Bearer .
Природно, для роботи з api-сервером k8s нам необхідний користувач з readonly доступами, маніфести для створення якого ви можете знайти в
Частина 5: реліз
Після того, як ви напишете власний плагін для Grafana, вам, природно, захочеться викласти його у відкритий доступ. У Grafana це бібліотека плагінів, доступна за посиланням
Для того щоб ваш плагін був доступний в офіційному сторі, вам необхідно зробити PR в
де version – версія вашого плагіна, url – посилання на репозиторій, а commit – hash комміта, за яким буде доступна конкретна версія плагіна.
І на виході ви побачите чудову картинку виду:
Дані для неї будуть автоматично пограбовані з вашого Readme.md, Changelog.md та файлу plugin.json з описом плагіна.
Частина 6: замість висновків
Ми не припинили розробку нашого плагіна після релізу. І зараз працюємо над коректним моніторингом використання ресурсів нод кластера, впровадженням нових фіч для підвищення UX, а також розгрібаємо велику кількість фідбеку, отриманого після установок плагіна як нашими клієнтами, так і з ішшуїв на гітхабі (якщо ви залишите своє issue або pull request, я буду дуже щасливий 🙂).
Сподіваємося, що дана стаття допоможе вам розібратися в такому прекрасному інструменті як Grafana і, можливо, написати свій власний плагін.
Спасибі!)
Джерело: habr.com