Istio Circuit Breaker: деактивиране на повредени контейнери

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

Istio Circuit Breaker: деактивиране на повредени контейнери

Днешната тема е Circuit Breaker, което на български означава „автоматичен прекъсвач“ — в жаргонен смисъл „защитен автомат“. Само че в Istio, този автомат не изключва късо съединение или претоварена верига, а неизправни контейнери.

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

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

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

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

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

Какво е Pool Ejection в Istio

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

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

За да покажем как работи това в Kubernetes на платформата OpenShift, нека започнем с екранен снимка на нормално работещи микросервизи от примера в репозитория. Демо на Red Hat Developer. Тук имаме два 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