Największa pułapka przy „ML z monitoringu” jest banalna: bardzo łatwo zbudować dataset, który wygląda świetnie w notebooku, a po wdrożeniu daje dwa tryby pracy — cisza (przegapia incydenty) albo syrena alarmowa (spamuje fałszywymi alarmami). Zwykle nie jest to wina „złego modelu”, tylko tego, że surowe logi i metryki zostały przerobione na cechy (features) bez kontraktu operacyjnego: co przewidujemy, z jakim wyprzedzeniem, na jakim poziomie i co wolno wiedzieć w chwili predykcji.
Feature engineering dla adminów to nie lista transformacji z podręcznika. To sztuka robienia takich danych treningowych, które da się policzyć na czas, utrzymać po deployach i zmianach instrumentacji, oraz użyć w praktyce (alert, autoskalowanie, rollback, triage incydentu). Jeśli jedna z tych rzeczy nie działa — model zaczyna żyć własnym życiem.
Gdy ML z monitoringu „nie działa”: objawy, które widać dopiero po tygodniu pracy
Model ma wyniki offline, a w produkcji łapie nie to, co trzeba
Klasyczny scenariusz: budujesz dataset z metryk Prometheusa, logów z ELK/Loki albo CloudWatch, etykietujesz incydenty na podstawie ticketów i… wychodzi AUC 0.95. Po wdrożeniu model prawie nic nie łapie, albo zgłasza „anomalię” dokładnie wtedy, gdy każdy alert już i tak krzyczy. To jest sygnał, że cechy są spóźnione (opisują skutki, nie symptomy) albo do treningu przemyciła się informacja z przyszłości (data leakage).
W monitoringu szczególnie zdradliwe są metryki i logi, które rosną dopiero po eskalacji problemu (np. liczba wyjątków po stronie serwera, liczba time-outów), ale w datasetcie wyglądają jak świetni „predyktorzy”. Jeśli okno obserwacji obejmuje choć kawałek okresu awarii, model nauczy się: „awaria = dużo błędów”. Tyle że to nie jest prognoza, tylko stwierdzenie faktu.
Drugie źródło rozczarowania: model uczy się sygnałów ubocznych (np. obciążenia związanego z backupem, batch jobem, cyklicznym skanem), bo w danych treningowych przypadkiem korelują z incydentami. W realu ten związek znika i predykcja przestaje mieć sens.
„Dużo danych, mało sygnału”: tysiące metryk i logów, a incydentów niewiele
Admin ma do dyspozycji ogromne ilości danych, ale większość z nich to „tło”: regularne wahania, reset counterów po restarcie, niestabilne etykiety (labels), sample’owanie logów, a do tego naturalny szum produkcyjny. Feature engineering zaczyna przypominać łowienie igły w stogu siana.
Jeśli dorzucisz do modelu wszystko jak leci, pojawiają się dwa problemy naraz: kardynalność (np. endpointy, user_id, request_id, dynamiczne nazwy podów) i feature spaghetti (setki cech, których nie da się utrzymać i zrozumieć). Model może „nauczyć się” identyfikatorów zamiast zachowań systemu.
W praktyce widać to tak: działa na jednym klastrze, ale nie przenosi się na drugi; działa tydzień, po czym po deployu metryk i etykiet jest tyle, że jakość predykcji spada.
Cechy nieprzenośne: działa na jednym hoście/regionie, pada po zmianie wersji
Operacyjne dane treningowe są żywe. Zmieniasz wersję aplikacji, dochodzi nowy middleware, inna biblioteka HTTP, przestawiasz histogramy latencji, a nawet tylko zmieniasz nazwy pól w logach — i nagle cechy tracą sens. To jest drift (dryf danych) w najpraktyczniejszej postaci: zmiana instrumentacji.
Jeśli feature engineering nie jest zakotwiczony w stabilnych konceptach (np. „odsetek 5xx na usługę”, „p95 latencji per endpoint z top-N”, „liczba restartów w oknie”), tylko w surowych nazwach i etykietach, model będzie kruchy.
Pierwsze pytania, które naprawdę decydują o jakości danych treningowych
Zanim ktokolwiek zacznie liczyć cechy, sensownie jest odpowiedzieć sobie na zestaw pytań, które admin zadałby naturalnie, gdy ma potem z tym żyć na dyżurze:
- Co dokładnie ma być wykrywane lub przewidywane: awaria, degradacja, anomalia, regresja po deployu?
- Z jakim wyprzedzeniem ma to przyjść: 1 minuta, 15 minut, godzina?
- Na jakim poziomie działa predykcja: host, pod, node pool, usługa, endpoint?
- Co jest akcją po predykcji: alert do on-calla, automatyczne skalowanie, blokada rollout’u, ticket?
- Co wolno wiedzieć w chwili predykcji (event time vs ingest time, opóźnienia w logach, spóźnione metryki)?
Każda z tych odpowiedzi zmienia dataset. Bez nich feature engineering szybko staje się sztuką dla sztuki.
Skąd bierze się porażka — trzy przyczyny: cel, czas i „prawda” w danych
Nieostry cel operacyjny: model ma „wykrywać problemy”, czyli co?
„Wykrywanie problemów” jest zbyt pojemne. Dla admina problemem może być: spadek SLA, wzrost latencji tylko na jednym endpointcie, saturacja I/O na node’ach z jednym typem workloadu, powtarzające się resety OOMKill, albo regresja po deployu, która nie łamie SLA, ale zwiększa koszty.
Jeśli cel jest nieostry, dataset będzie miał niespójne etykiety. Z jednej strony do pozytywów trafią tylko „głośne” incydenty (bo tylko te mają ticket/postmortem), z drugiej strony negatywy będą zawierały ukryte problemy, których nikt nie nazwał incydentem. Model uczy się wtedy czegoś przypadkowego: często rozpoznaje intensywność działań ludzi (np. wzrost logowania, debug, ręczne restartowanie) zamiast symptomów systemowych.
Operacyjny kontrakt dla celu to minimum pięć elementów: jednostka (np. usługa), okno obserwacji (z jakiej przeszłości liczymy cechy), horyzont (z jakim wyprzedzeniem przewidujemy), definicja pozytywu (co jest „awarią”), tolerancja fałszywych alarmów (bo to koszt dyżuru).
Czas w monitoringu jest pułapką: przyszłość wchodzi tylnymi drzwiami
W operacyjnych danych są co najmniej dwa „czasy”: event time (kiedy zdarzenie faktycznie zaszło) i ingest time (kiedy dane dotarły do systemu). Metryki mogą być scrapowane co 15s, ale zapisane z opóźnieniem; logi mogą docierać partiami, retry’owane, a część może być dropowana w trakcie problemu sieciowego.
Jeśli dataset budujesz „po timestampie w bazie”, a nie po czasie zdarzenia, bardzo łatwo przemycić sygnał, którego w chwili predykcji nie byłoby. Przykład: log o błędzie pojawia się w systemie dopiero po kilku minutach, ale w datasetcie przypisujesz go do czasu, gdy został wygenerowany. Model uczy się przewidywać incydent na podstawie logów, których jeszcze nie ma.
Drugim kanałem leakage jest etykieta wyznaczona z danych, które później używasz jako cechy. Jeśli etykieta „incydent” wynika z przekroczenia progu error_rate, a error_rate jest w cechach — model będzie „przewidywał” własną definicję.
Etykiety są niepełne albo fałszywe: incydenty, maintenance, cisza w ticketach
Źródła etykiet w SRE/DevOps są praktyczne, ale niedoskonałe: ticketing (Jira), status page, alerty, postmortemy, wpisy w Slacku, zdarzenia z narzędzi APM. Zwykle opisują tylko część prawdy: te incydenty, które ktoś zauważył i uznał za warte formalizacji.
To prowadzi do biasu: pozytywy to często „wielkie awarie”, a model ma działać również na „małe degradacje”. Dodatkowo maintenance i planned work potrafią wyglądać jak incydenty w metrykach. Jeśli nie oznaczysz okien maintenance jako osobnej klasy albo nie odfiltrujesz ich, model nauczy się, że „deploy = problem” albo „rebalans klastra = awaria”.
W danych treningowych potrzebujesz rozróżnienia co najmniej trzech typów okresów: normalne, incydentowe, oraz nieużywalne do nauki (maintenance, migracje, testy obciążeniowe), które lepiej wyciąć niż udawać, że są „negatywami”.
Zaczynanie od właściwego problemu: trzy cele i jak każdy zmienia dane oraz cechy
Cel 1 — wykrycie awarii „tu i teraz” (detekcja)
Detekcja w trakcie jest najbliższa klasycznym alertom, ale różni się tym, że ML ma łączyć sygnały. Tu liczy się krótki czas reakcji i odporność na szum. Etykieta zwykle mówi: „w tym oknie czasowym usługa jest w incydencie”. Cechy mogą być liczone z krótkich okien (np. 1–5 minut) i mają wychwytywać odchylenia od normalności.

W detekcji szczególnie ważne są cechy „stabilne” i tanie: agregacje metryk, liczniki zdarzeń z logów, proste miary zmienności. Zbyt ciężkie cechy (np. embeddingi tekstowe) często przegrywają operacyjnie, bo koszt i opóźnienie liczenia rośnie, a model zaczyna reagować później niż człowiek.
Pułapka: jeśli „awaria” jest definiowana przez to samo, co cechy (np. error_rate), detekcja staje się tautologią. Rozwiązanie: do etykiety używać miar konsekwencji biznesowej (SLO, spadek sukcesów, wzrost latencji), a cechy budować też z sygnałów wcześniejszych (queue, retries, saturacja zasobów).
Cel 2 — predykcja degradacji za X minut (horyzont predykcji)
Predykcja „za 15 minut będzie źle” jest kusząca, bo daje czas na autoskalowanie lub prewencyjny rollback. Ale to też najtrudniejszy wariant do zrobienia uczciwie. Musisz zdefiniować horyzont (np. 15 minut), a dataset budować tak, by cechy pochodziły wyłącznie z okresu przed incydentem.
W predykcji większą rolę grają cechy dynamiki: trendy, przyspieszenia, narastanie kolejek, rosnący odsetek retry, zmiany w rozkładzie latencji (np. p95 rośnie, p50 stoi). Często lepiej działa „czy narasta” niż „jaki jest poziom”.
Pułapka: jeśli incydenty mają różną długość, a ty etykietujesz „pozytyw” jako każde okno, które jest w incydencie, model zaczyna rozpoznawać środek incydentu, nie jego początek. W predykcji zwykle interesuje cię moment wejścia w problem (onset), a nie trwanie.
Cel 3 — „coś się zmieniło po deployu” (change-point / monitoring regresji)
Ten cel jest bardzo praktyczny: po rollout’cie niby działa, ale latencja rośnie o kilka procent, liczba retry w tle wzrasta, a SLO jeszcze się trzyma. Chcesz wykryć regresję zanim stanie się incydentem. Tu etykiety bywają słabe (bo regresje rzadko mają ticket), więc częściej stosuje się detekcję zmian (change-point) albo porównywanie rozkładów „przed vs po”.
W tym podejściu cechy powinny umieć „porównać świat”: np. różnica p95 latencji względem baseline z ostatniej godziny/dnia, zmiana udziału kodów odpowiedzi, różnica w entropii komunikatów logów. Kluczowe jest uwzględnienie kontekstu: zdarzenia deploy/config change jako sygnał, a nie jako „przypadkowa korelacja”.
Pułapka: sezonowość i ruch. Jeśli porównasz „przed” i „po” bez kontroli na wolumen i porę dnia, model nauczy się grafiku użytkowników, a nie regresji.
Krótki, realistyczny przykład operacyjny: latencja po deployu rośnie, ale alerty milczą
Typowy obraz: po deployu p95 latencji lekko rośnie, ale jeszcze nie przekracza progu. W metrykach widać subtelne zmiany, w logach pojawia się trochę więcej retry/timeouts, ale to ginie w szumie. Cechy, które mają szansę to uchwycić, to nie „średnia latencja”, tylko:
- relacja p95/p50 (ogon dystrybucji robi się cięższy),
- liczba przekroczeń progu w oknie (np. ile razy p95 było > baseline + delta),
- wzrost rate retry w logach na endpoint,
- zmiana rozkładu kodów (np. wzrost 499/504),
- korelacja „latencja rośnie, a CPU stoi” (często wskazuje na I/O, locki, zewnętrzny dependency).
To są cechy, które admin potrafi potem zinterpretować w triage.
Metryki: jak z time series zrobić cechy, które wytrzymują produkcję (okna, trendy, relacje)
Okno czasowe jako podstawowa jednostka próbki (i jak dobrać długość)
W metrykach czasowych nie masz „rekordu klienta” jak w CRM. Masz strumień próbek. Żeby zrobić dataset treningowy, potrzebujesz zamienić czas na wiersze: każdy wiersz opisuje okno obserwacji dla danej jednostki (host/pod/service), a etykieta mówi, co stanie się w przyszłości (albo czy właśnie trwa incydent).
Dobór długości okna to kompromis:
- Za krótkie okno (np. 30s) daje dużo szumu i losowych pików.
- Za krótkie okno (np. 30s) daje dużo szumu i losowych pików.
- Za długie okno (np. 30–60 min) wygładza wszystko tak bardzo, że model „widzi” problem dopiero wtedy, gdy i tak byłby ticket.
- Okno powinno pasować do fizyki systemu: inny sens ma 5 minut dla saturacji CPU, a inny dla kolejki, która potrafi narastać godzinę.
Praktyczna metoda doboru: weź kilka znanych incydentów i zobacz, ile czasu przed onsetem widać pierwsze stabilne odchylenia. Jeśli sygnał pojawia się 8–12 minut wcześniej, okno 10 minut plus cechy trendu zwykle ma więcej sensu niż ślepe 1 minuta „bo realtime”.
Druga rzecz, która potrafi zepsuć dataset: krok przesuwania okna (stride). Gdy liczysz cechy co 10 sekund z okna 10 minut, kolejne wiersze są prawie identyczne, a walidacja robi się zbyt optymistyczna. Bezpieczniej jest przesuwać okno rzadziej (np. co 1–2 minuty) albo walidować po czasie i po jednostce (host/service), żeby model nie uczył się „tych samych” okien.
Na koniec jeszcze jedna pułapka produkcyjna: brakujące próbki. Jeśli agent nie scrapuje metryki w trakcie awarii sieci, to „dziura” sama w sobie jest sygnałem. Zdecyduj świadomie, czy brak ma oznaczać zero, poprzednią wartość (forward fill), czy osobną cechę typu missing_ratio. W przeciwnym razie model w treningu zobaczy idealnie ciągłe serie, a w realu dostanie poszarpane.
Najrozsądniejszy kolejny krok jest mało romantyczny: wybrać jeden cel (detekcja, predykcja albo regresja po zmianie), zbudować pojedynczy, uczciwy dataset z poprawnym czasem zdarzeń i prostymi cechami okiennymi, a dopiero potem dokładać złożoność — inaczej monitoring będzie „inteligentny” tylko na slajdach.
Agregacje, które dają sygnał, a nie tylko wygładzają
Najprostszy błąd w metrykach to robienie jednej agregacji „średnia z okna” i liczenie, że model sam znajdzie resztę. Średnia często maskuje to, co w operacjach jest najbardziej kosztowne: krótkie piki, ciężki ogon, narastające kolejki. Zamiast jednej liczby, lepiej zbudować mały zestaw agregacji, który opisuje okno z kilku stron.
Dla typowych metryk infrastrukturalnych i aplikacyjnych zestaw startowy wygląda tak:
- poziom: średnia / mediana / ostatnia wartość (dla „stanu” jak queue depth),
- ekstrema: max oraz percentyle (p90/p95/p99 dla latencji),
- zmienność: odchylenie standardowe, IQR (różnica p75-p25) albo współczynnik zmienności (std/mean),
- „ile razy było źle”: licznik przekroczeń progu w oknie (nawet jeśli próg jest roboczy),
- czas trwania: ile czasu metryka spędziła powyżej progu (dwell time), bo „pik przez 10s” i „pik przez 4 minuty” to inne zjawiska.
Ciekawostka, która ma praktyczne konsekwencje: dla wielu problemów produkcyjnych max bywa lepszym predyktorem niż średnia, bo awarie często zaczynają się od krótkich spiętrzeń (GC stop-the-world, lock, chwilowa utrata dependency), a nie od powolnego dryfu.
Tempo zmian: różnice, nachylenie i „przyspieszenie”
Jeśli masz model, który ma ostrzegać wcześniej, same poziomy są zwykle spóźnione. Lepszy sygnał daje to, jak szybko coś rośnie. Da się to policzyć prosto, bez magii:
- delta:
x(t) - x(t-1)albo różnica średnich między dwoma podoknami (np. ostatnie 2 min vs poprzednie 2 min), - nachylenie trendu: współczynnik prostej dopasowanej do wartości w oknie (w praktyce: „czy rośnie stale”),
- przyspieszenie: różnica delt (czy wzrost się „rozkręca”).
W metrykach typu counter (np. liczba błędów) nie używaj surowych wartości, tylko tempo: rate/increase. W przeciwnym razie model nauczy się „starości serwisu” (im dłużej działa, tym większy licznik), a nie tego, że właśnie rośnie liczba błędów.
Opóźnienia (lags): jak model ma „pamiętać” historię
Często to nie pojedyncze okno zdradza problem, tylko sekwencja. Prosty sposób: dodać te same cechy policzone dla kilku wcześniejszych okien (lags). Na przykład: dla okna kończącego się w chwili t liczysz error_rate_mean dla t, t-5m, t-10m.
Trzy praktyczne zasady, żeby nie zrobić z tego „feature spaghetti”:

- Weź mało lagów, ale sensownych (np. 1, 2, 3 kroki wstecz), zamiast 30 lagów „bo LSTM by to zjadło”.
- Lagi dobieraj do fizyki zjawiska: kolejka i backpressure mają inną bezwładność niż CPU.
- Waliduj po czasie: jeśli mieszasz losowo wiersze, lags i okna zachodzą na siebie, a wynik jest zbyt optymistyczny.
Relacje między metrykami: sygnały, które działają w triage
Pojedyncza metryka rzadko mówi „to na pewno to”. W operacjach zwykle szukasz relacji: CPU rośnie czy stoi? Latencja rośnie mimo stabilnego ruchu? Error rate rośnie tylko na jednym endpointcie? Te relacje dają cechy, które są też łatwe do wytłumaczenia zespołowi.
Dobry zestaw „relacyjnych” cech startowych:
- ogon vs środek:
p95/p50albop99/p95dla latencji (kiedy robi się ciężki ogon), - błędy na ruch:
errors / requests(zamiast „errors” jako licznik), - saturacja: np.
cpu_usage / cpu_limit,memory_working_set / memory_limit(w Kubernetes to często ważniejsze niż same usage), - skutki uboczne: retry rate vs error rate (czasem retry rośnie zanim error w ogóle „przebije się” do klienta),
- zależności: korelacja w oknie (np. latencja rośnie razem z queue depth) albo prosta cecha „obie rosną naraz” (AND trendów).
Uwaga operacyjna: korelacje liczone „globalnie” potrafią być zdradliwe, bo zależą od ruchu i pory dnia. Bezpieczniej liczyć relacje w krótkich oknach oraz odnosić je do baseline (np. „p95 dziś vs median p95 z podobnych okien wczoraj”).
Sezonowość i kalendarz: użyteczne, ale łatwo zrobić z tego wytrych
Dodanie cech typu „godzina dnia” bywa kuszące, bo model natychmiast poprawia wynik. Tyle że często poprawia go w zły sposób: uczy się grafiku użytkowników, a nie symptomów awarii. To działa dopóty, dopóki nie zmienisz kampanii marketingowej, regionu albo harmonogramu batchy.
Jeśli już używać sezonowości, to w wersji „operacyjnej”, czyli jako odniesienie do baseline, a nie jako samodzielną prawdę:
- odchylenie od typowego poziomu dla tej pory (np. obecne p95 minus median p95 z ostatnich N dni dla podobnego okna),
- rank/percentyl w historii (czy to jest w top 1% najgorszych okien?),
- cechy „batch”: jeśli znasz harmonogram ciężkich zadań, lepiej dodać flagę zdarzenia (batch start) niż liczyć, że „godzina 2:00” zawsze znaczy to samo.
Braki danych, restarty, rollingi: potraktuj je jak sygnał, nie jak błąd ETL
W monitoringu brak próbki rzadko jest neutralny. Może oznaczać: agent padł, scrape nie doszedł, pod został ubity, a sieć akurat się dławi. Jeśli w treningu „wygładzisz” wszystko forward fille’em, model nauczy się świata, w którym obserwacja jest idealna — a produkcja taka nie jest.
Praktyczny kompromis to policzyć dwie rzeczy naraz:
- wartość metryki po prostej imputacji (np. forward fill do 1–2 braków),
- osobne cechy jakości obserwacji:
missing_ratio,gap_max_seconds, liczba restartów w oknie (z metryk typuprocess_start_timealbo z eventów K8s).
W Kubernetes dodatkowo uważaj na „znikanie serii” przy rolloutach. Jeśli uczysz model per-pod, to po deployu to już inny pod (inna nazwa, inny czas życia). Często stabilniej jest agregować na poziomie service albo deployment, a identyfikatory efemeryczne trzymać z dala od cech.
Logi: cechy bez ciężkiego NLP — parsowanie, szablony i liczniki, które dają sygnał
Dlaczego surowy tekst logów prawie zawsze przegrywa
Logi kuszą, bo „tam jest prawda”: stacktrace, komunikat błędu, kontekst requestu. Problem w tym, że surowy tekst jest zmienny, pełen identyfikatorów (request_id, user_id), a do tego ma koszmarną kardynalność. Model potrafi nauczyć się rozpoznawać pojedynczy incydent po unikalnym fragmencie stacktrace — i wygląda świetnie na walidacji, a potem nie działa w kolejnym tygodniu.
Operacyjnie wygrywa podejście: z tekstu zrób stabilne zliczenia, a dopiero potem myśl o NLP. Najpierw chcesz wiedzieć, że „w tym oknie pojawiło się 300 timeoutów do bazy”, nie że „model zrozumiał semantykę zdania”.
Najpierw struktura: poziom logu, komponent, endpoint, kod
Jeśli logi są choć trochę ustrukturyzowane (JSON, key=value), masz gotowe cechy o świetnym stosunku sygnału do kosztu. Przykłady, które zazwyczaj „niosą”:
- liczba wpisów per severity (ERROR/WARN/INFO) w oknie,
- rozkład kodów HTTP i ich zmiana względem baseline,
- top komponentów/klas, które emitują błędy (ale w wersji ograniczonej, np. top N),
- odsetek requestów z retry, timeoutem, circuit breaker open (jeśli masz takie pola).
Uważaj na pola o wysokiej kardynalności (np. pełny URL z parametrami, user_id). Zamiast tego normalizuj: endpoint jako szablon trasy (/users/:id), host jako rola lub grupa, a identyfikatory wycinaj.
Szablony komunikatów: „to samo, tylko z innym ID”
Duża część logów to powtarzalne komunikaty z wstrzykniętymi zmiennymi. Jeśli potraktujesz każdy wariant jako osobny tekst, dostajesz tysiące „unikalnych” zdarzeń, z których większość jest tym samym. Rozwiązanie: template mining (wydobywanie szablonów) albo prostsza normalizacja.
Praktyczne metody, od najprostszych:
- reguły normalizacji: zamień liczby, UUID, IP, długie hashe na tokeny typu
<NUM>,<UUID>, - grok/regex dla znanych formatów (nginx, JVM, DB),
- narzędzia do szablonów (Drain, Spell) — dostajesz ID szablonu i liczysz jego wystąpienia w oknie.
Na danych produkcyjnych „ID szablonu + licznik” jest często lepszą cechą niż embedding tekstu, bo jest stabilny, tani i da się go monitorować (widać, gdy pojawiają się nowe szablony po deployu).
Liczniki i rozkłady: jak zamienić logi na wektor cech
Gdy masz już szablony albo przynajmniej znormalizowane komunikaty, budujesz cechy jak w metrykach: okno czasowe i agregacje. Typowy zestaw:
- event counts: ile razy pojawił się dany szablon (albo grupa szablonów) w oknie,
- top N: wybierz N najczęstszych szablonów globalnie i licz tylko je; reszta idzie do koszyka „other”,
- entropy: entropia rozkładu szablonów (gdy nagle rośnie, często „coś się dzieje” — system emituje wiele różnych błędów),
- ratio: udział ERROR w całości logów, udział timeoutów wśród błędów, itp.
Mini-pułapka: liczenie „top N” na całym zbiorze potrafi wpuścić leakage, jeśli N wybierasz na podstawie okresu po incydencie. Bezpieczniej wybierać słownik na zbiorze treningowym, a potem zamrażać go dla walidacji i produkcji.
Łączenie logów z metrykami: ten sam zegar, ten sam kontekst
Logi i metryki muszą spotkać się w czasie i na wspólnej jednostce (host/pod/service). W praktyce to najczęstsze miejsce, gdzie projekt „ML z monitoringu” traci spójność: metryki są agregowane per-service, a logi zostają per-pod; metryki mają stały krok, a logi są burstowe.
Działający schemat to:
- wybrać wspólny poziom agregacji (często service lub deployment),
- przypisać logi do tej jednostki (np. labelami K8s, nazwą usługi, namespace),
- zrobić cechy logowe w tym samym oknie i tym samym stride co cechy metryk,
- dodać cechy „ruchu” (requests) jako normalizator: 100 błędów przy 100 requestach to co innego niż 100 błędów przy 1M requestów.
Jeśli logi przychodzą z opóźnieniem (częste w centralnym logowaniu), to dla predykcji może to być zabójcze. Wtedy logi mają sens bardziej jako wsparcie detekcji „tu i teraz” albo jako sygnał w postmortem, a nie jako feature do przewidywania 15 minut wcześniej.
Pułapki, które robią z datasetu „ładne liczby” zamiast narzędzia operacyjnego
Kardynalność etykiet i identyfikatorów: gdy model uczy się nazw, nie zjawisk
Najbardziej zdradliwe cechy to te, które wyglądają niewinnie: pod_name, node, availability_zone, pełny exception_class, pełny URL, tekst komunikatu. Model potrafi z nich zbudować „mapę” środowiska i przewidywać incydenty, bo ten node często był problematyczny, a tamta strefa miała awarie. Z punktu widzenia ML to „działa”, z punktu widzenia operacji — to tylko zakodowana historia.
Jeśli celem jest przenośność, identyfikatory trzeba:
- agregować (rola zamiast hosta, service zamiast poda),
- hashować tylko wtedy, gdy naprawdę musisz (i licz się z tym, że model będzie słabo generalizował),
- albo całkiem usuwać i zastępować cechami opisującymi stan (saturacja, kolejka, rozkłady kodów, template counts).
Dobry test: usuń wszystkie „nazwy własne” i zobacz, czy model nadal ma z czego wywnioskować ryzyko. Jeśli bez node i pod_name metryki typu saturacja CPU, opóźnienia i wzrost ERROR-ów nadal budują sensowny sygnał, to jesteś bliżej uczenia zjawisk, a nie mapy infrastruktury. Jeśli wynik leci na łeb na szyję, to nie znaczy, że ML jest zły — tylko że cechy są nieprzenośne.
W praktyce najczęściej wygrywa podejście „rolowe”: zamiast node trzymaj node_pool (np. „spot”, „gpu”, „default”), zamiast pełnego URL — szablon endpointu, zamiast pełnej klasy wyjątku — rodziny błędów (timeout/connection/reset/oom). A gdy już musisz zostawić identyfikator, traktuj go jako sygnał pomocniczy, nie główny: ogranicz wagę przez regularizację, pilnuj driftu, monitoruj importancje cech. Ciekawostka: pojedynczy „magiczny” identyfikator potrafi dać świetny wynik AUC, bo zadziała jak skrót do etykiety — i równie szybko się zdezaktualizuje po migracji lub rebalansie.
Da się też podejść do tematu jak do alarmów: jeśli problematyczny jest konkretny host, to pytanie brzmi, czy chcesz to wiedzieć, czy chcesz umieć przewidzieć awarię niezależnie od hosta. To dwa różne cele. Pierwszy przypomina system reputacji zasobów (operacyjnie użyteczny, ale mało „ML-owy”), drugi wymaga cech opisujących stan i zachowanie — bo inaczej model będzie tylko zgadywał na podstawie etykiet środowiska.
Leakage czasowy: model, który „przewiduje”, bo już widział skutki
W monitoringu najłatwiej o sprytny wyciek informacji z przyszłości. Klasyk: cechy liczone z okna, które zachodzi na moment incydentu, albo etykieta zbudowana z tej samej metryki, którą karmisz model. Przykład: label „awaria” to error_rate > X, a w cechach masz error_rate z tego samego pięciominutowego okna. Model nie przewiduje — on rozpoznaje definicję.
Trik, który ratuje projekty: konsekwentne przesunięcie czasu. Cechy liczysz do t, etykietę definiujesz na horyzoncie t + H (np. „czy w ciągu następnych 15 minut wystąpi SLO burn / incident”). Nawet w detekcji „tu i teraz” separuj moment obserwacji od momentu oceny — logi z opóźnieniem, metryki z różnym lagiem, wszystko to tworzy fałszywe przewagi na walidacji.
Leakage bywa też „organizacyjny”: po incydencie wjeżdża rollout z hotfixem, a model uczy się, że zmiana wersji = awaria, bo akurat w historii tak wyglądała korelacja. Jeśli wersja aplikacji (albo flaga feature) ma być cechą, to tylko wtedy, gdy odpowiada na realne pytanie operacyjne („ta wersja jest ryzykowna?”). W przeciwnym razie lepiej ją zostawić do analizy postmortem niż do predykcji.
Rozjechane definicje: „incydent”, „degradacja” i „błąd” to nie to samo
Nawet idealne cechy nie pomogą, jeśli etykieta jest pływająca. „Incydent” bywa tiketem w systemie, wpisem na statuspage, stroną w pagerze albo przekroczeniem burn rate. Te rzeczy mają inne opóźnienia, inne progi i inny poziom hałasu. Model trenowany na jednym źródle prawdy będzie wyglądał „źle”, gdy ocenisz go innym.
Najpraktyczniej jest dopasować etykietę do sposobu użycia. Jeśli celem jest wczesne ostrzeganie on-call, etykieta powinna być zbliżona do tego, co realnie budzi człowieka (SLO burn, page). Jeśli celem jest automatyczna klasyfikacja degradacji do triage, lepsze są etykiety z symptomów (np. „p95 latency wysokie”), bo są spójniejsze w czasie. W obu przypadkach pomaga prosty audyt: weź kilka zdarzeń „po ludzku”, sprawdź na osi czasu, kiedy pierwszy raz było widać sygnał w metrykach/logach i czy etykieta jest bliżej przyczyny, czy skutku.
Niestabilność cech: gdy po deployu „zmienia się świat”, a nie tylko model
Najbardziej frustrujący moment to ten, gdy model wygląda sensownie na danych z ostatnich tygodni, a po wdrożeniu nagle „głupieje”. Często nie dlatego, że zjawisko w systemie się zmieniło, tylko dlatego, że zmieniła się instrumentacja: nowe metryki, inne labelki, inny sampling logów, nowy exporter, zmienione poziomy logowania. Dla modelu to jak podmiana alfabetu.
Jeśli cecha ma być operacyjnie użyteczna, musi przeżyć:
- zmianę wersji (nowe endpointy, inne kody błędów, nowe wyjątki),
- zmianę topologii (więcej replik, inny autoscaling, inny node pool),
- zmianę pipeline’u observability (inne opóźnienie, downsampling, retencja).
Dlatego zamiast „surowych” sygnałów lepiej działa warstwa stabilizacji: normalizacje, ratio, agregacje per-rola, cechy oparte o zachowanie (trend, zmienność), a nie o nazwy metryk. Zysk jest podwójny: model mniej zależy od tego, jak opisujesz świat, a bardziej od tego, co świat robi.
Braki danych, restarty i „dziury”: nie maskuj ich, zamień w sygnał
W danych operacyjnych brak próbki rzadko znaczy „zero”. Częściej znaczy: padł agent, był restart, zniknął pod, ktoś zmienił scrape interval, a czasem po prostu system jest tak przeciążony, że telemetryka nie dojeżdża. Jeśli wypełnisz wszystko zerami, model dostanie fałszywe poczucie spokoju.
Praktyczna taktyka to rozdzielenie dwóch rzeczy: wartości i jakości obserwacji. Czyli obok cechy tworzysz jej „metrykę wiarygodności”. Minimalny zestaw, który często robi różnicę:
- missing_ratio w oknie: jaki procent próbek metryki nie dotarł,
- time_since_last_sample: ile czasu od ostatniej wartości,
- restart_count / uptime w oknie: czy coś się odradzało,
- scrape_interval_est: czy krok próbkowania „pływa” (objaw problemów z pipeline’em).
To nie jest tylko higiena. W praktyce „brak telemetryki” bywa jednym z pierwszych symptomów degradacji (kolejki, saturacja CPU, thundering herd), więc potraktowanie tego jako cechy potrafi podnieść czułość modelu bez dokładania skomplikowanych transformacji.
Jak dobrać horyzont i etykietę, żeby model dawał sygnał na czas
W operacjach nie chodzi o to, by model miał wysokie AUC w próżni. Chodzi o to, by dał użyteczne wyprzedzenie albo przynajmniej solidną detekcję „tu i teraz”. Najczęstsza przyczyna rozczarowania jest banalna: etykieta opisuje skutek (np. page po przekroczeniu progu), a cechy też opisują skutek — i nic nie zostaje do przewidzenia.
Trzy horyzonty, trzy różne zestawy cech
Dobór horyzontu H (ile minut do przodu) automatycznie zmienia, co ma sens jako feature. W uproszczeniu są trzy tryby:
- Detekcja (H≈0): „czy już jest źle?”. Działają ostre symptomy: error rate, timeouty, kolejki, p95/p99, bursty logów. To konkurencja dla alertów progowych — ML ma sens, jeśli chcesz mniej ręcznego strojenia i lepszą adaptację do różnych serwisów.
- Wczesne ostrzeganie (H=5–30 min): „czy zaraz będzie źle?”. Tu zaczynają wygrywać trendy, przyspieszenia (slope, delta), narastanie kolejek, rosnąca zmienność, zmiana rozkładów logów, sygnały saturacji (CPU throttling, GC, iowait).
- Ryzyko w dłuższym oknie (H=1–24 h): „czy ten komponent jest ryzykowny?”. Wchodzą cechy kontekstowe i „kondycyjne”: częstotliwość restartów, flapping, chroniczne burn rate, historyczna stabilność, sezonowość. To często bardziej scoring zdrowia niż predykcja pojedynczego incydentu.
Jeśli próbujesz robić H=60 minut, a jedyne dostępne sygnały pojawiają się 5 minut przed page’em, model nie ma z czego zbudować ostrzeżenia. Wtedy uczciwiej jest skrócić horyzont albo zmienić definicję etykiety na taką, która łapie wcześniejszy etap degradacji.
Etykieta z burn rate, a nie z „momentu w pagerze”
Pager jest wygodny jako „prawda”, ale ma duże opóźnienie i jest zależny od polityki alertów. Etykieta oparta o SLO bywa stabilniejsza, bo opisuje obiektywny stan usługi. Typowy wzorzec:
- definiujesz etykietę jako przekroczenie burn rate w oknie oceny (np. 5–30 min),
- cechy liczysz do chwili
t, - etykieta mówi: „czy w przedziale
[t, t+H]wejdziemy w burn”.
Taki label jest bliżej „degradacji”, a dalej od „organizacyjnych” artefaktów (kto skasował alert, kto miał dyżur, czy page poszedł). Nadal trzeba uważać, żeby w cechach nie mieć tych samych wskaźników w tym samym oknie, z którego budujesz etykietę — przesunięcie czasowe zostaje obowiązkowe.
Gdy nie masz etykiet: sensowne podejścia bez udawania, że „zaraz je zdobędziesz”
W wielu środowiskach historia incydentów jest uboga, rozproszona albo nie da się jej łatwo dopasować do osi czasu danych. To nie jest blokada — tylko zmiana gry. Zamiast klasyfikacji, częściej wygrywają metody, które nie wymagają etykiet, ale nadal dają operacyjny efekt.
Detekcja anomalii, która nie krzyczy na każdy deploy
Najprostsza anomalia to „odchylenie od normalnego”. Problem: normalne zachowanie zmienia się po deployach, skalowaniu i w cyklach dobowych. Żeby nie dostać detektora „anomalii zmian”, trzeba w feature engineering dorzucić kontekst:
- cechy per-rola/per-service, a nie per-pod (bo pody rotują),
- normalizacja ruchem (błędy na request, bajty na request),
- cechy różnicowe (delta, slope) zamiast surowych poziomów,
- znaczniki zmian: deploy, zmiana konfiguracji, autoscaling — jako osobne sygnały, żeby model „wiedział”, że środowisko się przestawiło.
Jeśli masz tylko jeden „globalny” model anomalii na wszystkie serwisy, to prawie zawsze kończy się to walką z różnicami skali. Bardziej praktyczne jest grupowanie usług (np. podobny ruch/latencja) i dopiero wtedy uczenie „normalności” w obrębie grupy.
Change-point: szukanie momentu, gdy metryka zmieniła reżim
Dla operacji często bardziej liczy się pytanie „kiedy zaczęło się psuć?” niż „czy jest anomalia w tej minucie?”. Change-point detection robi dokładnie to: wykrywa zmianę rozkładu (skok, trend, wzrost wariancji). W feature engineering pomaga, gdy dorzucisz cechy pod takie zmiany:
- krótkie i długie okna tego samego sygnału (np. 5 min vs 1 h) i ich różnica,
- miary „szorstkości” (wariancja, MAD) w oknie,
- relacje między sygnałami (np. rośnie latency, ale nie rośnie CPU — podejrzenie I/O lub sieci).
W praktyce change-point jest też dobrym „starterem etykiet”: możesz wziąć wykryte zmiany i ręcznie opisać ich małą próbkę, zamiast próbować etykietować całą historię od zera.
Decyzje architektoniczne, które determinują cechy (i odwrotnie)
Feature engineering w monitoringu nie jest warstwą „na końcu”. Cechy wynikają z tego, jak chcesz użyć modelu: centralny serwis scoringowy, agent przy węźle, batch raz na godzinę, streaming co minutę. Każde podejście narzuca ograniczenia na to, co da się policzyć na czas.
Batch kontra streaming: czy cecha musi być policzalna „od ręki”
Jeśli model ma działać w trybie alertowym, cechy muszą być:
- inkrementalne (da się je aktualizować w oknie bez liczenia całej historii),
- tanie (bez ciężkich joinów i ogromnych słowników),
- odporne na opóźnienia (metryki i logi przychodzą z lagiem).
To jest powód, dla którego proste agregacje (liczniki, ratio, slope, rolling stats) wygrywają w produkcji z wieloma „sprytnymi” transformacjami. Jeśli do obliczenia feature potrzebujesz pełnej doby danych i skomplikowanego dopasowania, a alert ma przyjść w 2 minuty — projekt zderzy się z rzeczywistością, nie z matematyką.
Per-host, per-pod, per-service: wybór, który ogranicza kardynalność
Poziom agregacji to nie detal. To decyzja o tym, czy uczysz się zjawisk infrastruktury, czy zachowania usługi:
- per-host ma sens dla problemów typu: noisy neighbor, dysk, sieć, kernel, saturacja węzła,
- per-pod bywa pułapką przez rotację i autoscaling (chyba że celem jest diagnostyka konkretnego workloada),
- per-service jest stabilne i zwykle najlepsze do wczesnego ostrzegania dla użytkownika końcowego.
Prosty test operacyjny: jeśli on-call po alarmie pyta „który serwis?” — idź w agregację per-service. Jeśli pyta „który node?” — per-host. Jeśli pyta „który pod?” i odpowiedź ma znaczenie tylko przez 10 minut, to model też będzie żył 10 minut, a potem uczył się śmieci.
Praktyczny finał: minimalny zestaw cech, który rzadko zawodzi
Gdy projekt grzęźnie w „feature spaghetti”, pomaga wrócić do zestawu bazowego, który jest policzalny, stabilny i ma sens niezależnie od technologii. Taki pakiet startowy (dla jednostki typu service lub node) zwykle zawiera:
- poziom: rolling mean/median dla kluczowych metryk (latency, error ratio, saturacja),
- trend: slope/delta w krótkim i długim oknie,
- zmienność: std/MAD, piki (max), liczba przekroczeń progu w oknie,
- relacje: ratio (errors/requests), korelacje objawów (latency vs queue, cpu vs throttling),
- logi: liczniki szablonów (top N + other), entropia szablonów, udział ERROR/timeout,
- jakość danych: missing_ratio, time_since_last_sample, restart/uptime,
- kontekst zmian: deploy/config change/autoscaling jako osobne flagi.
To jest zestaw, który da się utrzymać w produkcji i łatwo wytłumaczyć w postmortem. Jeśli na nim nie ma sygnału, dołożenie 200 kolejnych cech rzadko pomaga — wtedy problemem częściej jest etykieta, horyzont albo to, że monitoring nie mierzy tego, co naprawdę zapowiada awarię.
Mini-checklista przed trenowaniem: 10 minut, które oszczędza tydzień
- Czy cechy są policzone do czasu
t, a etykieta dotyczy[t, t+H]? - Czy logi i metryki są zgrane do tego samego kroku i tej samej jednostki (service/node)?
- Czy masz osobne cechy „braków” i restartów, zamiast wypełniać wszystko zerami?
- Czy usunąłeś „nazwy własne” (pod, node, pełny URL) albo przynajmniej je zredukowałeś?
- Czy słownik „top N” z logów jest zbudowany tylko na treningu i zamrożony?
- Czy cechy mają sens operacyjny: da się je policzyć na czas i utrzymać po zmianach?
Jeśli większość odpowiedzi brzmi „tak”, to zwykle widać wyraźnie, czy ML ma tu przewagę nad regułami. A jeśli „nie”, to też dobrze — bo wiesz dokładnie, gdzie jest przyczyna, zamiast dokładać kolejne warstwy modelu do danych, które nie są jeszcze danymi treningowymi.
Najważniejsze punkty
- Najczęstsza wpadka to dataset „piękny offline”, a bezużyteczny w produkcji — bo cechy nie mają kontraktu operacyjnego: co przewidujesz, z jakim wyprzedzeniem, na jakim poziomie (usługa/endpoint/host) i co model może „wiedzieć” w chwili predykcji.
- Jeśli model alarmuje dopiero wtedy, gdy i tak wszystko już krzyczy, to zwykle nie „zły algorytm”, tylko spóźnione cechy albo data leakage: do treningu wpadły sygnały z okresu awarii (np. skok 5xx), więc model uczy się stwierdzania faktu, nie prognozy.
- Monitoring kusi ilością, ale większość to tło i szum; dorzucenie „wszystkiego” kończy się kardynalnością i feature spaghetti — model zaczyna rozpoznawać identyfikatory (request_id, dynamiczne nazwy podów) zamiast zachowania systemu.
- Cechy muszą być przenośne i odporne na zmiany instrumentacji; wiązanie się z surowymi nazwami pól i etykietami jest kruche, lepiej opierać się o stabilne koncepty typu „odsetek 5xx na usługę”, „p95 latencji dla top-N endpointów”, „liczba restartów w oknie”.
- Jakość danych zaczyna się od pytań, które ktoś na dyżurze zada pierwszego dnia: co wykrywamy (awaria vs degradacja vs regresja po deployu), jaki horyzont (1 min vs 15 min), jaka akcja po predykcji (alert, autoskalowanie, blokada rollout’u) i jaki koszt fałszywych alarmów jest akceptowalny.






