Największe wpadki Cian

Największe wpadki Cian

Witam wszystkich! 

Nazywam się Nikita, jestem liderem zespołu inżynierów w Cian. Jednym z moich obowiązków w firmie jest doprowadzenie liczby incydentów związanych z infrastrukturą w produkcji do zera.
Temat, który poruszymy dalej, przysporzył nam wielu problemów, a celem tego artykułu jest zapobieżenie powtórzeniu naszych błędów lub przynajmniej zminimalizowanie ich wpływu. 

Wstęp

Za dawnych czasów, kiedy Cian składał się z monolitów i nie było żadnych wskazówek o mikrousługach, mierzyliśmy dostępność zasobów sprawdzając 3–5 stron. 

Jeśli odpowiedzi były - wszystko dobrze, jeśli nie było odpowiedzi przez dłuższy czas - alert. Ile czasu powinny nie działać, aby to uznawano za incydent, decydowali ludzie na spotkaniach. Zespół inżynierów zawsze uczestniczył w badaniu incydentu. Gdy badanie było zakończone, pisaliśmy post mortem - swoisty raport na e-mail w formacie: co się stało, jak długo trwało, co zrobiliśmy w danym momencie, co zrobimy w przyszłości. 

Główne strony serwisu lub jak rozumiemy, że dotknęliśmy dna

 
Aby jakoś zrozumieć priorytet błędu, wydzieliliśmy najważniejsze dla funkcjonalności biznesowej strony serwisu. Na ich podstawie liczymy liczbę udanych/niedanych żądań i timeoutów. W ten sposób mierzymy uptime. 

Powiedzmy, że ustaliliśmy, że są pewne superważne sekcje serwisu, które odpowiadają za główną usługę - wyszukiwanie i składanie ogłoszeń. Jeśli liczba żądań, które zakończyły się błędem, przekracza 1% - to jest to krytyczny incydent. Jeśli w ciągu 15 minut w godzinach szczytu procent błędów przekracza 0,1% - to również uznaje się za krytyczny incydent. Te kryteria pokrywają większość incydentów, pozostałe wykraczają poza ramy tego artykułu.

Największe wpadki Cian

Top najlepszych incydentów w Cian

Zatem dokładnie nauczyliśmy się określać, że incydent się zdarzył. 

Teraz każdy incydent jest szczegółowo opisany i odzwierciedlony w epiku Jira. Swoją drogą, dla tego celu założyliśmy osobny projekt, nazwealiśmy go FAIL - można w nim tworzyć tylko epiki. 

Jeśli zbierzemy wszystkie porażki z ostatnich kilku lat, to dominują: 

  • incydenty związane z mssql;
  • incydenty spowodowane czynnikami zewnętrznymi;
  • błędy administratorów.

Skupimy się bardziej szczegółowo na błędach administratorów oraz na innych interesujących porażkach.

Pięć miejsc - 'Porządkujemy DNS'

To był pochmurny wtorek. Postanowiliśmy uporządkować klaster DNS. 

Zdecydowaliśmy się przenieść wewnętrzne serwery DNS z bind na powerdns, wydzielając na to zupełnie oddzielne serwery, na których nie ma nic poza DNS. 

Umieściliśmy po jednym serwerze DNS w każdej lokalizacji naszych data center, a nadszedł czas przeniesienia stref z bind na powerdns oraz przełączenia infrastruktury na nowe serwery. 

W trakcie przenoszenia z wszystkich serwerów, które były wskazane w lokalnych pamięciach cache bind na wszystkich serwerach, pozostał tylko jeden, który znajdował się w data center w Sankt Petersburgu. To data center początkowo zostało zadeklarowane jako niekrytyczne dla nas, ale nagle stało się single point of failure.
W takim właśnie okresie przeniesienia padł kanał między Moskwą a Sankt Petersburgiem. Faktycznie zostaliśmy bez DNS przez pięć minut i wznowiliśmy działanie, gdy powinien umieć to zrobić) usunięto usterki. 

Wnioski:

Jeśli wcześniej lekceważyliśmy zewnętrzne czynniki podczas przygotowań do prac, to teraz również zostały one uwzględnione w liście rzeczy, do których się przygotowujemy. Dążymy do tego, aby wszystkie komponenty były zarezerwowane w systemie n-2, a podczas prac możemy obniżyć ten poziom do n-1.

  • Podczas tworzenia planu działania zaznaczaj punkty, w których usługa może spaść, i wymyślaj scenariusz, w którym wszystko idzie "najgorzej jak to możliwe" z wyprzedzeniem.
  • Rozdzielaj wewnętrzne serwery DNS po różnych geolokalizacjach/data center/szafach/przełącznikach/łączeniach.
  • Na każdym serwerze zainstaluj lokalny serwer DNS cache, który przekierowuje zapytania do głównych serwerów DNS, a w przypadku jego niedostępności odpowiada z pamięci cache. 

Czwarte miejsce - "Porządkujemy Nginx"

Pewnego pięknego dnia nasz zespół postanowił, że "dość tego znosimy" i rozpoczął proces refaktoryzacji konfiguracji Nginx. Głównym celem było nadanie konfiguracjom intuicyjnej struktury. Wcześniej wszystko było "historycznie ugruntowane" i nie miało żadnej logiki. Teraz każdy server_name został przeniesiony do pliku o tej samej nazwie i wszystkie konfiguracje zostały rozdzielone na foldery. Na marginesie — konfiguracja zawiera 253949 wierszy lub 7836520 znaków i zajmuje prawie 7 megabajtów. Górny poziom struktury: 

Struktura Nginx

├── dostęp
│   ├── allow.list
...
│   └── whitelist.conf
├── geobase
│   ├── exclude.conf
...
│   └── geo_ip_to_region_id.conf
├── geodb
│   ├── GeoIP.dat
│   ├── GeoIP2-Country.mmdb
│   └── GeoLiteCity.dat
├── inc
│   ├── error.inc
...
│   └── proxy.inc
├── lists.d
│   ├── bot.conf
...
│   ├── dynamic
│   └── geo.conf
├── lua
│   ├── cookie.lua
│   ├── log
│   │   └── log.lua
│   ├── logics
│   │   ├── include.lua
│   │   ├── ...
│   │   └── utils.lua
│   └── prom
│       ├── stats.lua
│       └── stats_prometheus.lua
├── map.d
│   ├── access.conf
│   ├── .. 
│   └── zones.conf
├── nginx.conf
├── robots.txt
├── server.d
│   ├── cian.ru
│   │   ├── cian.ru.conf
│   │   ├── ...
│   │   └── my.cian.ru.conf
├── service.d
│   ├── ...
│   └── status.conf
└── upstream.d
    ├── cian-mcs.conf
    ├── ...
    └── wafserver.conf

Zdecydowanie stało się lepiej, ale podczas zmiany nazw i rozdzielania konfiguracji część z nich miała niewłaściwe rozszerzenie i nie trafiła do dyrektywy include *.conf. W rezultacie część hostów stała się niedostępna i zwracała 301 na stronę główną. Ponieważ kod odpowiedzi nie był 5xx/4xx, zauważono to dopiero nad ranem. Po tym zaczęliśmy pisać testy do sprawdzenia komponentów infrastrukturalnych.

Wnioski: 

  • Poprawnie strukturyzuj konfiguracje (nie tylko nginx) i przemyśl ich strukturę na wczesnym etapie projektu. Dzięki temu będą bardziej zrozumiałe dla zespołu, co z kolei zmniejszy czas wprowadzenia na rynek (TTM).
  • Dla niektórych komponentów infrastrukturalnych pisz testy. Na przykład: sprawdzanie, czy wszystkie kluczowe server_name zwracają właściwy status oraz treść odpowiedzi. Wystarczy mieć kilka skryptów, które sprawdzają podstawowe funkcje komponentu, aby nie trzeba było w środku nocy nerwowo przypominać sobie, co jeszcze należy sprawdzić. 

Trzecie miejsce — "Nagle skończyło się miejsce w Cassandra"

Dane stopniowo rosły i wszystko było dobrze, aż do momentu, gdy w klastrze Cassandra zaczęły padać naprawy dużych keyspace'ów, ponieważ nie mogła działać kompakcja. 

Pewnego deszczowego dnia klaster niemal zamienił się w dynię, a dokładnie:

  • pozostało około 20% łącznej przestrzeni w klastrze;
  • nie można w pełni dodać węzłów, ponieważ nie przechodzi czyszczenie po dodaniu węzła z powodu braku miejsca na partycjach;
  • wydajność stopniowo spada, ponieważ kompakcja nie działa; 
  • klaster działa w trybie awaryjnym.

Największe wpadki Cian

Wyjście — dodano jeszcze 5 węzłów bez czyszczenia, po czym zaczęliśmy systematycznie wyprowadzać z klastra i ponownie wprowadzać jako puste węzły, na których skończyło się miejsce. Czas poświęcony jest znacznie większy niż byśmy chcieli. Istniało ryzyko częściowej lub całkowitej niedostępności klastra. 

Wnioski:

  • Na wszystkich serwerach Cassandra nie powinno być zajęte więcej niż 60% miejsca na każdej partycji. 
  • Powinny być załadowane nie więcej niż w 50% pod względem CPU.
  • Nie należy lekceważyć planowania pojemności i trzeba je przemyśleć dla każdego komponentu, biorąc pod uwagę jego specyfikę.
  • Im więcej węzłów w klastrze — tym lepiej. Serwery zawierające małą ilość danych szybciej się odnawiają, a taki klaster łatwiej przywrócić do działania. 

Drugie miejsce — „Zniknęły dane z magazynu kluczy wartości Consul”

Do usług odkrywania serwisów używamy, jak wielu, Consul. Ale nasz magazyn kluczy wartości jest wykorzystywany także do wdrożeń blue-green monolitu. Przechowuje informacje o aktywnych i nieaktywnych upstreamach, które zmieniają się miejscami podczas wdrożenia. W tym celu napisano usługę wdrożenia, która współdziałała z magazynem KV. W pewnym momencie dane z KV zniknęły. Odzyskaliśmy według pamięci, ale z licznymi błędami. W konsekwencji — podczas wdrożenia obciążenie na upstreamach rozłożyło się nierównomiernie, a otrzymaliśmy wiele błędów 502 z powodu przeciążenia backendów pod względem CPU. Ostatecznie przenieśliśmy się z magazynu KV Consul do Postgresa, skąd ich usunięcie nie jest już tak proste.  

Wnioski:

  • Usługi bez jakiejkolwiek autoryzacji nie powinny zawierać krytycznych danych dla działania strony. Na przykład, jeśli nie ma autoryzacji w ES — lepiej byłoby zablokować dostęp na poziomie sieci z wszędzie, gdzie nie jest potrzebny, pozostawić tylko niezbędne, a także ustawić action.destructive_requires_name: true.
  • Zastosuj mechanizm tworzenia kopii zapasowych i przywracania zawczasu. Na przykład, przygotuj skrypt (na przykład w Pythonie), który potrafi zarówno tworzyć kopie zapasowe, jak i przywracać.

Pierwsze miejsce — „Kapitan Nieoczywistość” 

W pewnym momencie zauważyliśmy nierównomierne rozłożenie obciążenia na upstreamy Nginx, gdy w backendzie było 10+ serwerów. Ponieważ round-robin kierował zapytania od pierwszego do ostatniego upstreamu w kolejności, a każdy reload Nginx zaczynał od nowa, na pierwsze upstreamy zawsze przypadało więcej zapytań niż na pozostałe. W rezultacie pracowały one wolniej i ucierpiała cała strona. Stawało się to coraz bardziej zauważalne wraz ze wzrostem ruchu. Proste zaktualizowanie Nginx, aby włączyć random, nie wystarczyło — trzeba było przerobić masę kodu Lua, który nie działał w wersji 1.15 (w tym czasie). Musieliśmy poprawić naszego Nginx 1.14.2, wprowadzając wsparcie dla random. To rozwiązało problem. Ten błąd zdobywa nagrodę w kategorii „kapitan nieoczywistość”.

Wnioski:

To było bardzo interesujące i angażujące badanie tego błędu). 

  • Ustawcie monitoring tak, aby pomagał szybko znajdować podobne fluktuacje. Na przykład można używać ELK, aby obserwować rps na każdym backendzie każdego upstreamu, śledzić ich czasy odpowiedzi z perspektywy Nginx. W tym przypadku to właśnie pomogło nam zidentyfikować problem. 

W rezultacie większą część awarii można byłoby uniknąć przy bardziej skrupulatnym podejściu do tego, co robisz. Zawsze trzeba pamiętać o prawie Murphy'ego: Cokolwiek może pójść źle, pójdzie źle, i budować komponenty, kierując się tym. 

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster