Alarm, który był czerwony od pierwszego dnia

Zbudowaliśmy monitor, który miał wyłapywać kopie zapasowe, które po cichu przestają się wykonywać. Następnego ranka wył. Chwilę zajęło ustalenie, że to monitor był zepsuty — a jeszcze dłużej zauważenie, że miał rację w zupełnie innej sprawie.

Luka, którą zamykaliśmy

Projekt to platforma tradingowa. Jej baza danych jest co noc kopiowana i przenoszona na dwa osobne konta AWS — jedno dla izolacji kont, drugie w innym regionie na wypadek prawdziwej katastrofy regionalnej.

Alerty o porażkach były już podłączone: jeśli job kopiowania zawiedzie, odpala zdarzenie i ląduje na kanale czatu. To obejmuje kopię, która ruszyła i się wysypała.

Nie obejmuje kopii, która w ogóle nie ruszyła.

Usuń harmonogram, odłącz wybór zasobów, odbierz uprawnienie we właściwy sposób, a żaden job nigdy nie powstanie. Brak joba, brak porażki, brak zdarzenia, brak alertu. Kanał milczy — a milczenie to dokładnie to, jak wygląda zdrowa noc. Z samego kanału alertów tych dwóch stanów nie da się odróżnić, a ten cichy jest niebezpieczny.

Dodaliśmy więc kontrolę obecności: alarm, który odpala, gdy liczba zakończonych jobów kopiowania spadnie poniżej jednego w długim oknie, z brakującymi danymi jawnie traktowanymi jako przekroczenie. Ponieważ metryka istnieje tylko wtedy, gdy praca się dzieje, „brak danych” jest całym sednem.

Wdrożyliśmy. Następnego ranka każde środowisko było czerwone.

Pierwsze pytanie: czy to prawdziwe?

Instynkt przy czerwonym alarmie kopii dla bazy tradingowej każe zakładać najgorsze. Instynkt myli się na tyle często, że warto poświęcić minutę na sprawdzenie.

Dwa fakty przyszły szybko:

Joby kopiowania się kończyły. Ich lista pokazała czyste codzienne uruchomienia do obu miejsc docelowych przez poprzednie trzy dni. Nic nie stanęło.

Czerwone były wszystkie trzy środowiska, nie jedno. Ten sam wzorzec konfiguracji, wdrożony wszędzie, zawodzący wszędzie. Awarie infrastruktury zwykle nie respektują granic konfiguracji tak równo. To sygnatura błędu w rzeczy, którą właśnie wypuściłeś.

Sam powód stanu alarmu mówił to wprost:

Threshold Crossed: no datapoints were received for 2 periods and 2 missing
  datapoints were treated as [Breaching].

Nie „liczba wynosiła zero”. Żadnych punktów danych. Alarm obserwował serię metryki, która nie istniała.

Zwrot akcji: etykieta wskazuje w drugą stronę

Alarm był zawężony po nazwie vaultu, a my zawęziliśmy go do vaultu, z którego kopie są wykonywane. Wydawało się to oczywiście słuszne — metrykę publikuje konto źródłowe, więc na pewno jest oznaczona vaultem źródłowym.

Wylistowanie tego, co platforma faktycznie publikuje, rozstrzygnęło sprawę:

NumberOfBackupJobsCompleted     vault = <the local source vault>
NumberOfRecoveryPointsCompleted vault = <the local source vault>
NumberOfCopyJobsCompleted       vault = <destination A>
NumberOfCopyJobsCompleted       vault = <destination B>

Metryki jobów kopiowania są oznaczone vaultem docelowym. Nazwa vaultu źródłowego pojawia się tylko przy jobach kopii zapasowych i punktach odtworzenia. Nasz alarm prosił o kombinację, która nigdy nie jest emitowana, dostał ciszę i posłusznie potraktował ciszę jako przekroczenie — dokładnie tak, jak mu kazaliśmy.

Zwrot akcji wart więcej niż poprawka

Tygodnie wcześniej, wychodząc z tego samego błędnego założenia, zapisaliśmy pewne ograniczenie.

Rozumowanie szło tak: oba miejsca docelowe kopii współdzielą jeden vault źródłowy, więc oba trafiają do jednej serii metryki. Alarm na tej serii zostaje zielony, jeśli jedno miejsce docelowe po cichu umrze, bo drugie utrzymuje liczbę powyżej zera. Wykrycie tego wymagałoby monitoringu wewnątrz konta docelowego, które nie miało stosu alertów. Zapisaliśmy to jako zaakceptowaną lukę i poszliśmy dalej.

Każda część tego wynikała ze złej etykiety.

Ponieważ metryka jest kluczowana miejscem docelowym, każde miejsce docelowe ma własną serię. A to znaczy, że można po prostu uruchomić jeden alarm na miejsce docelowe — i porażka, którą spisaliśmy na straty jako niewykrywalną, staje się łatwym przypadkiem. Kopia w drugim regionie milknąca, gdy pierwsza dalej działa, to dokładnie scenariusz, przed którym ta kopia ma chronić, i teraz to wywołuje alert.

Udokumentowane ograniczenie rozpuściło się w dwie linie konfiguracji. Nigdy nie było ograniczeniem. Było błędnym założeniem w przebraniu ograniczenia.

Obok nowych alarmów zapisaliśmy notkę: nie scalajcie ich z powrotem w jeden. Agregat z obu miejsc docelowych świeci na zielono, gdy którekolwiek z nich samo nadal działa.

Zielony, ale nie zdrowy

Poprawiliśmy etykietę, wdrożyliśmy ponownie i wszystko zrobiło się zielone. Gotowe.

Tyle że powód stanu jednego alarmu brzmiał:

Threshold Crossed: 1 datapoint [1.0 (05:36:00)] was not less than the
  threshold (1.0) and 1 missing datapoint was treated as [Breaching].

Zielony — a już liczący jeden z dwóch punktów danych jako przekroczenie. Jedno szturchnięcie od odpalenia.

Pogoń za tym wyciągnęła ostatnią niespodziankę. Kiedy odpytujesz metrykę, platforma dzieli ją na kubełki według równych granic zegara — północ, południe. Kiedy alarm ocenia tę samą metrykę, tak nie robi. Jego okna są zakotwiczone w chwili utworzenia lub ostatniej aktualizacji alarmu.

Wdrożyliśmy o 05:34. Kopie kończą się między 05:23 a 05:45. Postawiliśmy granicę kubełka dokładnie na obserwowanym zdarzeniu, więc kilka minut zwykłych wahań wrzuca uruchomienie do sąsiedniego kubełka i fabrykuje lukę.

  wdrożenie 05:34 ──┐
                    ▼
  kubełek A ┤───────┼──────────────────────┤  kubełek B
            05:34   │                   17:34
                    │
       kopie kończą ~05:23 – 05:45  ── część ląduje na lewo od
                                        granicy, część na prawo

Nic w konfiguracji tego nie mówi. Dwie identyczne definicje wdrożone sześć godzin od siebie zachowują się różnie, a ta różnica jest niewidoczna w code review. Poprawką jest wdrażanie w czasie odległym od obserwowanego zdarzenia — czyli prawdziwy, nośny stan, który żyje wyłącznie w znaczniku czasu wdrożenia.

Sufit, o którym nikt nie mówi

Jeszcze jedna rzecz warta poznania, zanim to zbudujesz.

Naturalny projekt to okno nieco dłuższe niż harmonogram: dla joba dziennego „alarmuj, jeśli nic się nie zakończyło w ciągu około 26 godzin”. To daje trochę luzu na spóźnione uruchomienie.

Nie da się tego zbudować. Platforma ogranicza okno alarmu do 24 godzin — period × evaluation_periods ≤ 86400. Dla joba dziennego maksymalne okno to dokładnie interwał joba, co zostawia najwyżej jeden kubełek luzu, jakkolwiek ułożysz okresy.

To nie problem strojenia. Jeśli taki alarm zacznie trzepotać, nie strój go ponownie — tego kształtu nie da się uczynić wygodnym. Przejdź na coś, co przyjmuje próg wieku wprost („brak punktu odtworzenia nowszego niż 2 dni”), co takiego sufitu nie ma.

Wskazówki dla czytelnika

Alerty o porażkach i alerty o nieobecności to różne monitory. Mieć jeden i wierzyć, że ma się oba — to jest faktyczne ryzyko. Każdy zaplanowany, niewidoczny, wartościowy job — kopie zapasowe, replikacja, archiwizacja, uzgadnianie — potrzebuje kontroli obecności, bo nikt nie zauważa nieobecności, dopóki nie potrzebuje danej rzeczy.

Zanim zaufasz alertowi, poświęć minutę na „czy to prawdziwe?”. Sprawdź, czy praca pod spodem faktycznie się wykonała i czy alert zawiódł wszędzie naraz. Wzorzec zawodzący jednakowo w niezależnych środowiskach to błąd konfiguracji, a nie awaria.

Zła konfiguracja, która krzyczy pierwszego dnia, to prezent. Nasza była nie do przeoczenia, bo nieistniejąca metryka plus traktowanie braku jako przekroczenia przyszpila alarm na czerwono od urodzenia. Alternatywa — monitor, który cicho świeci na zielono, obserwując nic — to ten sam błąd z gorszym zakończeniem. Wybierając wartości domyślne, wybieraj te, które zawodzą głośno.

Czytaj uzasadnienie alertu, a nie jego kolor. „Zielony” i „zdrowy z zapasem” to różne twierdzenia, a odróżnia je tylko szczegółowy stan. Nasz był zielony i jeden punkt danych od odpalenia.

Sprawdź, w którą stronę wskazuje etykieta. Metryki o transferze mogą być oznaczone źródłem albo celem, a intuicyjne zgadywanie to rzut monetą. Wylistuj, co platforma faktycznie publikuje, zamiast to wnioskować — jedno polecenie, a u nas przy okazji zburzyło ograniczenie, z którym żyliśmy.

Uważaj na założenia, które już awansowały do dokumentacji. Nasze błędne przypuszczenie nie tylko zepsuło alarm; zostało zapisane jako ograniczenie architektoniczne i zaakceptowane. Kiedy poprawiasz fakt, wróć i sprawdź, co jeszcze na nim zbudowałeś.