Święta dobiegły końca, a my wracamy z naszym drugim postem z serii o Istio Service Mesh.

Dzisiejszym tematem jest Circuit Breaker, który w tłumaczeniu na polski oznacza „wyłącznik automatyczny”, potocznie – „zabezpieczenie”. Tylko w Istio ten automat wyłącza nie przepaloną czy przeciążoną sieć, a uszkodzone kontenery.
Jak to powinno działać w idealnych warunkach
Kiedy mikroserwisy są zarządzane przez Kubernetes, na przykład w ramach platformy OpenShift, automatycznie skalują się w górę i w dół w zależności od obciążenia. Ponieważ mikroserwisy działają w podach, na jednym końcowym punkcie może być kilka egzemplarzy kontenerowego mikroserwisu, a Kubernetes będzie routować zapytania i równoważyć obciążenie między nimi. I – w idealnym świecie – wszystko to powinno działać perfekcyjnie.
Pamiętamy, że mikroserwisy są małe i efemeryczne. Efemeryczność, która w tym przypadku oznacza prostotę powstawania i znikania, często jest niedoceniana. Narodziny i śmierć kolejnego egzemplarza mikroserwisu w podzie są rzeczami całkowicie oczekiwanymi; OpenShift i Kubernetes radzą sobie z tym dobrze, a wszystko to działa wspaniale – ale znowu, tylko w teorii.
Jak to działa w rzeczywistości
A teraz wyobraźcie sobie, że jakiś konkretny egzemplarz mikroserwisu, a mianowicie kontener, stał się niesprawny: albo nie odpowiada (błąd 503), albo – co gorsza – reaguje, ale zbyt wolno. Innymi słowy, działa z opóźnieniem lub nie odpowiada na zapytania, ale nie jest automatycznie usuwany z puli. Co należy w takim przypadku zrobić? Powtórzyć próbę? Usunąć go z schematu routingu? A co oznacza „zbyt wolno” – ile to w liczbach i kto to określa? Może po prostu dać mu chwilę i spróbować później? Jeśli tak, to jak długo później?
Czym jest Pool Ejection w Istio
I tutaj z pomocą przychodzi Istio ze swoimi automatami zabezpieczeń Circuit Breaker, które tymczasowo usuwają uszkodzone kontenery z puli zasobów routingu i równoważenia obciążenia, realizując procedurę Pool Ejection.
Wykorzystując strategię wykrywania odstępstw (outlier detection), Istio wykrywa krzywe pody, które wybiegają poza normę, i na określony czas, nazywany „oknem snu” (sleep window), usuwają je z puli zasobów.
Aby pokazać, jak to działa w Kubernetes na platformie OpenShift, zaczniemy od zrzutu ekranu działających mikroserwisów z przykładu w repozytorium . Mamy tutaj dwa pody, v1 i v2, z których każdy obsługuje jeden kontener. Kiedy zasady routingu Istio nie są stosowane, Kubernetes domyślnie stosuje równomiernie zbalansowane cykliczne rutowanie:

Przygotowujemy się do awarii
Zanim wykonamy Ejection Pool, musimy stworzyć zasadę routingu Istio. Załóżmy, że chcemy rozdzielać żądania pomiędzy podami w stosunku 50/50. Dodatkowo zwiększymy liczbę kontenerów v2 z jednego do dwóch, w ten sposób:
oc scale deployment recommendation-v2 --replicas=2 -n tutorial
Teraz ustalamy zasadę routingu, aby ruch był rozdzielany pomiędzy podami w stosunku 50/50.

Oto jak wygląda wynik tej zasady:

Można się przyczepić, że na tym zrzucie ekranu nie jest 50/50, a 14:9, ale z czasem sytuacja się unormuje.
Sprawiamy awarię
A teraz wyprowadzimy z użytku jeden z dwóch kontenerów v2, abyśmy mieli jeden sprawny kontener v1, jeden sprawny kontener v2 i jeden uszkodzony kontener v2:

Naprawiamy awarię
I tak, mamy uszkodzony kontener, nadszedł czas na Ejection Pool. Dzięki bardzo prostemu configowi wykluczymy ten uszkodzony kontener z jakichkolwiek schematów routingu na 15 sekund, zakładając, że sam wróci do sprawności (lub się zrestartuje, lub przywróci wydajność). Oto jak wygląda ten config i wyniki jego działania:


Jak widać, uszkodzony kontener v2 nie jest już używany przy routingu żądań, ponieważ został usunięty z puli. Po 15 sekundach automatycznie wróci do puli. Właściwie właśnie pokazaliśmy, jak działa Ejection Pool.
Zaczynamy budować architekturę
Ejection Pool w połączeniu z możliwościami monitorowania Istio pozwala zacząć budować framework automatycznej wymiany uszkodzonych kontenerów, aby zredukować, a nawet całkowicie wyeliminować przestoje i awarie.
NASA ma jedno głośne hasło – Awaria to nie opcja, którego autorem jest kierownik lotów W języku polskim można to przetłumaczyć jako „Porazka nie jest opcją”, a sens polega na tym, że można sprawić, aby wszystko działało, mając na to wystarczającą wolę. Jednak w rzeczywistości awarie nie tylko się zdarzają, lecz są nieuniknione, wszędzie i we wszystkim. Jak więc sobie z nimi radzić w przypadku mikroserwisów? Naszym zdaniem lepiej polegać nie na sile woli, lecz na możliwościach kontenerów. , , i .
Jak już wcześniej pisaliśmy, Istio wprowadza dobrze znaną z rzeczywistego świata koncepcję automatycznych wyłączników. Tak jak wyłącznik elektryczny odcina problemowy fragment obwodu, tak programowy Circuit Breaker w Istio przerywa połączenie między strumieniem żądań a problemowym kontenerem, gdy coś jest nie tak z punktem końcowym, na przykład gdy serwer upadł lub zaczął zwalniać.
W przypadku drugiego scenariusza problemów jest jeszcze więcej, ponieważ zwolnienia jednego kontenera nie tylko powodują kaskadowe opóźnienia w usługach do niego się odwołujących, co w konsekwencji zmniejsza wydajność całego systemu, ale także prowadzą do ponownych żądań do i tak już wolno działającej usługi, co tylko pogarsza sytuację.
Circuit Breaker w teorii
Circuit Breaker to proxy, które kontroluje strumień żądań do punktu końcowego. Gdy ten punkt przestaje działać lub – w zależności od ustawień – zaczyna zwalniać, proxy przerywa połączenie z kontenerem. Ruch po tym zostaje przekierowany do innych kontenerów, po prostu z powodu równoważenia obciążenia. Połączenie pozostaje otwarte (open) przez określony czas snu, powiedzmy dwie minuty, a następnie staje się półotwarte (half-open). Próba wysłania następnego żądania określa dalszy stan połączenia. Jeśli z usługą wszystko w porządku, połączenie wraca do stanu roboczego i znów staje się zamknięte (closed). Jeśli natomiast coś jest nie tak z usługą, połączenie zostaje przerwane i okno snu jest wznawiane. Oto uproszczony diagram zmiany stanów Circuit Breaker:

Ważne jest, aby zauważyć, że wszystko to dzieje się na poziomie, powiedzmy, architektury systemowej. Dlatego w pewnym momencie będziesz musiał nauczyć swoje aplikacje współpracować z Circuit Breaker, na przykład zwracając wartość domyślną lub, jeśli to możliwe, ignorując istnienie usługi. W tym celu stosuje się wzorzec bulkhead, ale wykracza on poza zakres tego artykułu.
Circuit Breaker w praktyce
Na przykład uruchomimy na OpenShift dwie wersje naszego mikroserwisu rekomendacji. Wersja 1 będzie działać normalnie, natomiast w v2 wbudujemy opóźnienie, aby zasymulować spowolnienie serwera. Do przeglądania wyników użyjemy narzędzia :
siege -r 2 -c 20 -v customer-tutorial.$(minishift ip).nip.io

Wszystko wydaje się działać, ale jaką ceną? Na pierwszy rzut oka mamy 100% dostępności, ale przyjrzyj się dokładniej – maksymalny czas trwania transakcji wynosi 12 sekund. To zdecydowanie wąskie gardło, które trzeba rozwiązać.
W tym celu, za pomocą Istio, wykluczymy połączenia z wolnymi kontenerami. Oto jak wygląda odpowiednia konfiguracja przy użyciu Circuit Breaker:

Ostatni wiersz z parametrem httpMaxRequestsPerConnection sygnalizuje, że połączenie powinno być przerywane w momencie próby utworzenia kolejnego – drugiego – połączenia oprócz już istniejącego. Ponieważ nasz kontener imituje spowalniający serwis, takie sytuacje mogą się okresowo zdarzać, i wtedy Istio zwróci błąd 503, a oto co pokaże siege:

OK, mamy Circuit Breaker, co dalej?
Zrealizowaliśmy automatyczne odłączenie, nie dotykając w ogóle kodu źródłowego samych usług. Używając Circuit Breaker i opisaną wyżej procedurę Pool Ejection, możemy wyłączać z puli zasobów wolne kontenery, aż powrócą do normy, i sprawdzać ich stan z określoną częstotliwością – w naszym przykładzie są to dwie minuty (parametr sleepWindow).
Zauważ, że zdolność aplikacji do reagowania na błąd 503 jest wciąż definiowana na poziomie jej kodu źródłowego. Istnieje wiele strategii działania z Circuit Breaker, które są stosowane w zależności od sytuacji.
W następnej publikacji: opowiemy o śledzeniu i monitorowaniu, które są już wbudowane lub łatwo dodawane do Istio, oraz o tym, jak celowo wprowadzać błędy do systemu.
Źródło: habr.com
