Istio Circuit Breaker: изключване на неизправни контейнери

Празниците свършиха и ние се завръщаме с нашата втора публикация от серията за Istio Service Mesh.

Istio Circuit Breaker: изключване на неизправни контейнери

Темата днес е Circuit Breaker, което в превод от руски електротехнически означава „автоматичен прекъсвач“, а на разговорен език – „защитен автомат“. Само че в Istio този автомат не изключва късо съединение или пренатоварена верига, а неизправни контейнери.

Как това трябва да работи в идеалния случай

Когато микросервисите се управляват от Kubernetes, например, в платформата OpenShift, те автоматично се мащабират нагоре и надолу в зависимост от натоварването. Тъй като микросервисите работят в pod-ове, на една крайна точка могат да има няколко инстанции на контейнеризиран микросервис, а Kubernetes маршрутизира заявките и балансира натоварването между тях. И – в идеалния случай – всичко това трябва да работи отлично.

Помним, че микросервисите са малки и ефимерни. Ефимерността, която тук означава лесно възникване и изчезване, често се подценява. Раждането и смъртта на всяка инстанция на микросервиса в pod-а са напълно очаквани неща, OpenShift и Kubernetes се справят отлично с това и всичко работи чудесно – но отново, в теорията.

Как това всъщност работи

А сега си представете, че конкретна инстанция на микросервиса, тоест контейнер, е станала неизправна: или не отговаря (грешка 503), или – което е по-неприятно – реагира, но твърде бавно. С други думи, той заеква или не отговаря на заявките, но все пак не се изключва автоматично от пулата. Какво трябва да се направи в този случай? Да повторим опита? Да го изключим от схемата за маршрутизиране? И какво означава „твърде бавно“ – какво е количеството и кой го определя? Може би просто да му дадем малко време и да опитаме по-късно? Ако да, колко по-късно?

Какво е Pool Ejection в Istio

И тук на помощ идва Istio с автоматите за защита Circuit Breaker, които временно премахват неизправните контейнери от пулата за маршрутизиране и балансиране на натоварването, осъществявайки процедурата Pool Ejection.

Използвайки стратегията за откритие на отклонения (outlier detection), Istio открива проблемни pod-ове, които се отклоняват от нормата, и ги премахва от пулата на ресурсите за зададен период, наречен „сънно време“ (sleep window).

За да демонстрираме как работи това в Kubernetes на платформата OpenShift, ще започнем с екранна снимка на нормално функциониращи микросервизи от примера в репозитория. Демонстрации на разработчици от Red Hat. Имаме два pod-а, v1 и v2, всеки с по един контейнер. Когато правилата за маршрутизация на Istio не се използват, Kubernetes по подразбиране прилага равномерно балансирана циклична маршрутизация:

Istio Circuit Breaker: изключване на неизправни контейнери

Подготвяме се за срив

Преди да извършим Pool Ejection, трябва да създадем правило за маршрутизация на Istio. Да кажем, че искаме да разпределяме запитванията между pod-овете в пропорция 50/50. Освен това ще увеличим броя на контейнерите v2 от един на два, ето как:

oc scale deployment recommendation-v2 --replicas=2 -n tutorial

Сега задаваме правило за маршрутизация, така че трафикът да се разпределя между pod-овете в пропорция 50/50.

Istio Circuit Breaker: изключване на неизправни контейнери
Ето как изглежда резултатът от това правило:

Istio Circuit Breaker: изключване на неизправни контейнери
Може да се каже, че на този екран не е 50/50, а 14:9, но с времето ситуацията ще се стабилизира.

Създаваме срив

А сега ще извлечем един от двата контейнера v2, така че да имаме един работещ контейнер v1, един работещ контейнер v2 и един неизправен контейнер v2:

Istio Circuit Breaker: изключване на неизправни контейнери

Поправяме срива

И така, имаме неизправен контейнер и е време за Pool Ejection. С много прост конфигурационен файл ще изключим този неизправен контейнер от всякакви схеми за маршрутизиране за 15 секунди с надеждата, че той сам ще се върне в работно състояние (или ще се рестартира, или ще възстанови производителността). Ето как изглежда този конфиг и резултатите от работата му:

Istio Circuit Breaker: изключване на неизправни контейнери
Istio Circuit Breaker: изключване на неизправни контейнери
Както се вижда, неизправният контейнер v2 вече не се използва при маршрутизацията на запитвания, тъй като е премахнат от пула. Но след 15 секунди той автоматично ще се върне в пула. Всъщност, току що демонстрирахме как работи Pool Ejection.

Започваме да изграждаме архитектурата

Pool Ejection в комбинация с възможностите за мониторинг на Istio позволява да започнем да изграждаме рамка за автоматична замяна на неизправни контейнери, за да намалим или дори напълно да елиминираме престои и сривове.
 
NASA има едно известно мото - Failure Is Not an Option, авторът на което е ръководителят на полети Джин Кранц. На български може да се преведе като „Поражението не е опция“, а смисълът тук е, че всичко може да работи, ако имаме достатъчно воля за това. Въпреки това, в реалния живот отказите не просто се случват, те са неизбежни, навсякъде и във всичко. Как да се справяме с тях в контекста на микросервизите? Според нас, по-добре е да се разчита не на волята, а на възможностите на контейнерите. Kubernetes, Red Hat OpenShift, и Istio.

Istio, както вече писахме по-горе, реализира прекрасно утвърдената концепция за автоматични прекъсвачи от физическия свят. И както електрическият прекъсвач отключва проблемната част на веригата, така и софтуерният Circuit Breaker в Istio прекъсва връзката между потока от запитвания и проблемния контейнер, когато нещо не е наред с крайна точка, например, когато сървърът се срине или започне да забавя.

При това, във втория случай проблемите са още повече, тъй като забавянето на един контейнер не само причинява каскадни закъснения в свързаните с него услуги и, в резултат, намалява производителността на системата като цяло, но и предизвиква повторни запитвания към вече бавно работещата услуга, което само влошава ситуацията.

Circuit Breaker в теорията

Circuit Breaker е прокси, който контролира потока от запитвания към крайна точка. Когато тази точка спре да работи или – в зависимост от зададените настройки – започне да забавя, проксито прекъсва връзката с контейнера. Трафикът след това се пренасочва към други контейнери, просто заради натоварването. Връзката остава разкъсана (open) в продължение на зададено времево окно, да кажем две минути, след което се счита за полуразкъсана (half-open). Опитът да се изпрати следващото запитване определя бъдещото състояние на връзката. Ако с услугата всичко е наред, връзката се връща в работно състояние и отново става затворена (closed). Но ако с услугата все още нещо не е наред, връзката отново се разкъсва и времевото окно се включва отново. Ето как изглежда опростената диаграма на промените в състоянията на Circuit Breaker:

Istio Circuit Breaker: изключване на неизправни контейнери
Тук е важно да се отбележи, че всичко това се случва на ниво, така да се каже, системна архитектура. Следователно, в някой момент ще трябва да научите приложенията си да работят с Circuit Breaker, например, да предоставят в отговор стойност по подразбиране или, ако е възможно, да игнорират съществуването на услугата. За целта се използва bulkhead pattern, но той излиза извън обхвата на тази статия.

Circuit Breaker в практиката

Например, ще стартираме две версии на нашия микросервиз за препоръки на OpenShift. Версия 1 ще работи нормално, а в v2 ще вградим забавяне, за да имитираме забавяне на сървъра. Инструментът за преглеждане на резултатите е siege:

siege -r 2 -c 20 -v customer-tutorial.$(minishift ip).nip.io

Istio Circuit Breaker: изключване на неизправни контейнери
Всичко изглежда да работи, но на каква цена? На пръв поглед имаме 100 % наличност, но ако погледнете внимателно – максималната продължителност на транзакцията е цели 12 секунди. Това определено е тясно място, и трябва да се разшири.

За целта с помощта на Istio ще изключим обращенията към бавни контейнери. Ето как изглежда съответният конфиг с използване на Circuit Breaker:

Istio Circuit Breaker: изключване на неизправни контейнери
Последният ред с параметъра httpMaxRequestsPerConnection сигнализира, че връзката трябва да бъде разкъсана при опит за създаване на още едно – второ – свързване в допълнение към вече съществуващото. Тъй като нашият контейнер имитира забавяща услуга, такива ситуации периодично ще възникват и тогава Istio ще върне грешка 503, а ето какво ще покаже siege:

Istio Circuit Breaker: изключване на неизправни контейнери

ОК, имаме Circuit Breaker, какво следва?

И така, реализирахме автоматично прекъсване, без да докосваме изходния код на самите услуги. Използвайки Circuit Breaker и описаната по-горе процедура Pool Ejection, можем да премахваме забавящи контейнери от пула на ресурсите, докато не се нормализират, и да проверяваме състоянието им с определена периодичност – в нашия пример, това е две минути (параметър sleepWindow).

Обърнете внимание, че способността на приложението да реагира на грешка 503 все още се определя на ниво изходен код. Съществуват множество стратегии за работа с Circuit Breaker, които се прилагат в зависимост от ситуацията.

В следващия пост: ще разкажем за трасировката и мониторинга, които вече са вградени или лесно добавяеми в Istio, а също така как нарочно да внасяме грешки в системата.

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster