company_banner

Разработка плагина для 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/<your_plugin_name>/dist/module.js) и можeт быть загружен как SystemJS-модуль. Также в этой директории должен находиться файл plugin.json, содержащий в себе всю метаинформацию о вашем плагине: название, версия, тип плагина, ссылки на репозиторий/сайт/лицензию, зависимости и так далее.


    module.ts


    plugin.json

    Как видно на скриншоте, мы указали plugin.type = app. Ибо плагины для Grafana могут быть трех видов:

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


    Пример зависимостей плагина с 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 c помощью 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 = [];
       }
    }

    C помощью 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;
    }

    Pages

    Список всех страниц нашего плагина изначально описывается в нашем pluing.json в разделе зависимостей:



    В блоке для каждой страницы мы должны указать НАЗВАНИЕ СТРАНИЦЫ (оно затем будет сконвертировано в slug, по которому эта страница будет доступна); название компонента, отвечающего за работу этой странице (список компонентов экспортится в module.ts); указание роли пользователя, для которого доступна работа с этой страницей и настройки навигации для боковой панели.

    В компоненте, отвечающем за работу страницы, мы должны установить templateUrl, передав туда путь до html-файла с разметкой. Внутри контроллера, через dependency injection, мы можем получить доступ до 2-х важных angular-сервисов:

    • backendSrv — сервис, обеспечивающий взаимодействие с api-сервером графаны;
    • datasourceSrv — сервис, обеспечивающий локальное взаимодействие со всеми datasource, установленными в вашей Grafana (например, метод .getAll() — возвращает список всех установленных datasource’ов; .get(<nаme>) — возвращает объект-инстанс конкретного 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» (подробнее на официальной странице документации). В настройках нашего datasource’а мы можем объявить набор правил роутинга, которые будут обрабатываться proxy-сервером grafana. Например, для каждого отдельного endpoint’а существует возможность проставления заголовков или url с возможностью шаблонизирования, данные для которых могут браться из полей jsonData и secureJsonData (для хранения паролей или токенов в шифрованном виде). В нашем примере запросы вида /__proxy/api/v1/namespaces будут проксироваться на url вида
    <your_k8s_api_url>/api/v1/namespaces с проставлением заголовка Authorization: Bearer .





    Естественно, для работы с api-сервером k8s нам необходим пользователь с readonly доступами, манифесты для создания которого вы можете также найти в исходном коде плагина.

    Часть 5: релиз




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

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



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

    И на выходе вы увидите замечательную картинку вида:



    Данные для нее будут автоматически сграблены с вашего Readme.md, Changelog.md и файла plugin.json с описанием плагина.

    Часть 6: вместо выводов


    Мы не прекратили разработку нашего плагина после релиза. И сейчас работаем над корректным мониторингом использования ресурсов нод кластера, внедрением новых фич для повышения UX, а также разгребаем большое количества фидбека, полученного после установок плагина как нашими клиентами, так и из ишшуев на гитхабе (если вы оставите свое issue или pull request, я буду очень счастлив :-) ).

    Надеемся что данная статья поможет вам разобраться в таком прекрасном инструменте как Grafana и, возможно, написать свой собственный плагин.

    Спасибо!)
    • +22
    • 4,7k
    • 2
    ITSumma
    524,95
    Собираем безумных людей и вместе спасаем интернет
    Поделиться публикацией

    Комментарии 2

      +1

      Скриншоты кода и ни одного экрана дашбоарда))

        –1
        а зачем в статье про разработку показывать конечный результат?)

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое