Rozwój wtyczki do Grafany: historia wielkich ujęć

Cześć wszystkim! Kilka miesięcy temu wprowadziliśmy do produkcji nasz nowy projekt open source - wtyczkę Grafana do monitorowania kubernetes, którą nazwaliśmy DevOpsProdigy KubeGraf. Kod źródłowy wtyczki jest dostępny pod adresem publiczne repozytorium na GitHubie. W tym artykule chcemy podzielić się z Wami historią o tym, jak stworzyliśmy wtyczkę, jakich narzędzi użyliśmy i jakie pułapki napotkaliśmy podczas procesu tworzenia. Chodźmy!

Część 0 – wprowadzenie: jak dotarliśmy do tego punktu?

Pomysł napisania własnego pluginu dla Grafana przyszedł nam do głowy zupełnie przez przypadek. Nasza firma od ponad 10 lat monitoruje projekty internetowe o różnym stopniu złożoności. Przez ten czas zgromadziliśmy dużą wiedzę, ciekawe przypadki i doświadczenie w stosowaniu różnych systemów monitoringu. I w pewnym momencie zadaliśmy sobie pytanie: „Czy istnieje magiczne narzędzie do monitorowania Kubernetesa, aby, jak to mówią, „ustawić i zapomnieć”?”.. Branżowym standardem monitorowania k8s jest oczywiście od dawna Połączenie Prometeusza i Grafany. A jako gotowe rozwiązania dla tego stosu istnieje duży zestaw różnego rodzaju narzędzi: prometheus-operator, zestaw dashboardów kubernetes-mixin, grafana-kubernetes-app.

Najciekawszą opcją wydała nam się wtyczka grafana-kubernetes-app, która jednak nie jest wspierana od ponad roku, a ponadto nie może współpracować z nowymi wersjami node-exporter i kube-state-metrics. I w pewnym momencie zdecydowaliśmy: „Czy nie powinniśmy podjąć własnej decyzji?”

Jakie pomysły postanowiliśmy wdrożyć w naszej wtyczce:

  • wizualizacja „mapy aplikacji”: wygodna prezentacja aplikacji w klastrze, pogrupowana według przestrzeni nazw, wdrożeń...;
  • wizualizacja połączeń typu „wdrożenie - usługa (+porty)”.
  • wizualizacja rozmieszczenia aplikacji klastrowych pomiędzy węzłami klastra.
  • zbieranie metryk i informacji z kilku źródeł: Prometheus i serwer api k8s.
  • monitorowanie zarówno części infrastrukturalnej (wykorzystanie czasu procesora, pamięci, podsystemu dyskowego, sieci), jak i logiki aplikacji - pody stanu kondycji, liczba dostępnych replik, informacja o przejściu testów żywotności/gotowości.

Część 1: Co to jest „wtyczka Grafana”?

Z technicznego punktu widzenia wtyczka do Grafany jest kontrolerem kątowym, który jest przechowywany w katalogu danych Grafany (/var/grafana/wtyczki/ /dist/module.js) i można go załadować jako moduł SystemJS. Również w tym katalogu powinien znajdować się plik plugin.json zawierający wszystkie metainformacje o Twojej wtyczce: nazwa, wersja, typ wtyczki, linki do repozytorium/strony/licencji, zależności i tak dalej.

Rozwój wtyczki do Grafany: historia wielkich ujęć
moduł.ts

Rozwój wtyczki do Grafany: historia wielkich ujęć
wtyczka.json

Jak widać na zrzucie ekranu, określiliśmy plugin.type = app. Ponieważ wtyczki do Grafany mogą być trzech typów:

płyta: najpopularniejszy typ wtyczki - jest to panel do wizualizacji dowolnych metryk, służący do budowy różnych dashboardów.
źródło danych: złącze wtykowe do jakiegoś źródła danych (na przykład Prometheus-datasource, ClickHouse-datasource, ElasticSearch-datasource).
Aplikacja: Wtyczka umożliwiająca zbudowanie własnej aplikacji frontendowej w Grafanie, tworzenie własnych stron HTML i ręczny dostęp do źródła danych w celu wizualizacji różnych danych. Jako zależności można również używać wtyczek innych typów (źródło danych, panel) i różnych dashboardów.

Rozwój wtyczki do Grafany: historia wielkich ujęć
Przykładowe zależności wtyczek z type=app.

Jako języka programowania możesz używać zarówno JavaScript, jak i TypeScript (my go wybraliśmy). Przygotowania do wtyczek hello-world dowolnego typu znajdź link: to repozytorium zawiera dużą liczbę pakietów startowych (istnieje nawet eksperymentalny przykład wtyczki w React) z preinstalowanymi i skonfigurowanymi kreatorami.

Część 2: przygotowanie środowiska lokalnego

Do pracy nad wtyczką potrzebujemy oczywiście klastra kubernetes ze wszystkimi preinstalowanymi narzędziami: prometheus, node-exporter, kube-state-metrics, grafana. Środowisko powinno zostać skonfigurowane szybko, łatwo i naturalnie, a aby zapewnić hot-reload, katalog danych Grafany powinien być montowany bezpośrednio z komputera programisty.

Naszym zdaniem najwygodniejszym sposobem pracy lokalnej z kubernetesem jest minikube. Następnym krokiem jest instalacja kombinacji Prometheus + Grafana za pomocą operatora prometheus. W Ten artykuł Szczegółowo opisano proces instalacji prometheusa-operatora na minikube. Aby włączyć trwałość, należy ustawić parametr wytrwałość: prawda w pliku charts/grafana/values.yaml dodaj własne PV i PVC i określ je w parametrze Persistence.existingClaim

Nasz ostateczny skrypt uruchamiania 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: aktualny rozwój

Model obiektowy

Przygotowując się do wdrożenia wtyczki, postanowiliśmy opisać w formie klas TypeScript wszystkie podstawowe encje Kubernetesa, z którymi będziemy pracować: pod, Deploy, daemonset, statefulset, job, cronjob, service, node, namespace. Każda z tych klas dziedziczy ze wspólnej klasy BaseModel, która opisuje konstruktor, destruktor, metody aktualizacji i przełączania widoczności. Każda z klas opisuje zagnieżdżone relacje z innymi jednostkami, na przykład listę zasobników dla jednostki typu wdrożenia.

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 = [];
   }
}

Za pomocą getterów i setterów możemy wyświetlić lub ustawić potrzebne nam metryki encji w wygodnej i czytelnej formie. Na przykład sformatowane dane wyjściowe przydzielanych węzłów procesora:

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

O Nas

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

Rozwój wtyczki do Grafany: historia wielkich ujęć

W bloku dla każdej strony musimy podać NAZWĘ STRONY (zostanie ona wówczas zamieniona na ślimak, dzięki któremu ta strona będzie dostępna); nazwa komponentu odpowiedzialnego za działanie tej strony (lista komponentów jest eksportowana do module.ts); wskazanie roli użytkownika, dla którego dostępna jest praca z tą stroną oraz ustawień nawigacji na pasku bocznym.

W komponencie odpowiedzialnym za działanie strony musimy ustawić templateUrl, przekazując tam ścieżkę do pliku html ze znacznikami. Wewnątrz kontrolera, poprzez wstrzykiwanie zależności, możemy uzyskać dostęp do aż 2 ważnych usług kątowych:

  • backendSrv – usługa zapewniająca interakcję z serwerem Grafana API;
  • datasourceSrv - usługa zapewniająca lokalną interakcję ze wszystkimi źródłami danych zainstalowanymi w Twojej Grafanie (np. metoda .getAll() - zwraca listę wszystkich zainstalowanych źródeł danych; .get( ) - zwraca obiekt instancji określonego źródła danych.

Rozwój wtyczki do Grafany: historia wielkich ujęć

Rozwój wtyczki do Grafany: historia wielkich ujęć

Rozwój wtyczki do Grafany: historia wielkich ujęć

Część 4: źródło danych

Z punktu widzenia Grafany datasource jest dokładnie tą samą wtyczką, co wszystkie inne: ma swój własny punkt wejścia module.js, znajduje się tam plik z metainformacjami plugin.json. Tworząc wtyczkę z type = app, możemy wchodzić w interakcję zarówno z istniejącymi źródłami danych (na przykład 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 pochodzi z kodu wtyczki. Niezbędne jest także posiadanie szablonu config.html oraz kontrolera ConfigCtrl, który będzie używany na stronie konfiguracyjnej instancji źródła danych oraz kontrolera Datasource, który implementuje logikę działania Twojego źródła danych.

We wtyczce KubeGraf z punktu widzenia interfejsu użytkownika źródłem danych jest instancja klastra kubernetes realizująca następujące możliwości (kod źródłowy jest dostępny по ссылке):

  • zbieranie danych z serwera api k8s (uzyskiwanie listy przestrzeni nazw, wdrożeń...)
  • proxy żądań do prometheus-datasource (które jest wybierane w ustawieniach wtyczki dla każdego konkretnego klastra) i formatowanie odpowiedzi w celu wykorzystania danych zarówno na stronach statycznych, jak i w dashboardach.
  • aktualizacja danych na statycznych stronach wtyczek (z ustawioną częstotliwością odświeżania).
  • przetwarzanie zapytań w celu wygenerowania arkusza szablonów w grafana-dashboardach (metoda metriFindQuery())

Rozwój wtyczki do Grafany: historia wielkich ujęć

Rozwój wtyczki do Grafany: historia wielkich ujęć

Rozwój wtyczki do Grafany: historia wielkich ujęć

  • test 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: "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"};
       })
}

Osobnym ciekawym naszym zdaniem punktem jest implementacja mechanizmu uwierzytelniania i autoryzacji źródła danych. Zazwyczaj od razu po wyjęciu z pudełka możemy skorzystać z wbudowanego komponentu Grafana datasourceHttpSettings, aby skonfigurować dostęp do finalnego źródła danych. Za pomocą tego komponentu możemy skonfigurować dostęp do źródła danych http poprzez podanie adresu URL oraz podstawowych ustawień uwierzytelniania/autoryzacji: hasło-logowania lub certyfikat-klienta/klucz-klienta. Aby zaimplementować możliwość konfiguracji dostępu za pomocą tokena na okaziciela (de facto standard dla k8s), musieliśmy dokonać drobnych poprawek.

Aby rozwiązać ten problem, można skorzystać z wbudowanego w Grafanę mechanizmu „Plugin Routes” (więcej szczegółów na stronie oficjalna strona dokumentacji). W ustawieniach naszego źródła danych możemy zadeklarować zestaw reguł routingu, które będą przetwarzane przez serwer proxy grafana. Przykładowo dla każdego punktu końcowego możliwe jest ustawienie nagłówków lub adresów URL z możliwością szablonowania, dla których dane można pobrać z pól jsonData i secureJsonData (do przechowywania haseł lub tokenów w postaci zaszyfrowanej). W naszym przykładzie zapytania takie jak /__proxy/api/v1/przestrzenie nazw zostanie przesłany do adresu URL formularza
/api/v8/namespaces z nagłówkiem Authorization: Bearer.

Rozwój wtyczki do Grafany: historia wielkich ujęć

Rozwój wtyczki do Grafany: historia wielkich ujęć

Naturalnie do pracy z serwerem api k8s potrzebujemy użytkownika z dostępem tylko do odczytu, manifesty do tworzenia, które można również znaleźć w kod źródłowy wtyczki.

Część 5: uwolnienie

Rozwój wtyczki do Grafany: historia wielkich ujęć

Kiedy już napiszesz własną wtyczkę Grafana, naturalnie będziesz chciał udostępnić ją publicznie. W Grafanie jest to biblioteka wtyczek dostępna tutaj grafana.com/grafana/plugins

Aby Twoja wtyczka była dostępna w oficjalnym sklepie, musisz zrobić PR to repozytoriumdodając taką treść do pliku repo.json:

Rozwój wtyczki do Grafany: historia wielkich ujęć

gdzie wersja to wersja Twojej wtyczki, url to link do repozytorium, a commit to skrót zatwierdzenia, dla którego będzie dostępna konkretna wersja wtyczki.

A na wyjściu zobaczysz wspaniały obraz, taki jak:

Rozwój wtyczki do Grafany: historia wielkich ujęć

Dane do niego zostaną automatycznie pobrane z plików Readme.md, Changelog.md i pliku plugin.json z opisem wtyczki.

Część 6: zamiast wniosków

Nie przestaliśmy rozwijać naszej wtyczki po wydaniu. A teraz pracujemy nad poprawnym monitorowaniem wykorzystania zasobów węzłów klastra, wprowadzeniem nowych funkcji poprawiających UX, a także zbieraniem dużej ilości opinii otrzymanych po zainstalowaniu wtyczki zarówno przez naszych klientów, jak i od osób na GitHubie (jeśli opuścisz Twój problem lub żądanie ściągnięcia, będę bardzo szczęśliwy :)

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

Dziękuję!)

Źródło: www.habr.com

Dodaj komentarz