Tworzenie wtyczki dla Grafany: historia na nabicie kantów

Cześć wszystkim! Kilka miesięcy temu uruchomiliśmy w produkcji nasz nowy projekt open-source — plugin Grafana do monitorowania Kubernetes, który nazwaliśmy DevOpsProdigy KubeGraf. Źródło kodu pluginu jest dostępne w publicznym repozytorium na GitHubie. A w tym artykule chcemy podzielić się z wami historią o tym, jak stworzyliśmy plugin, jakich narzędzi używaliśmy i z jakimi problemami napotkaliśmy się w procesie rozwoju. Zaczynajmy!

Część 0 — wprowadzenie: jak doszliśmy do tego momentu?

Pomysł napisania własnego pluginu dla Grafany wpadł nam zupełnie przypadkowo. Nasza firma od ponad 10 lat zajmuje się monitorowaniem projektów internetowych o różnym stopniu złożoności. W tym czasie zdobytą dużą wiedzę oraz ciekawe przypadki użycia różnych systemów monitorowania. I w pewnym momencie zaczęliśmy się zastanawiać: „Czy istnieje magiczne narzędzie do monitorowania Kubernetes, które można by po prostu „zainstalować i zapomnieć”?”.. Standardem przemysłowym dla monitorowania k8s jest oczywiście zestaw Prometheus + Grafana. Jako gotowe rozwiązania dla tego stosu istnieje wiele różnorodnych narzędzi: prometheus-operator, zestaw dashboardów kubernetes-mixin, grafana-kubernetes-app.

Najbardziej interesującą opcją dla nas wydawał się plugin grafana-kubernetes-app, ale nie jest on już wspierany od ponad roku i, co więcej, nie działa z nowymi wersjami node-exportera i kube-state-metrics. I w pewnym momencie postanowiliśmy: „A może stworzyć własne rozwiązanie?”

Jakie pomysły postanowiliśmy zrealizować w naszym pluginie:

  • wizualizacja „mapy aplikacji”: wygodne przedstawienie aplikacji w klastrze, pogrupowanych według namespace’ów, deployment’ów…;
  • wizualizacja powiązań typu „deployment — service (+ports)”.
  • wizualizacja rozmieszczenia aplikacji klastra na nodach klastra.
  • zbieranie metryk i informacji z kilku źródeł: Prometheus i k8s API server.
  • monitorowanie zarówno części infrastrukturalnej (użycie czasu procesora, pamięci, subsystemu dyskowego, sieci), jak i logiki aplikacji — stan zdrowia podów, liczba dostępnych replik, informacje o przeprowadzeniu testów liveness/readiness.

Część 1: Czym jest „plugin do Grafany”?

Z technicznego punktu widzenia, plugin do Grafany to kontroler Angular, który jest przechowywany w katalogu data Grafany (/var/grafana/plugins/<your_plugin_name>/dist/module.js) i może być załadowany jako moduł SystemJS. Również w tym katalogu powinien znajdować się plik plugin.json, zawierający wszystkie metainformacje o twoim wtyczce: nazwa, wersja, typ wtyczki, linki do repozytorium/strony/licencji, zależności i tak dalej.

Tworzenie wtyczki dla Grafany: historia na nabicie kantów
module.ts

Tworzenie wtyczki dla Grafany: historia na nabicie kantów
plugin.json

Jak widać na zrzucie ekranu, wskazaliśmy plugin.type = app. Ponieważ wtyczki dla Grafany mogą być trzech rodzajów:

panel: najpopularniejszy typ wtyczek — stanowi panel do wizualizacji różnych metryk, używany do tworzenia różnych dashboardów.
źródło danych: wtyczka-konektor do jakiegoś źródła danych (na przykład, Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).
app: wtyczka, która pozwala ci zbudować własną aplikację front-endową w Grafanie, tworzyć własne strony html i ręcznie odwoływać się do datasource w celu wizualizacji różnych danych. Jako zależności mogą być również używane wtyczki innych typów (datasource, panel) oraz różne dashboardy.

Tworzenie wtyczki dla Grafany: historia na nabicie kantów
Przykład zależności wtyczki typu app.

Jako języka programowania można używać zarówno JavaScript, jak i TypeScript (my wybraliśmy ten drugi). Szablony dla wtyczek hello-world każdego typu można znaleźć pod tym linkiem: w tym repozytorium znajduje się wiele starter-packów (jest nawet eksperymentalny przykład wtyczki w React) z preinstalowanymi i skonfigurowanymi narzędziami budującymi.

Część 2: przygotowanie lokalnego środowiska

Do pracy nad wtyczką potrzebujemy oczywiście klastra kubernetes z wszystkimi preinstalowanymi narzędziami: prometheus, node-exporter, kube-state-metrics, grafana. Środowisko powinno być konfigurowane szybko, łatwo i bezproblemowo, a dla zapewnienia hot-reload katalog danych Grafany powinien być montowany bezpośrednio z maszyny dewelopera.

Najwygodniejszym, naszym zdaniem, sposobem lokalnej pracy z kubernetes jest minikube. Następnym krokiem jest zainstalowanie zestawu Prometheus + Grafana za pomocą prometheus-operator. W tym artykule dokładnie opisano proces instalacji prometheus-operator na minikube. Aby włączyć persistentness, należy ustawić parametr persistence: true w pliku charts/grafana/values.yaml, dodać własny PV i PVC oraz wskazać je w parametrze persistence.existingClaim

Ostateczny skrypt uruchomienia minikube wygląda następująco:

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

Część 3: rozwój

Model obiektowy

W ramach przygotowań do realizacji wtyczki postanowiliśmy opisać wszystkie podstawowe encje Kubernetes, z którymi będziemy pracować, jako klasy TypeScript: pod, deployment, daemonset, statefulset, job, cronjob, service, node, namespace. Każda z tych klas dziedziczy po wspólnej klasie BaseModel, w której opisano konstruktor, destruktor, metody do aktualizacji i zmiany widoczności. W każdej z klas opisano zagnieżdżone relacje z innymi encjami, na przykład lista podów w encji typu deployment.

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

export class Deployment extends BaseModel{
   pods: Array;
   services: Array;

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

Dzięki getterom i setterom możemy wyświetlać lub ustawiać potrzebne nam metryki encji w wygodny i czytelny sposób. Na przykład sformatowane wyjście allocatable cpu węzła:

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

Strony

Lista wszystkich stron naszej wtyczki jest początkowo opisana w naszym pluing.json w sekcji zależności:

Tworzenie wtyczki dla Grafany: historia na nabicie kantów

W bloku dla każdej strony musimy określić NAZWĘ STRONY (która później zostanie skonwertowana na slug, pod którym ta strona będzie dostępna); nazwę komponentu odpowiedzialnego za działanie tej strony (lista komponentów jest eksportowana w module.ts); wskazanie roli użytkownika, dla którego dostępna jest praca z tą stroną oraz ustawienia nawigacji dla paska bocznego.

W komponencie odpowiedzialnym za pracę strony musimy ustawić templateUrl, przekazując tam ścieżkę do pliku html z szablonem. Wewnątrz kontrolera, za pomocą dependency injection, możemy uzyskać dostęp do 2 ważnych serwisów angular:

  • backendSrv — serwis zapewniający interakcję z api-serwerem Grafany;
  • datasourceSrv — serwis zapewniający lokalną interakcję ze wszystkimi datasource, które są zainstalowane w twojej Grafanie (na przykład metoda .getAll() — zwraca listę wszystkich zainstalowanych datasource'ów; .get() — zwraca obiekt-instancję konkretnego datasource.

Tworzenie wtyczki dla Grafany: historia na nabicie kantów

Tworzenie wtyczki dla Grafany: historia na nabicie kantów

Tworzenie wtyczki dla Grafany: historia na nabicie kantów

Część 4: datasource

Z perspektywy Grafana, źródło danych (datasource) jest dokładnie takim samym wtyczką jak wszystkie inne: ma swój punkt wejścia module.js oraz plik z metainformacjami plugin.json. Podczas opracowywania wtyczki z type = app, możemy współdziałać zarówno z już istniejącymi źródłami danych (takimi jak prometheus-datasource), jak i naszymi własnymi, które możemy przechowywać bezpośrednio w katalogu wtyczki (dist/datasource/*) lub instalować jako zależność. W naszym przypadku źródło danych dostarczane jest razem z kodem wtyczki. Obowiązkowo musi być także obecny szablon config.html oraz kontroler ConfigCtrl, które będą używane na stronie konfiguracji instancji źródła danych oraz kontrolera Datasource, w którym realizuje się logika działania naszego źródła danych.

W wtyczce KubeGraf, z punktu widzenia interfejsu użytkownika, źródło danych (datasource) stanowi instancję klastra kubernetes, w którym zrealizowane są następujące możliwości (kod źródłowy jest dostępny) pod tym linkiem):

  • pobieranie danych z api-servera k8s (uzyskiwanie listy namespace'ów, deploymentów…)
  • przekazywanie zapytań do prometheus-datasource (który jest wybierany w ustawieniach wtyczki dla każdego konkretnego klastra) oraz formatowanie odpowiedzi w celu wykorzystania danych zarówno na stronach statycznych, jak i w pulpitach nawigacyjnych.
  • aktualizacja danych na statycznych stronach wtyczki (z ustalonym czasem odświeżania).
  • przetwarzanie zapytań w celu utworzenia listy szablonów w grafana-dashboards (metoda .metriFindQuery())

Tworzenie wtyczki dla Grafany: historia na nabicie kantów

Tworzenie wtyczki dla Grafany: historia na nabicie kantów

Tworzenie wtyczki dla Grafany: historia na nabicie kantów

  • testowanie połączenia z końcowym klastrem 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: "Źródło danych jest OK", title: "Sukces"};
           }else{
               return {status: "error", message: "Źródło danych nie jest OK", title: "Błąd"};
           }
       }, error => {
           return {status: "error", message: "Źródło danych nie jest OK", title: "Błąd"};
       })
}

Ciekawym aspektem, naszym zdaniem, jest realizacja mechanizmu uwierzytelniania i autoryzacji dla źródła danych. Zazwyczaj, aby skonfigurować dostęp do końcowego źródła danych, możemy użyć wbudowanego komponentu Grafana — datasourceHttpSettings. Dzięki temu komponentowi możemy skonfigurować dostęp do http-źródła danych, wskazując adres URL oraz podstawowe ustawienia uwierzytelniania/autoryzacji: login-hasło lub client-cert/client-key. Aby zrealizować możliwość skonfigurowania dostępu za pomocą tokena typu bearer (de facto standard dla k8s), musieliśmy nieco „pohimichować”.

Aby rozwiązać ten problem, można użyć wbudowanego mechanizmu Grafana „Plugin Routes” (więcej na oficjalnej stronie dokumentacji). W ustawieniach naszego źródła danych możemy zadeklarować zestaw reguł routingu, które będą przetwarzane przez serwer proxy Grafana. Na przykład dla każdego pojedynczego endpointu istnieje możliwość ustawienia nagłówków lub adresu URL z możliwością szablonowania, a dane do nich mogą być pobierane z pól jsonData i secureJsonData (do przechowywania haseł lub tokenów w zaszyfrowanej postaci). W naszym przykładzie zapytania typu /__proxy/api/v1/namespaces będą proxy’owane do adresu URL typu
/api/v1/namespaces z ustawieniem nagłówka Authorization: Bearer.

Tworzenie wtyczki dla Grafany: historia na nabicie kantów

Tworzenie wtyczki dla Grafany: historia na nabicie kantów

Oczywiście, aby móc pracować z serwerem API k8s, potrzebujemy użytkownika z uprawnieniami do odczytu. Manifesty do jego utworzenia można również znaleźć w źródłowym kodzie wtyczki.

Część 5: wydanie

Tworzenie wtyczki dla Grafany: historia na nabicie kantów

Po tym, jak napiszesz własną wtyczkę do Grafana, naturalnie zechcesz udostępnić ją publicznie. W Grafana dostępna jest biblioteka wtyczek, dostępna pod linkiem grafana.com/grafana/plugins

Aby twoja wtyczka była dostępna w oficjalnym sklepie, musisz zrobić PR w ten repozytorium, dodając do pliku repo.json zawartość w postaci:

Tworzenie wtyczki dla Grafany: historia na nabicie kantów

gdzie version — wersja twojej wtyczki, url — link do repozytorium, a commit — hash commit, pod którym dostępna będzie konkretna wersja wtyczki.

I na końcu zobaczysz wspaniałą grafikę typu:

Tworzenie wtyczki dla Grafany: historia na nabicie kantów

Dane do niej będą automatycznie pobierane z twojego Readme.md, Changelog.md i pliku plugin.json z opisem wtyczki.

Część 6: zamiast wniosków

Nie przerwaliśmy prac nad naszym wtyczką po jej wydaniu. Obecnie pracujemy nad poprawnym monitorowaniem wykorzystania zasobów węzłów klastra, wprowadzaniem nowych funkcji dla lepszego UX, a także przetwarzamy dużą ilość feedbacku, który otrzymaliśmy po instalacjach wtyczki zarówno od naszych klientów, jak i z zgłoszeń na GitHubie (będę bardzo szczęśliwy, jeśli zostawisz swoje zgłoszenie lub pull request 🙂 ).

Mamy nadzieję, że ten artykuł pomoże Ci zrozumieć tak wspaniałe narzędzie jak Grafana i być może napiszesz swoją własną wtyczkę.

Dziękuję!)

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster