DevOps

Markdown – GitHub

Markdown – GitHub MarkDOWN, cheat sheet, link, table, image, …

Markdown to lekki język znaczników, który można używać do dodawania elementów formatowania do dokumentów tekstowych w postaci zwykłego tekstu. Jego celem jest łatwość w odczytywaniu i pisaniu. Za pomocą kilku znaczników pomoże nam podrasować wygląd tekstu, tak aby był bardziej przyjazny dla oka.

Markdown – wprowadzenie

Z tego materiału dowiesz się:

  • Czym jest Markdown?
  • Jaka jest składnia w Markdown’ie?
  • Czy można stosować Markdown w GitHubie?

Markdown

Markdown to lekki język znaczników, który można używać do dodawania elementów formatowania do dokumentów tekstowych w postaci zwykłego tekstu. Markdown jest obecnie jednym z najpopularniejszych języków znaczników na świecie. Jest on szeroko stosowany m.in. w komunikatorach, forach internetowych, stronach www, dokumentacji i plikach readme.

Używanie Markdown różni się od takich aplikacji jak np. Microsoft Word, w której klikasz przyciski, aby sformatować słowa i frazy, a zmiany są widoczne natychmiast. Natomiast kiedy tworzysz plik w formacie Markdown, dodajesz składnię Markdown do tekstu, aby wskazać, które słowa i frazy powinny wyglądać inaczej.

Przykład Markdown

Fragment markdown:

# Nagłówek

Zostanie zamieniony na następujący kod HTML:

<h1>Nagłówek</h1>

Kilka ciekawych faktów na temat Markdown’u:

  • Twórcami są John Gruber i Aaron Swartz.
  • Markdown został stworzony w 2004 roku.
  • Oryginalnie został zaimplementowany w Perlu.
  • W 2014 r. udostępniono jednoznaczną specyfikację i zestaw testów dla Markdown.

Markdown | Markdown tutorial

Markdown cechuje prostota, która zwiększa czytelność, jak i ułatwia pisanie tekstu. Wszystko fajnie, ale pewnie zastanawiasz się, po co Ci tak właściwie Markdown? 🙃

Istnieją dość dobrze rozwinięte edytory tekstów, w których za jednym kliknięciem możesz szybko sformatować kod. Jest kilka powodów, dla których duża grupa osób wybiera właśnie Markdown:

  • Popularność – markdown stosuję się naprawdę w wielu miejscach, gdzie tworzy się tekst. Przykładowo jest używany do tworzenia stron internetowych, dokumentów, notatek, książek, prezentacji, wiadomości e-mail oraz dokumentacji technicznej. Są to elementy tak naprawdę, z których korzysta praktycznie każdy, kto obecnie nie żyje w jaskini pośrodku niczego 😏
  • Pliki zawierające tekst w formacie Markdown można otwierać za pomocą praktycznie dowolnej aplikacji. Jeśli uznasz, że nie podoba Ci się aktualnie używana aplikacja Markdown, możesz zaimportować swoje pliki Markdown do innej aplikacji Markdown. Stanowi to wyraźny kontrast z aplikacjami do przetwarzania tekstu, takimi jak Microsoft Word, które blokują zawartość w zastrzeżonym formacie pliku.
  • Markdown jest niezależny od platformy. Tekst w formacie Markdown można tworzyć na dowolnym urządzeniu z dowolnym systemem operacyjnym.
  • Obecnie wiele aplikacji oraz witryn internetowych obsługuję Markdown m.in. takie witryny jak Reddit czy GitHub.

Powyższe argumenty oraz łatwość użytkowania Markdown’u sprawiają, że z przyjemnością z niego korzystam w codziennej pracy.

Markdown editor

Istnieje sporo możliwości, jeżeli chodzi o wybór edytora Markdown’u. Wszystko tak naprawdę zależy od Twoich osobistych preferencji. Przykładowo – możesz użyć Visual Studio Code, Typora lub iA Writer, czy IntelliJ Idea – czyli naszego popularnego IDE.

Markdown cheat sheet | Ściągawka

Kiedy zaczynasz używać Markdown’u dobrze mieć małą ściągawkę, do której można zajrzeć w każdej chwili. Poniżej znajdziesz taką właśnie ściągawkę, która pomoże Ci w przyswojeniu elementów składni Markdown. Sam Markdown również udostępnia nam naprawdę przystępną i zrozumiała dokumentację, do której warto zajrzeć.

Markdown syntax | Składnia

Liczba opcji formatowania w Markdown jest dosyć ograniczona. Warto dlatego jest się nauczyć na pamięć, co niewątpliwie przyspieszy proces tworzenia i formatowania praktycznie dowolnego tekstu.

Markdown heading | Nagłówek

Aby uzyskać nagłówek, należy zastosować #<tekst> co będzie równe H1, ##<tekst> to H2 itd. np. #Hello lub ##Hello.

Markdown – Markdown syntax Markdown nagłówek

Markdown bold | Pogrubienie

W celu pogrubienia tekstu należy zastosować jedną z poniższych opcji:

  • **<tekst>** np. **Hello**,
  •  __<tekst>__ np. __Hello_.

Markdown – Markdown syntax Markdown bold

Markdown italic | Kursywa

Aby uzyskać kursywę tekstu, należy zastosować jedną z poniższych opcji:

  • *<tekst>* np. *Hello*,
  • _<tekst>_ np. _Hello_.

Markdown – Markdown syntax Markdown italic

Markdown bold & italic | Pogrubienie z kursywą

Możliwa jest również sytuacja, w której chcemy zarówno pogrubić tekst, jak i nadać mu kursywę.
W tym przypadku mamy kilka możliwości, aby to zrobić:

  • ***<tekst>*** np. ***Hello***,
  • ___<tekst>___ np. ___Hello___,
  • __*<tekst>*__ np. __*Hello*__,
  • **_<tekst>_** np. **_Hello_**.

Markdown – Markdown syntax Markdown bold italic

Markdown blockquote | Cytat

Jeżeli chcesz w tekście stworzyć tzw. blok cytatu (ang. blocquote), musisz dodać > <tekst> np. > Być albo nie być.
Blok cytatu powoduje dodanie charakterystycznego wcięcia z lewej strony.
Markdown – Markdown syntax Markdown bold

Markdown list | Lista

Lista uporządkowana (ang. ordered list)

Jeżeli chcesz w tekście stworzyć listę uporządkowaną, wystarczy, że postąpisz według poniższego schematu:

  • 1.<tekst> następnie 2.<tekst> itd. np. 1. Pierwszy, 2. Drugi.

Alternatywnie możesz nawet wypisać kolejne punkty jako:

  • 1. <tekst> 1.<tekst> 1.<tekst> np. 1. Pierwszy, 1. Drugi, 1. Trzeci,
  • 1.<tekst> 5.<tekst> 7.<tekst> np. 1. Pierwszy, 5. Drugi, 7. Trzeci.

Efekt końcowy będzie taki sam – lista sformatuje się poprawnie, wyliczając po kolei elementy.

 

Markdown – Markdown syntax Markdown list

 

Istnieje również możliwość stworzenia zagnieżdżonej listy:

Markdown – Markdown syntax Markdown list

Lista nieuporządkowana (ang. unordered list)

Jeżeli chcesz w tekście stworzyć listę nieuporządkowaną, musisz dodać odpowiedni symbol do każdej kolejnej linii, która ma tworzyć listę:

  • <tekst> następnie -<tekst> itd. np. – Pierwszy, – Drugi,
  • +<tekst> następnie +<tekst> itd. np. +Pierwszy, +Drugi,
  • *<tekst> następnie *<tekst> itd. np. *Pierwszy, *Drugi.

Markdown – Markdown syntax Markdown unordered list

Analogicznie jak w przypadku listy uporządkowanej i w tym przypadku możesz stworzyć zagnieżdżoną listę.

Lista z zadaniami (ang. task list)

Ciekawą opcją jest możliwość tworzenia tzw. task lists. Aby utworzyć taką listę, musisz dodać przed elementem listy – [x] lub – []:

  • – [x] <zadanie> (jeżeli task jest wykonany), – [] <zadanie> (jeżeli task nie został jeszcze wykonany) np.
    – [x] Zrobić zakupu,
    – [] Umyć naczynia.

Markdown – Markdown syntax Markdown task list

Markdown code | Kod

Aby dodać do tekstu fragment kodu, wystarczy użyć `<code>` np. `int i = 1;`.

Markdown – Markdown syntax Markdown code

Markdown horizontal | Pozioma linia

Aby utworzyć poziomą linię, należy użyć lub ***.

Markdown – Markdown syntax Markdown horizontal

Markdown link

Chcąc dodać link, należy użyć [<nazwa>](<url>)
np. [StormIt](https://www.stormit.pl).

Markdown image | Zdjęcie

Dodanie zdjęcia jest zbliżone do dodawania linku:
![<alt-tekst>](<zdjęcie>)
np. ![Markdown image](Markdown.jpg)

Markdown new line | Nowa linia

Chcąc dodać tekst w nowej linii, nie wystarczy pojedynczy enter.
Aby rozpocząć tekst w nowej linii, zastosuj podwójny enter lub html’owy znacznik <br>.

Markdown table | Tabela

Stworzenie tabeli w Markdown’ie wymaga trochę więcej pracy.

  • Kolumnę wyznacza się, używając ||.
  • Aby wyznaczyć nowy wiersz, wystarczy w nowej linii stworzyć kolejną kolumnę.
  • Jeżeli chcesz oznaczyć pierwszy wiersz jako nagłówek, musisz oddzielić go od kolejnego wiersza | ———– |.

Tabela dostosowuje swój rozmiar automatycznie do treści.

Markdown – Markdown syntax Markdown table

Możliwe jest również wyjustowanie tekstu. Aby to zrobić zamiast | ———– | zastosuj jedną z opcji:

  • | :—       | – Wyjustowanie do lewej,

  • :—-:   | – Wyśrodkowanie,

  • |       —: | – Wyjustowanie do prawej.

Nie musisz koniecznie ustawiać jednej opcji dla całej tabeli. Możesz również modyfikować pojedyncze kolumny.

Markdown comment | Komentarz

Zdarzają się sytuacje, kiedy chcemy pozostawić komentarz, który ma nie być widoczny, wtedy wykorzystaj jedną z poniższych opcji:

  • [comment]: <> (<komentarz>) np. [comment]: <> (Jestem komentarzem).
  • [//]: <> (<komentarz>) np. [//]: <> (Jestem komentarzem).

Markdown – Markdown syntax Markdown comment

 

Github Markdown

GitHub to bardzo wszechstronne narzędzie, które ciągle się rozwija. Posiada on również w swojej „ofercie” możliwość korzystania z Markdown’u.

GitHub łączy składnię do formatowania tekstu o nazwie GitHub Flavored Markdown z kilkoma unikalnymi funkcjami pisania.

Oprócz formatowania Markdown takiego jak pogrubienie i kursywa oraz tworzenie nagłówków, linków i list, pasek narzędzi zawiera funkcje specyficzne dla GitHub, takie jak @wzmianki, listy zadań oraz linki do problemów (ang. issues) i pull request’ów.

GitHub umożliwia włączenie czcionki o stałej szerokości w każdym polu komentarza w serwisie GitHub. Każdy znak w czcionce o stałej szerokości zajmuje tę samą przestrzeń poziomą, co może ułatwić edycję zaawansowanych struktur Markdown, takich jak tabele i fragmenty kodu.

Aby włączyć opcję stałej szerokości czcionki na swoim koncie GitHub, musisz udać się do zakładki ustawień (ang. settings).

Markdown – Markdown syntax Markdown GitHub

Następnie wejdź do zakładki wyglądu (ang. appearance).

Markdown – Markdown syntax Markdown GitHub

W ostatni, kroku musisz odhaczyć opcję dot. czcionki i już od teraz możesz korzystać z tej funkcjonalności.

Markdown – Markdown syntax Markdown GitHub

W tym materiale tylko wspominam o GitHub’ie w kontekście Markdown’u. Jeżeli nie masz jeszcze konta na GitHub’ie lub chciałbyś bliżej zapoznać się z tym narzędziem, to zapraszam do poniższego wpisu:

➡ ZOBACZ 👉: GitHub tutorial | GitHub desktop, GitHub actions

Markdown – podsumowanie

W ramach tego materiału zapoznaliśmy się z tematem Markdown. Poznaliśmy również składnię Markdown’u, która przyda Ci się w codziennej pracy z tekstem.

Jeżeli spodobał Ci się ten materiał lub jeśli dopiero co zaczynasz swoją przygodę z programowaniem i chcesz dobrze wejść w świat deweloperów, zapraszam Cię do zapoznania się z moim programem dotyczącym Javy:

➡ ZOBACZ 👉: Java od podstaw

No comments
Share:
Automatyzacja monitoringu – AlertManager

Automatyzacja monitoringu dzięki alarmom

Jest to druga część z serii wpisów o monitorowaniu aplikacji, dlatego, jeżeli jeszcze nie miałeś okazji przeczytać pierwszego tekstu, to gorąco zachęcam do zapoznania się najpierw z wpisem o Prometheus. Znajdziesz tam podstawowe założenia budowanego przez nas nowoczesnego systemu monitoringu oraz wprowadzenie do obsługi samego Promethues. Dziś natomiast skupimy się przede wszystkim na dopracowywaniu i automatyzacji procesów monitorowania aplikacji oraz weryfikacji poprawności jej działania.

[prometheus-monitoring-linki]

W poprzedniej części powiedzieliśmy sobie, jak i dlaczego powinniśmy zbierać różnego rodzaju metryki. Jednak samo gromadzenie danych to w dzisiejszych czasach zdecydowanie za mało – trzeba jeszcze wiedzieć, co z tymi danymi można zrobić.

Jakie decyzje możemy podjąć na podstawie zbieranych metryk?

W idealnej sytuacji powinniśmy mieć zawczasu przemyślane wszystkie potencjalne sytuacje, do jakich może dojść w naszym systemie, oraz – co równie ważne – jak wtedy powinniśmy zareagować. Jeżeli będziemy tylko patrzeć na nasze metryki, to poza mądrze wyglądającymi zbiorami danych niewiele z tego wyniknie.

Jednym z pierwszych kroków w monitoringu, jaki musimy zrobić, jest ustalenie, co właściwie chcemy obserwować i jakie wartości tych metryk uznamy za niepokojące, a jakie za wymagające wręcz natychmiastowej interwencji. Dopiero po tak przygotowanych procedurach można przejść do kolejnego etapu, czyli definiowania alarmów.

Najlepiej, by już na etapie projektowania naszego rozwiązania ustalić te wartości i później tylko lekko je dopracowywać, ponieważ takie założenia mogą wpłynąć na wiele decyzji projektowych – np. czy chcemy zoptymalizować naszą aplikację pod kątem wielokrotnych odczytów i stosunkowo rzadkich modyfikacji danych, czy odwrotnie.

Przeanalizujmy to na trzech prostych przykładach.

Zużycie dysku na serwerze

Jedną z łatwiejszych i bardziej przydatnych rzeczy, jaką możemy monitorować, jest ilość wykorzystywanych zasobów dyskowych. W wielu różnych projektach trafiałem na problemy z brakiem miejsca na dysku. Przykładowo Jenkins działał bezproblemowo od wielu miesięcy, aż tu nagle, bez żadnej dodatkowej aktualizacji czy zmiany ustawień, wszystkie kolejne buildy na wszystkich projektach świecą się na czerwono i straszą developerów.

O ile wydarzy się to w dniu wolnym od pracy lub gdy nie mamy zbyt dużego obciążenia projektowego, to jeszcze pół biedy. Można wtedy na spokojnie poszukać przyczyny problemu i go rozwiązać. Zazwyczaj jednak takie rzeczy, zgodnie z prawem Murphy’ego, przytrafiają się w najgorszym możliwym momencie, czyli np. gdy klient ciągle do nas telefonuje i czeka na pilną poprawkę błędu, który całkowicie zablokował mu system 🙂

Tłumaczenie, że właśnie skończyło nam się miejsce na dysku, nie jest zbyt profesjonalne – zwłaszcza że spokojnie można było zapobiec tego typu sytuacji.

Wystarczy, że będziemy monitorowali ilość zajętego miejsca na dysku i w odpowiednim momencie, np. przy wykorzystaniu 80% dostępnej przestrzeni, wzbudzimy odpowiedni alarm. Jeżeli wystarczająco wcześnie to zrobimy, to takie zgłoszenie może nawet chwilę poczekać, zanim skończymy inne ważniejsze rzeczy – w końcu JESZCZE się nie pali.

Jeżeli natomiast mamy dostępną bardzo dużą, prawie nieograniczoną przestrzeń dyskową i tak warto obserwować ilość zajętego miejsca – by nie płacić niepotrzebnie np. za zalegające stare logi itp.

Wykorzystanie CPU

Kolejnym zasobem, któremu warto się przyjrzeć, jest procesor, a właściwie – poziom jego wykorzystania. Dobrze dostrojone systemy powinny prawie całkowicie wykorzystywać dostępne zasoby i skalować się dopiero, gdy zaczynamy zbliżać się do wartości krytycznych – jak zawsze chodzi o pieniądze, nie ma sensu płacić za niewykorzystywany sprzęt.

Jeżeli jednak po wydaniu nowej wersji aplikacji średnie zużycie procesora wzrasta o 30% lub więcej, a jednocześnie nie widzimy zwiększonego ruchu – to wiedz, że coś się dzieje!

W takiej sytuacji system powinien zeskalować się, dokładając dodatkowe zasoby sprzętowe – tak, by mógł dalej poprawnie działać, jednocześnie nie blokując użytkownika. W tym samym czasie jednak odpowiednie alarmy powinny poinformować developerów, że dzieje się coś złego i trzeba szybko znaleźć przyczynę zwiększonego obciążenia. Dobrze by było, gdybyśmy już na tym etapie byli w stanie ustalić, która aplikacja konkretnie generuje większe obciążenie.

Poza obserwowaniem samych wartości liczbowych warto również przyglądać się trendom – przykładowo, jeżeli z miesiąca na miesiąc nasze odczyty są coraz wyższe, to – mimo iż nie osiągnęliśmy jeszcze wartości alarmowych – można przyjrzeć się sytuacji.

Ilość wywołań API

Jako ostatni przykład niech posłuży nam ilość wywołań naszego API. Załóżmy, że mamy restowe API, które odpowiedzialne jest za obsługę użytkowników w naszym systemie. Mamy zaimplementowane standardowe metody: GET – do pobierania użytkownika, POST – do dodawania, oraz PUT – do aktualizacji itp.

Wiemy również, że statystycznie pobieranie informacji powinno odbywać się przynajmniej 10 razy częściej niż ich aktualizacja. Jest to pierwsza rzecz, którą możemy monitorować, i jeżeli ta statystyka nie zostanie zachowana, może to świadczyć np. o uruchomieniu jakiejś migracji danych lub potencjalnej próbie ataku na nasz system. Natomiast jeżeli te wartości nie będą zachowane przez dłuższy czas, może się okazać, że nasze pierwotne założenia były błędne i powinniśmy zoptymalizować system pod kątem ilości zapisów, a nie odczytów.

W przypadku API restowych warto również obserwować zwracane kody odpowiedzi. W większości wypadków poprawne odpowiedzi w stylu 200, 201, 202 itp. powinny górować nad kodami błędów z kategorii 4** i 5**. Zwiększona ilość błędów może świadczyć o niepoprawnym działaniu naszej aplikacji lub aplikacji klienckiej, która próbuje się z nami połączyć.

Wystarczy przykładów, myślę, że jest to już zrozumiałe. Zobaczmy teraz, w jaki sposób obsługa alarmów została rozwiązana w środowisku Prometheus.

AlertManager – alarmy

AlertManager – alarmy

Alarmy w Prometheus (ang. alerts)

Prometheus definiuje alarmy jako warunek, który musi być spełniony, oraz czas określający, jak długo obserwowany warunek ma trwać by ogłosić alarm. Przykładowo wykorzystanie ponad 80% dysku przez pół godziny lub procesora na 90% przez 5 minut itp.

Każdy alarm składa się z poniższych własności:

  • unikatowa nazwa (ang. alert name) – nazwa alarmu, po której rozpoznamy, co się stało;
  • grupa alarmów (ang. alert group) – możemy np. pogrupować nasze alarmy według aplikacji lub konkretnej funkcjonalności;
  • warunek (ang. expression) – warunek, czyli wymagania, jakie muszą zostać spełnione, żeby podnieść alarm. W tym miejscu posługujemy się wyrażeniem w PromQL (czyli wewnętrznym języku wbudowanym w Prometheus – więcej na ten temat możesz przeczytać w poprzedniej części wpisu);
  • czas (ang. time) – określający, na jakiej przestrzeni czasu powyższy warunek musi być spełniony, by podnieść alarm. Dodatkowy warunek na czas został wprowadzony, by nie generować fałszywie prawdziwych (ang. false positive) alarmów w przypadku chwilowych wahań obserwowanych metryk;
  • etykiety (ang. labels) – czyli wszystkie dodatkowe etykiety, jakimi chcemy oznaczyć nasz alarm, np. poziom alarmu (ang. severity) – INFO, WARN, ERROR itp., lub zespół (ang. team), do którego później taki alarm ma zostać przekierowany. Jest to szczególnie przydatne, jeżeli później chcemy filtrować generowane alarmy.

Listę wszystkich alarmów zdefiniowanych w Prometheus możemy podejrzeć w zakładce /Alerts.

Prometheus alerts

Prometheus alerts

Stan alarmu

Każdy alarm może funkcjonować w jednym z trzech stanów:

  • nieaktywny (ang. inactive) – wszystko dobrze, warunek alarmu nie jest spełniony;
  • nierozstrzygnięty (ang. pending) – została zaobserwowana nieprawidłowość. Warunek jest spełniony, ale jeszcze nie minął czas zdefiniowany w alercie;
  • aktywny (ang. firing) – warunek był spełniony przez przynajmniej założony czas, co skutkuje podniesieniem alarmu i przekazaniem go do dalszych warstw systemu – za chwilę powiemy sobie, co może się stać z takim podniesionym alarmem.

Dodanie nowego alarmu

Żeby zdefiniować w systemie nowy alarm, należy sprawdzić konfigurację w pliku prometheus.yml. W tym miejscu powinna być wskazana ścieżka do pliku przechowującego definicje alarmów – domyślnie jest to plik alert_rules.yml.

rule_files:
   - alert_rules.yml

Poniżej przykładowy plik z definicją alarmu sprawdzającego ilość poprawnie zdeployowanych podów na Kubernetes:

groups:
- name: StormIT
  rules:
  - alert: StormIT-works  
    expr: sum by(job,app)(up) == 0
    for: 5m
    labels:
      severity: critical
    annotations:
      summary: StormIT.pl page doesn't work

Wiemy już, jak definiować własne alarmy oraz kiedy zostaną aktywowane. Zastanówmy się teraz, co może (i co powinno!) się stać po podniesieniu naszego alarmu.

AlertManager jako strażnik i zarządca alarmów

Jednym ze sposobów dalszej obsługi alarmów jest skorzystanie z AlertManagera. AlertManager, mimo iż stanowi niezależną aplikację, dostarczany jest razem z Prometheus. Do jego głównych zadań należy przede wszystkim grupowanie, filtrowanie i wyciszanie alarmów.

AlertManager

AlertManager

Zastanówmy się jednak najpierw, po co nam kolejna aplikacja, która jeszcze bardziej skomplikuje system monitoringu?

Jak to zwykle bywa, na początku wszystko jest proste, jednak z czasem sprawy się komplikują. Wyobraź sobie, że chcemy zająć się monitoringiem trochę większego systemu. Zdefiniowaliśmy piękne alarmy, mamy ich całą masę – każdy aspekt naszej aplikacji jest dokładnie obsłużony.

W tym momencie dochodzi do problemów z siecią na naszym środowisku produkcyjnym, np. z powodu zmiany konfiguracji routera sieciowego aplikacje nie mogą połączyć się z bazą danych. Zobacz, jak zachowałaby się teraz większość alarmów. Przy założeniu, że zdefiniowaliśmy wcześniej czujkę weryfikującą problemy z siecią, zostaniemy o tym poinformowani – jednak dodatkowo zostanie aktywowana bardzo duża ilość innych alarmów. Aplikacja cały czas będzie próbowała bezskutecznie połączyć się z bazą danych, dlatego zaobserwujemy zwiększony czas odpowiedzi z usług. Po pewnym czasie nasze serwisy zaczną zwracać niestandardową ilość błędów itd. Dosłownie zostaniemy zasypani ogromną ilością alarmów, a wszystkie będą efektem jednego błędu w konfiguracji routera.

Właśnie w takich sytuacjach z pomocą przychodzi nam AlertManager. Wróćmy jeszcze na chwilę do jego głównych zadań:

  • grupowanie – pierwsza i podstawowa funkcjonalność tej aplikacji to grupowanie alarmów. Jeżeli jesteśmy zalewani przez ogromną ilość takich samych alarmów, to możemy tak skonfigurować AlertManager, by przepuścił tylko pierwszy alarm danej kategorii, a później wysyłał już zgrupowaną informację, że taka sytuacja występuje np. co 30 minut.
  • filtrowanie – w przedstawionym przykładzie z góry wiemy, że jeżeli będą problemy z dostępem do bazy danych, to usługi zaczną zwracać większą ilość błędów, dlatego nie ma sensu raportować tego faktu, tylko lepiej skupić się na prawdziwym problemie. Możemy tak skonfigurować zależności między alarmami, że jeżeli alarm wyższego poziomu zostanie wzbudzony, to wszystkie zależne od niego będą już ignorowane.
  • wyciszanie – mamy również możliwość wyłączania konkretnych alarmów, np. na czas testów lub – jeżeli uznamy je za błędne – na czas ich naprawy.

Instalacja AlertManager

Przy instalacji AlertManager, podobnie jak podczas instalacji Prometheus, skorzystałem z przygotowanych obrazów Docker. Możesz je pobrać ze strony GitHub projektu lub bezpośrednio z Quay.

docker pull quay.io/prometheus/alertmanager

Konfiguracja AlertManager

W pierwszym kroku musimy wskazać w konfiguracji Prometheus, gdzie znajduje się instancja AlertManager.

# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets:
       - alertmanager:9093

W kolejnym kroku modyfikujemy już konfigurację samego AlertManager w pliku alertmanager.yml.

global:
  resolve_timeout: 5m

route:
  group_by: ['alertname']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 1h
  receiver: 'web.hook'
receivers:
- name: 'web.hook'
  webhook_configs:
  - url: 'http://127.0.0.1:5001/'

Powyższy przykład to podstawowa konfiguracja pozwalająca zgrupować alerty i przekazać je do zewnętrznego endpointu. AlertManager ma wbudowany jednak szereg różnych integracji, można go np. skonfigurować tak, by wysyłał komunikaty na adres e-mail, Hipchat, PagerDutyPushover, SlackOpsGenieVictorOpsWeChat itp. Więcej na ten temat możesz przeczytać w dokumentacji AlertManager Configuration.

W przykładzie pojawiły się aż trzy ustawienia odnośnie czasów, dlatego warto wyjaśnić, co one oznaczają:

  • group_wait – jak długo alarmy z tej samej grupy mają czekać w buforze przed pierwszym wysłaniem;
  • group_interval – jak długo system ma czekać na ponowne wysłanie grupy, jeżeli doszło do niej coś nowego;
  • repeat_interval – po jakim czasie wysłać ten sam alarm, jeżeli dalej jest aktywny.

Podsumowanie AlertManager

Wszelkiego rodzaju automatyzacje i monitoring to już swego rodzaju standard w nowoczesnych rozwiązaniach IT i zdecydowanie warto inwestować swój czas i wysiłek w ich wdrażanie.

Masz przed sobą gotowy przepis, jak można zorganizować monitoring w Twoim projekcie. Mam nadzieję, że na samym przeczytaniu się nie skończy i uda Ci się przynajmniej przemyśleć część przedstawionych tu rozwiązań. Dowiedziałeś się dziś:

  • jak automatyzować monitoring;
  • dlaczego warto monitorować aplikacje;
  • co warto monitorować;
  • jak działa system alarmów w Prometheus;
  • jaki jest cykl życia alarmu;
  • jak zdefiniować nowy alarm;
  • jak zainstalować i skonfigurować AlertManager;
  • jakie są główne zadania AlertManager.

Zastosowanie tej wiedzy w praktyce z pewnością pomoże Ci utrzymać Twoje aplikacje w lepszej kondycji i za sprawą automatyzacji z większym spokojem myśleć o kolejnych aktualizacjach systemu.

W kolejnej części serii przyjrzymy się bliżej możliwościom wizualizacji metryk w Grafana.

2 komentarze
Share:
Monitoring Prometheus

Prometheus jako serce nowoczesnego systemu monitoringu

Zajmiemy się dziś dość gorącym ostatnio tematem monitoringu rozbudowanych aplikacji z wykorzystaniem Prometheus, Alertmanager oraz Grafana. W kontekście coraz bardziej rozpowszechnionego podejścia DevOps, microservices oraz Kubernetes zdecydowanie już teraz warto zainteresować się tym tematem. Cały materiał ukaże się w formie minicyklu składającego się z trzech części (linki poniżej). Opowiemy sobie najpierw o zmianach, jakie zaszły w architekturze aplikacji i jakie wymusiły nowe podejście do monitoringu. Następnie przyjrzymy się bliżej jednemu z najpopularniejszych darmowych rozwiązań open source do monitoringu aplikacji online – Prometheus. W kolejnych wpisach natomiast omówię sposoby automatyzacji monitoringu, korzystając z alarmów, oraz przyjrzymy się możliwościom wizualizacji metryk w Grafana.

[prometheus-monitoring-linki]

Dlaczego w dzisiejszych czasach potrzebujemy dobrego monitoringu?

Jeszcze nie tak dawno większość nawet dużych projektów była realizowana z wykorzystaniem statycznych serwerów, do których można było podłączyć się za pomocą statycznego adresu IP. Odrobinę sytuację skomplikowało wprowadzenie różnego rodzaju maszyn wirtualnych oraz kolejnej warstwy wirtualizacji pod postacią obrazów Docker.

Mimo wszystko taka architektura była cały czas raczej statyczna i dość rzadko zmieniała swoją strukturę. W tak stabilnej konfiguracji sprzętowej był wdrażany również statyczny zbiór aplikacji. Z góry wiedzieliśmy, gdzie nasza aplikacja zostanie zdeployowana oraz co dokładnie znajduje się na którym serwerze.

To wszystko spowodowało, że stosunkowo niewielkim wysiłkiem można było się podłączyć do takiego serwera i zwyczajnie sprawdzić, co na nim się dzieje. Podstawowa znajomość Linuxa, komenda htop, przegląd logów i wiemy już, co dzieje się w naszym systemie.

Skalowanie systemu vs słabe wykorzystanie zasobów

To teraz spróbujmy trochę skomplikować sytuację, w jakiej działa nasz system. Załóżmy, że przy pomocy naszej aplikacji sprzedajemy bilety na różnego rodzaju koncerty. Zazwyczaj ruch jest umiarkowany, jednak od czasu do czasu trafia nam się wielka gwiazda typu Metallica czy Justin Bieber 😉 Ludzie wariują, a wraz z nimi statystyki naszej strony i oczywiście nasze serwery. Przypuśćmy, że ruch w takim okresie jest około 10 razy większy niż przeciętnie, a nawet 20–30 razy większy niż w środku nocy lub w niedzielę rano – czyli w okresie, gdy mało kto decyduje się na zakup biletów.

Jednym ze sposobów poradzenia sobie z takimi problemami jest utrzymywanie cały czas architektury gotowej do obsługi maksymalnego ruchu. O ile w przypadku niewielkiego projektu nie będzie to stanowiło wygórowanej ceny, o tyle w przypadku bardziej popularnych rozwiązań koszt takiej nadmiarowości może być ogromny!

A co gdybyśmy tak byli w stanie dynamicznie dodawać nowe maszyny do naszego klastra? A następnie bez uszczerbku na działaniu aplikacji ze strony naszych klientów przenieść bardziej obciążającą część aplikacji na nowe maszyny? I dodatkowo to wszystko działoby się całkowicie automatycznie, bez naszej ręcznej ingerencji?

Brzmi rewelacyjnie i do tego jest to realne. Z pomocą w podobnych problemach przychodzi nam Kubernetes. Jednak, jak za chwilę się przekonamy, w IT – jak w życiu – rzadko coś jest całkiem za darmo.

Jak Kubernetes zmienił stopnień skomplikowania architektury?

Wykorzystanie Kubernetes w projekcie daje niewątpliwie bardzo dużo dodatkowych korzyści, nie można jednak zapominać również o kosztach. Dodatkowa warstwa abstrakcji drastycznie podnosi poziom skomplikowania architektury systemu. Bardzo dużo rzeczy się zmienia, a wiele innych zwyczajnie podlega dynamizacji. Nawet ilość serwerów w klastrze obsługującym nasz system może się zmienić. Dodatkowo nie możemy założyć, gdzie nasza aplikacja zostanie wdrożona – to również może się zmienić podczas działania systemu. Wystarczy bowiem, że obciążenie na pierwotnym nodzie będzie zbyt duże i część podów z naszą aplikacją zostanie przeniesiona gdzie indziej.

Jest to tylko część problemów, z którymi trzeba się zmierzyć w nowej architekturze. Nie zrozum mnie źle – nie mam zamiaru nikogo zniechęcać do tego rozwiązania. Wręcz przeciwnie – jestem jego ogromnym zwolennikiem. Trzeba jedynie pamiętać, że w IT nie ma idealnych narzędzi, które rozwiążą wszystkie nasze problemy w każdej możliwej sytuacji. Dodatkowo należy pamiętać, że nowe technologie zazwyczaj wprowadzają nowe problemy, do których rozwiązania będziemy potrzebowali nowych narzędzi. Tak samo jest i w tym wypadku – standardowe podejście do monitoringu oraz istniejące narzędzia bardzo słabo sprawdzają się w środowisku skontenerowanym, przez co monitoring z ich wykorzystaniem często jest niewydajny lub nawet praktycznie niemożliwy.

Czy Prometheus warto stosować tylko z Kubernetes?

Zdecydowanie nie tylko. Kubernetes jest świetnym sposobem na skomplikowanie architektury w projekcie, jednak nie jest to jedyny sposób 🙂 Dobrze ogarnięty monitoring przyda się w większości trochę bardziej rozbudowanych projektów. Przygotowując się do tego wpisu, poukładałem trochę swoje aplikacje i przygotowałem monitoring oparty o Prometheus do tego bloga. Mimo iż jego architektura jest stosunkowo prosta, to i tutaj udało mi się skorzystać z wielu dobrodziejstw Prometheus.

Prometheus i WordPress

Nie jest to zbyt popularne połączenie, jednak zdecydowałem się to zrobić głównie z dwóch powodów. Po pierwsze od pewnego czasu już zbierałem się do automatyzacji monitoringu moich projektów, a po drugie chciałem zademonstrować, że monitorować można prawie wszystko – nawet aplikacje, które pierwotnie nie były do tego przystosowane.

Moja wersja monitoringu udostępnia kilka podstawowych metryk, które udało mi się zebrać z poziomu WordPress: ilość użytkowników, stron i postów oraz kilka podstawowych informacji z samego PHP.

Największe trudności, na jakie trafiłem, były związane z tym, że blog działa na współdzielonym hostingu, gdzie są dość mocne ograniczenia na instalowanie nowych rzeczy. Dlatego musiałem zdecydować się na ograniczoną wersję metryk i napisać je ręcznie. Ciężko się jednak temu dziwić, raczej mało kto chce w ten sposób monitorować aplikacje na współdzielonych hostingach.

function get_wordpress_metrics(){
    $posts = wp_count_posts();
    $n_posts_pub = $posts->publish;
    $n_posts_dra = $posts->draft;
    $n_pages = wp_count_posts('page');

    $result = 'wp_posts_total{status="published"} ' . $n_posts_pub . "\n";

    $result .= "# HELP wp_posts_draft_total Total number of posts published.\n";
    $result .= "# TYPE wp_posts_draft_total counter\n";
    $result .= 'wp_posts_total{status="draft"} ' . $n_posts_dra . "\n";

    $result .= "# HELP wp_pages_total Total number of posts published.\n";
    $result .= "# TYPE wp_pages_total counter\n";
    $result .= 'wp_pages_total{status="published"} ' . $n_pages->publish . "\n";
    $result .= 'wp_pages_total{status="draft"} ' . $n_pages->draft . "\n";

    return $result;
}

Architektura systemu monitoringu opartego o Prometheus

Prometheus architektura

Prometheus architektura

Powyższy obrazek przedstawia ogólny schemat nowoczesnego systemu monitoringu opartego o Prometheus. W samym sercu układu znajduje się oczywiście Prometheus, który odpowiedzialny jest za zbieranie, gromadzenie oraz zarządzanie wszystkimi informacjami statystycznymi. Poszczególne metryki mogą być zbierane z różnych systemów, np. z silnika bazy danych, Kubernetes, AWS, Kafka, Docker Engine itp. W celu automatyzacji monitoringu wykorzystywane są alerty, za które odpowiedzialny jest Alertmanager.

Alertmanager to niezależna aplikacja, która dostarczana jest w pakiecie Prometheus. Za jego pomocą można zarządzać wszelkimi automatycznymi zadaniami w monitoringu, np. wyślij maila do zespołu operations w przypadku zwiększonego zużycia pamięci lub w przypadku wykorzystania 90% przestrzeni dyskowej itp. Alertmanager daje również możliwość grupowania podobnych ostrzeżeń, wyciszania ich, przekierowywania do odpowiednich osób oraz powstrzymywania duplikatów.

Pushgateway to również niezależny komponent pozwalający integrować zewnętrzne systemy z Prometheus. Za jego pomocą można dostarczyć metryki.

Jednym ze sposobów na wizualizację metryk zebranych w systemie Prometheus jest wykorzystanie dashboardów w Grafana.

Prometheus – instalacja

Wszystkie niezbędne pliki możemy znaleźć i pobrać ze strony download projektu lub bezpośrednio z github.

Do wyboru mamy wiele sposobów na instalację aplikacji: gotowe prekompilowane pliki (Windows, Linux…), budowanie wszystkiego ręcznie ze źródeł czy – ostatecznie – uruchomienie gotowych obrazów Docker.

Ja zdecydowałem się na rozwiązanie z wykorzystaniem Docker, wcześniej odpowiednio modyfikując plik Dockerfile, dostosowując go do swoich potrzeb.

docker run --name prometheus -d -p 127.0.0.1:9090:9090 quay.io/prometheus/prometheus

W podstawowej wersji wystarczy uruchomić powyższą komendę i po chwili Prometheus powinien być dostępny pod adresem: http://localhost:9090/.

Czym jest metryka?

Większość nowoczesnych systemów monitoringu działa w oparciu o metryki. Metrykę możemy rozumieć jako swego rodzaju charakterystykę, która opisuje naszą aplikację, np. czas odpowiedzi, ilość błędów HTTP, czy bardziej biznesowe przypadki, jak ilość założonych kont lub zarejestrowanych użytkowników.

W ogólności możemy mierzyć prawie wszystko w naszych aplikacjach i nawet powinniśmy, jeżeli tylko stanowi to dla nas jakąś przydatną informację.

Wymiana informacji PULL vs PUSH

System monitoringu oparty o Prometheus może zbierać metryki na dwa główne sposoby: model push oraz pull.

  1. Model push.
    W tym scenariuszu nasze aplikacje są aktywne i to one są odpowiedzialne za wysłanie zgromadzonych metryk do Prometheus przez Pushgateway. Taki model najlepiej sprawdza się do gromadzenia informacji, np. z wynikami testów, lub innych cyklicznych zadań, które mogą być wykonywane nieregularnie i przez dłuższy okres.
  2. Model pull.
    W tej sytuacji aplikacja jest bierna i tylko przygotowuje swoje metryki w formie endpointu. Natomiast to Prometheus podejmuje decyzję, kiedy je pobrać. To podejście jest rekomendowane przez twórców aplikacji i powinno być wykorzystywane w większości przypadków.

W naszym systemie możemy oczywiście łączyć oba modele i część aplikacji obsłużyć jednym sposobem, a część – drugim.

Przygotowanie i wystawienie metryk dla Prometheus

Prometheus jest bardzo popularnym rozwiązaniem, co zaowocowało powstaniem bardzo rozbudowanego ekosystemu wokół niego. Dla nas jako programistów chcących skorzystać z tego rozwiązania jest to bardzo dobra wiadomość. W bardzo wielu przypadkach nie będziemy musieli sami przygotowywać eksportów z metrykami z zewnętrznych systemów, ale skorzystamy z gotowych rozwiązań. Listę najpopularniejszych dostępnych bibliotek znajdziemy w dokumentacji exportów, znajdują się tam gotowce, między innymi dla PostgreSQL, AWS, Kafka, JVM, Docker oraz wielu innych.

Własne customowe metryki

Od czasu do czasu nie obejdzie się jednak bez przygotowania własnych metryk, specyficznych dla naszego konkretnego rozwiązania. Możemy napisać je całkowicie ręcznie, np. korzystając z modelu pull, w którym wystawiany jest endpoint z metrykami. Wynikowe metryki to przecież tylko zwykły kawałek tekstu. Warto jednak rozważyć wykorzystanie jednej z bibliotek klientów. Osobiście miałem okazję wykorzystać dwie z nich dla Javy oraz PHP. W obu przypadkach byłem bardzo zadowolony z tego rozwiązania i obeszło się bez większych problemów.

@Service
public class DataSourcesExports {

    private Gauge current;

    @PreDestroy
    public void destroy() {
        CollectorRegistry.defaultRegistry.unregister(current);
    }

    @PostConstruct
    public void init() {
        current = Gauge.build("datasource", "Export datasource statistics")
                       .register();
    }

    public void registerNewDataSource(){
        current.inc();
    }

    public void deregisterDataSource(){
        current.dec();
    }
}

Wbudowane typy metryk w Prometheus

Biblioteki klienckie dla Prometheus oferują nam zazwyczaj cztery główne typy metryk, dzięki którym możemy obsłużyć nasze dane. Jednak Prometheus nie wykorzystuje jeszcze informacji o typie i spłaszcza wszystkie dane do prostych szeregów czasowych (ang. time series).

  1. Counter
    Ten typ odpowiedzialny jest za przechowywanie danych liczbowych, które mogą tylko rosnąć lub – zostać zminimalizowane do zera podczas restartu systemu. Counter może być wykorzystany np. do zbierania informacji o ilości HTTP requestów lub zarejestrowanych błędów itp.
  2. Gauge
    Metryki tego typu również przechowują wartości numeryczne, jednak tym razem mogą one rosnąć lub maleć w czasie, np. ilość zarejestrowanych data source w aplikacji lub obiektów w kolejce.
  3. Histogram
    Z wykorzystaniem histogramów najczęściej monitoruje się metryki, takie jak czas odpowiedzi. Przed rozpoczęciem pomiarów musimy najpierw zdefiniować możliwe przedziały wyników. W przypadku pomiaru czasu odpowiedzi może to być: poniżej jednej sekundy, między 1 sekundą a 2 sekundami, między 2 a 5 i powyżej 5. Następnie zaobserwowaną wartość przypisujemy do pasującego przedziału. W późniejszych obliczeniach posługujemy się już tylko informacją o ilości elementów w poszczególnych przedziałach, a nie konkretnymi wartościami.
  4. Summary
    Typ Summary działa bardzo podobnie do histogramu, jednak jest odrobinę bardziej rozbudowany, np. przechowuje informacje o łącznej ilości obserwacji.

System zadań w Prometheus

Wspomniany wcześniej model pobierania metryk w Prometheus pull wewnętrznie działa z wykorzystaniem zadań. W poszczególnych zadaniach możemy zdefiniować, gdzie Prometheus powinien szukać metryk, jak często, z wykorzystaniem jakiej autoryzacji itp., np. przeszukaj wszystkie pody z Kubernetes z etykietą X o wartości Y lub sprawdź konkretny adres URL itp.

Poszczególne joby dodajemy w pliku konfiguracyjny prometheus.yml.

scrape_configs:
- job_name: prometheus
  scrape_interval: 30s
  scrape_timeout: 10s
  metrics_path: /metrics
  scheme: http
  static_configs:
  - targets:
    - localhost:9090

Zamieszczony przykład co 30 sekund sprawdzi adres http://localhost:9090/metrics  w poszukiwaniu metryk.

Możemy wyróżnić dwie główne kategorie zadań:

  1. Dla zdefiniowanego adresu URL lub stałej listy adresów, jak przykładowy job: job_name: prometheus.
  2. Dynamiczne zadania, w których określamy jedynie warunki, ale jeszcze nie znamy konkretnych adresów serwerów. Przykład poniżej działa z wykorzystaniem Kubernetes i dodaje wszystkie pody z labelką scrape_job == kubernetes-common-pods.
- job_name: 'kubernetes-common-pods'
    - role: pod
    relabel_configs:
    - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape_job]
    action: keep
    regex: kubernetes-common-pods

    - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
    action: replace
    regex: ([^:]+)(?::\d+)?;(\d+)
    replacement: $1:$2
    target_label: __address__

    - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
    action: replace
    target_label: __metrics_path__
    regex: (.+)

    - action: labelmap
    regex: __meta_kubernetes_pod_label_(.+)

Prometheus – znalezione endpointy (ang. targets)

Zadania w Prometheus są wyjątkowo pomocne nie tylko dlatego, że pozwalają w wygodny sposób gromadzić metryki z wielu różnorodnych miejsc, ale również pozwalają w tym samym czasie w łatwy sposób zweryfikować stan naszego systemu.

Na stronie Status/Targets możemy sprawdzić, jakie endpointy z metrykami zostały odnalezione oraz czy ostatnia synchronizacja przebiegła pomyślnie. W przypadku niepowodzenia zobaczymy stosowny komunikat z przechwyconym błędnym kodem odpowiedzi.

Prometheus targets

Prometheus targets

Prometheus – znajdowanie usług (ang. service discovery)

W przypadku bardziej rozbudowanych definicji zadań przydatna może okazać się strona Status/Service Discovery. Zawiera ona dość szczegółowe informacje, w jaki sposób przebiegało konkretne odnajdowanie endpointu z metrykami, jakie labele były brane pod uwagę przy filtrowaniu, jakie endpointy zostały odrzucone itp.

Prometheus service discovery

Prometheus service discovery

Prometheus – przegląd metryk

Zebrane przez nas metryki możemy podejrzeć, korzystając z widoku Graph w Prometheus.

Prometheus metryki

Prometheus metryki

process_virtual_memory_bytes{instance="localhost:9090",job="prometheus"}	134479872

Pojedyncza metryka składa się z unikatowej nazwy, zbioru etykiet oraz przechowywanej wartości:

  • nazwa – powinna być unikatowa oraz zawierać wymowny prefix, który niejako zastępuje przestrzeń nazw (ang. namespace), w celu rozróżnienia różnych metryk.
  • etykiety – dopiero nazwa i unikatowy zbiór wartości etykiet określa konkretną metrykę. Dzięki etykietom można filtrować poszczególne wartości, grupować je, sumować itp.
  • wartość – liczba, którą widzimy w przykładzie, to najnowsza zaobserwowana wartość dla danej metryki, jednak Prometheus przechowuje wartość metryk jako pary wartości oraz czasu obserwacji. Jest to dużo lepiej widoczne, gdy na końcu metryki dodamy  [1h]  wymuszający pobranie wszystkich wartości z ostatniej godziny lub przejdziemy do zakładki Graph.
Prometheus metryki czas

Prometheus metryki czas

Prometheus pozwala nam również na wywołanie różnorodnych funkcji na przechowywanych metrykach, takich jak: abs (wartość absolutna), ceil (zaokrąglenie do góry), sort (sortowanie) oraz wielu innych. Więcej przykładów można znaleźć w dokumentacji na temat funkcji.

Prometheus, czy to wszystko?

Opowiedzieliśmy sobie dzisiaj o bardzo wielu rzeczach: jakie zmiany w architekturze systemów spowodowało wykorzystanie Kubernetes, jak zaprojektować i wdrożyć nowoczesny system monitoringu oraz – ostatecznie – jak skonfigurować i korzystać z Prometheus. Uzbrojony w taką wiedzę z pewnością możesz zacząć eksperymenty z własnym monitoringiem swoich mikrousług.

Nie ma jednak wątpliwości, że nie wyczerpaliśmy jeszcze całego tematu, dlatego w kolejnych tekstach postaram się przybliżyć temat automatyzacji monitoringu z wykorzystaniem alertów oraz wizualizacji metryk w Grafana.

PS. W strefie znajdziecie też slajdy z prezentacji, którą miałem niedawno okazję przeprowadzić właśnie na ten temat.

2 komentarze
Share: