Zaktualizowano 9 min czytania

Metryki i alerty: co mierzyć i kiedy budzić człowieka

Tysiące szeregów czasowych i dwieście powiadomień dziennie dają zero odpowiedzi i zespół, który przestał czytać kanał z alertami. Mały zestaw sygnałów i alerty, z których każdy wymaga działania.

Rys. 1. Sygnały trafiają do reguły z progiem i czasem trwania; poziom alertu wybiera kanał, a człowieka budzi tylko to, co wymaga działania teraz.

Agent monitoringu zbiera z każdego serwera kilka tysięcy metryk, a system wysyła dwieście powiadomień dziennie: „CPU 81%”, „certyfikat wygasa za 89 dni”, „host niedostępny” po każdym planowanym restarcie. Po miesiącu zespół wycisza kanał z alertami. Kiedy w nocy zapełnia się dysk bazy danych, właściwe powiadomienie ginie wśród pozostałych, a o awarii informują użytkownicy rano.

Oba objawy mają wspólne źródło: metryki zbierane bez pytania, na które mają odpowiadać, i alerty ustawiane bez pytania, co ma zrobić osoba, która je dostanie. Poniżej mały zestaw sygnałów, z którego buduje się alerty, oraz zasady, dzięki którym alert o trzeciej w nocy jest naprawdę tego wart.

Co mierzyć: cztery sygnały i trzy pytania

Dla usługi widocznej dla użytkowników, niezależnie od technologii, wystarczą cztery sygnały. Dla każdego zasobu fizycznego lub wirtualnego wystarczą trzy pytania.

ObiektSygnałPrzykładowa metrykaNa co odpowiada
UsługaOpóźnienie95. lub 99. percentyl czasu odpowiedziczy użytkownicy czekają
UsługaRuchżądania na sekundę, aktywne sesjekontekst dla pozostałych sygnałów
UsługaBłędyodsetek odpowiedzi 5xx, nieudane transakcjeczy żądania się udają
UsługaNasyceniewykorzystanie puli połączeń do bazy, długość kolejkijak blisko limitu jest najwęższy zasób
ZasóbWykorzystanieprocent CPU, zajętość dysku, ruch na interfejsiejaka część pojemności jest zajęta
ZasóbNasyceniekolejka procesora, czas oczekiwania na dysk, odrzucone pakietyile pracy czeka na zasób
ZasóbBłędybłędy odczytu i zapisu, błędy interfejsu, retransmisjeczy zasób działa poprawnie

Opóźnienie mierzy się percentylem, nie średnią. Średnia 300 ms może ukrywać, że co dwudziesty użytkownik czeka pięć sekund.

W środowisku Windows dochodzi kilka metryk spoza tych schematów: opóźnienie i błędy replikacji między kontrolerami domeny, liczba nieudanych uwierzytelnień w czasie, rozbieżność zegarów oraz wiek ostatniej udanej kopii zapasowej każdego systemu.

Czego nie zbierać

  • Metryk co sekundę tam, gdzie decyzje zapadają w skali minut. Interwał 15–60 sekund wystarcza niemal zawsze.
  • Metryk dla każdego procesu na każdej maszynie. Szczegóły procesu zbiera się tylko dla usług monitorowanych jako usługi.
  • Wartości, które należą do logów: identyfikatorów użytkowników, adresów IP, numerów żądań. Każda unikalna kombinacja etykiet to osobny szereg czasowy, więc taka metryka rozsadza bazę. Metryka liczy, log opisuje; podział opisuję w tekście Strategia logowania.

Alert wymaga działania

Każdy alert odpowiada na pytanie: co ma zrobić osoba, która go dostanie? Jeśli odpowiedź brzmi „nic, tylko wiedzieć”, to jest informacja na pulpit. Jeśli brzmi „sprawdzić, czy to coś poważnego”, alert jest za szeroki i trzeba go zawęzić.

Alarmuje się na objawy widoczne dla użytkowników, nie na przyczyny. Odsetek błędów i czas odpowiedzi wychwytują każdą przyczynę, także tę, której nikt nie przewidział. CPU na 90% przy normalnym czasie odpowiedzi nie jest powodem, żeby kogokolwiek budzić; metryki zasobów służą do diagnozy po tym, jak alert na objaw już przyszedł.

Poziomy alertów i kanały

PoziomKiedyKanałOczekiwana reakcjaPrzykład
Budziusługa nie działa dla użytkowników albo zaraz przestanietelefon lub aplikacja dyżurna, o każdej porzeminutyodsetek błędów 5xx powyżej 5% przez 10 minut
Zgłoszeniecoś wymaga uwagi w ciągu dnia roboczegozgłoszenie w systemie ITSMten sam lub następny dzień roboczydysk zapełni się w ciągu 7 dni
Tylko pulpittrend, kontekst, ostrzeżeniepulpit, raport tygodniowybrak powiadomieniaCPU powyżej 85% w godzinach szczytu

Alert bez przypisanego poziomu trafia na najniższy. W małej organizacji alertów poziomu „budzi” powinno być kilka w miesiącu, nie kilka dziennie.

Próg z czasem trwania w Zabbix

Okrągła liczba nie jest progiem. Próg wynika z historii: jeśli dysk zapełnia się o 1% tygodniowo, alert przy 80% daje miesiące zapasu, a jeśli o 5% dziennie, 80% to za późno. Do tego każdy alert ma warunek czasu trwania: chwilowy skok CPU podczas kopii zapasowej nie jest incydentem, 15 minut powyżej progu już tak.

Wyzwalacze w składni wprowadzonej w Zabbix 5.4 i obowiązującej w 6.0 LTS oraz nowszych:

# CPU powyżej 90% przez całe 15 minut (każda wartość z okresu)
min(/srv-app-01/system.cpu.util,15m)>90

# Scenariusz WWW „Portal” kończy się błędem przez 5 minut
min(/srv-web-01/web.test.fail[Portal],5m)>0

# Przy tempie z ostatniej doby /var osiągnie 95% w ciągu 7 dni
timeleft(/srv-app-01/vfs.fs.size[/var,pused],24h,95)<7d

Pierwsze dwa wyzwalacze mają poziom „budzi” albo „zgłoszenie” zależnie od usługi, trzeci zawsze „zgłoszenie”: daje tydzień na reakcję w godzinach pracy. Żeby problem nie migał na granicy progu, wyzwalacz dostaje osobne wyrażenie powrotu (Recovery expression), na przykład max(/srv-app-01/system.cpu.util,10m)<80. Problem zamyka się wtedy dopiero, gdy CPU przez 10 minut jest poniżej 80%.

Reguła Prometheus z czasem trwania

W Prometheus czas trwania określa pole for:. Warunek musi być spełniony przy każdej ewaluacji przez podany czas; wcześniej alert ma stan pending i nie trafia do Alertmanagera. Nazwy metryk i etykiet zależą od eksportera.

groups:
  - name: portal
    rules:
      - alert: PortalHighErrorRate
        expr: |
          sum(rate(http_requests_total{job="portal", status=~"5.."}[5m]))
            / sum(rate(http_requests_total{job="portal"}[5m])) > 0.05
        for: 10m
        labels:
          severity: page
        annotations:
          summary: "Portal: ponad 5% odpowiedzi z błędem 5xx od 10 minut"
          runbook_url: "https://wiki.example.com/runbooks/portal-5xx"

      - alert: DiskFullIn7Days
        expr: predict_linear(node_filesystem_avail_bytes{mountpoint="/var"}[6h], 7 * 24 * 3600) < 0
        for: 1h
        labels:
          severity: ticket
        annotations:
          summary: "{{ $labels.instance }}: /var zapełni się w ciągu 7 dni"
          runbook_url: "https://wiki.example.com/runbooks/disk-full"

Etykieta severity decyduje o kanale. W Alertmanagerze trasa domyślna nie wysyła niczego, więc alert bez poziomu zostaje na pulpicie:

route:
  receiver: dashboard-only        # brak poziomu = brak powiadomienia
  group_by: [alertname, job]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - matchers: ['severity="page"']
      receiver: on-call
    - matchers: ['severity="ticket"']
      receiver: itsm
      repeat_interval: 24h

receivers:
  - name: dashboard-only
  - name: on-call                 # tu konfiguracja powiadomień dyżurnych
  - name: itsm                    # tu webhook tworzący zgłoszenie

Grupowanie, okna serwisowe i eskalacja

Awaria jednego przełącznika generuje sto alertów o niedostępności, po jednym na każdy host za nim. Osoba dyżurna potrzebuje jednego. W Zabbix służą do tego zależności wyzwalaczy: problem hosta nie jest zgłaszany, gdy aktywny jest problem przełącznika, od którego zależy. W Alertmanagerze group_by łączy alerty w jedno powiadomienie, a reguły inhibit_rules wyciszają alerty usług, gdy aktywny jest alert o niedostępnym węźle.

Planowane prace są częścią procesu zmian: okres konserwacji w Zabbix albo wyciszenie (silence) w Alertmanagerze zakłada się przed restartem, z numerem zgłoszenia w komentarzu. Alert poziomu „budzi” bez potwierdzenia w ustalonym czasie, na przykład 15 minut, trafia do następnej osoby. Reguła eskalacji jest zapisana, żeby nikt o trzeciej w nocy nie zastanawiał się, kogo obudzić.

Każdy alert z instrukcją postępowania

Alert bez instrukcji jest testem wiedzy osoby dyżurnej przeprowadzanym w najgorszych warunkach. Instrukcja jest krótka i ma stałą strukturę:

Co oznacza:  ponad 5% odpowiedzi portalu kończy się błędem 5xx od 10 minut
Wpływ:       część użytkowników nie może się zalogować ani zapisać danych
Sprawdź:     1. pulpit „Portal”: który endpoint zwraca błędy
             2. log aplikacji z ostatnich 15 minut: wyjątki, przekroczenia czasu bazy
             3. czy w ostatniej godzinie było wdrożenie
Obejście:    wycofanie ostatniego wdrożenia albo restart puli aplikacji
Eskalacja:   właściciel aplikacji, jeśli po 30 minutach nie ma poprawy

Odnośnik do instrukcji jest częścią alertu: w Prometheus jako adnotacja runbook_url, w Zabbix w polu URL wyzwalacza. Dzięki temu trafia do powiadomienia razem z treścią alertu.

Retencja dwupoziomowa

PoziomRozdzielczośćOkres przechowywaniaDo czego
Dane suroweinterwał zbierania, 15–60 s7–14 dnidiagnoza incydentu
Agregatygodzinowe minimum, średnia i maksimum12 miesięcy lub dłużejplanowanie pojemności, trendy

W Zabbix ten podział jest wbudowany: każda pozycja ma osobny okres przechowywania historii (na przykład 14 dni) i trendów (na przykład 365 dni), a trendy to właśnie godzinowe agregaty. Lokalna baza Prometheus ma jedną retencję, ustawianą parametrem --storage.tsdb.retention.time. Dane długoterminowe przechowuje się więc zwykle w osobnym systemie, do którego trafiają zagregowane szeregi z reguł nagrywających (recording rules).

Przegląd raz w miesiącu

Raz w miesiącu zespół przegląda alerty z ostatnich 30 dni w dwóch grupach. Pierwsza to najgłośniejsze alerty: te, które wystrzeliły najczęściej albo trwały najdłużej. Druga to alerty bez reakcji: niepotwierdzone, bez zgłoszenia i bez śladu działania. Alert, który przez miesiąc wystrzelił dziesięć razy i za każdym razem nikt nic nie zrobił, jest poprawiany, degradowany do pulpitu albo usuwany. Liczba alertów maleje, a zaufanie do kanału rośnie.

Sprawdź u siebie

Które reguły alertów nie mają czasu trwania, poziomu albo instrukcji:

curl -s http://prometheus.example.com:9090/api/v1/rules \
  | jq -r '.data.groups[].rules[]
      | select(.type == "alerting")
      | select(.duration == 0 or .labels.severity == null or .annotations.runbook_url == null)
      | .name'

Pusty wynik jest dobry. Każda nazwa na liście to reguła do uzupełnienia: brak for: oznacza alert na chwilowy skok, brak severity alert, który nigdzie nie trafi, brak runbook_url alert bez instrukcji.

Które alerty były najgłośniejsze w ostatnich 30 dniach:

curl -sG http://prometheus.example.com:9090/api/v1/query \
  --data-urlencode 'query=topk(10, sum by (alertname) (count_over_time(ALERTS{alertstate="firing"}[30d])))' \
  | jq -r '.data.result[] | "\(.value[1])\t\(.metric.alertname)"'

Wynik to liczba ewaluacji w stanie firing; przy interwale ewaluacji jednej minuty odpowiada liczbie minut. Alert, który przez miesiąc był aktywny przez tysiące minut, sygnalizuje stały problem, którego nikt nie rozwiązuje, albo źle ustawiony próg. W Zabbix podobną listę daje raport Reports → Top 100 triggers za okres 30 dni.

Które alerty są wyciszone i od jak dawna:

amtool silence query --alertmanager.url=http://alertmanager.example.com:9093

Każde wyciszenie powinno mieć komentarz z numerem zgłoszenia i datę końca w ciągu dni, nie tygodni. Wyciszenie odnawiane od miesięcy to alert bez reakcji, tylko lepiej ukryty.