
Прим. прев.: този цикъл беше посветен на запознаването с възможностите на Istio и демонстрирането им в действие. Сега ще разгледаме по-сложни аспекти на конфигурацията и използването на тази service mesh, а именно - прецизно конфигуриране на маршрутизацията и управление на мрежовия трафик.
Напомняме също, че в статията се използват конфигурации (манифести за Kubernetes и Istio) от репозитория. .
Управление на трафика
С Istio в кластера се появяват нови възможности, които позволяват да се осигури:
- Динамично маршрутизиране на заявки: канаречни внедрения, A/B тестване;
- Балансировка на натоварването: проста и безпроблемна, основана на хешове;
- Възстановяване след сривове: таймаути, повторни опити, circuit breakers;
- Внедряване на неизправности: забавяния, прекъсвания на заявки и т.н.
В продължение на статията тези възможности ще бъдат показани на примера на избрано приложение и ще бъдат представени нови концепции. Първата такава концепция ще бъде DestinationRules (т.е. правила за получателя на трафик/заявки — бел. ред.), с помощта на които активираме A/B тестването.
A/B тестване: DestinationRules на практика
A/B тестването се прилага в случаи, когато съществуват две версии на приложението (обикновено те се различават визуално) и не сме напълно сигурни коя от тях ще подобри взаимодействието с потребителя. Затова едновременно стартираме и двете версии и събираме метрики.
За деплой на втората версия на фронтенда, необходима за демонстрация на A/B тестването, изпълнете следната команда:
$ kubectl apply -f resource-manifests/kube/ab-testing/sa-frontend-green-deployment.yaml
deployment.extensions/sa-frontend-green createdМанифестът на deployment’а за «зелената версия» се различава на две места:
- Образът е базиран на друга таг —
istio-green, - Pod'овете имат етикет
version: green.
Тъй като и двата deployment-а имат етикет app: sa-frontend, заявките, маршрутизирани от виртуалната услуга sa-external-services към услугата sa-frontend, ще бъдат пренасочени към всичките й инстанции и натоварването ще се разпредели посредством , което ще доведе до следната ситуация:

Заявяваните файлове не са намерени
Тези файлове не са намерени, защото по различен начин се наричат в различните версии на приложението. Нека се уверим в това:
$ curl --silent http://$EXTERNAL_IP/ | tr '"' 'n' | grep main
/static/css/main.c7071b22.css
/static/js/main.059f8e9c.js
$ curl --silent http://$EXTERNAL_IP/ | tr '"' 'n' | grep main
/static/css/main.f87cd8c9.css
/static/js/main.f7659dbb.js Това означава, че index.html, запрашиващ един вариант статични файлове, може да бъде изпратен от балансировача на натоварването към подове, които имат друга версия, където по разбираеми причини такива файлове не съществуват. Затова, за да заработи приложението, трябва да поставим ограничение: "същата версия на приложението, която предостави index.html, трябва да обслужи и последващите заявки».
Ще постигнем целта чрез последователно балансиране на натоварването на базата на хешове (Consistent Hash Loadbalancing). В този случай заявките от един клиент се изпращат към един и същи екземпляр на бекенда, за което се използва предопределено свойство — например, HTTP заглавие. Реализира се с помощта на DestinationRules.
DestinationRules
След като VirtualService упъти заявката към необходимата услуга, с помощта на DestinationRules можем да определим политиките, които ще се прилагат за трафика, предназначен за екземплярите на тази услуга:

Управление на трафика с ресурси Istio
Забележка: Влиянието на ресурсите Istio върху мрежовия трафик е представено тук по опростен начин за разбиране. За да бъдем точни, решението за това на кой екземпляр да се изпрати заявката се взема от Envoy в Ingress Gateway, настроен в CRD.
С помощта на Destination Rules можем да настроим балансиране на натоварването така, че да се използват последователни хешове и да се гарантира, че отговорите от един и същи екземпляр на услугата отиват при един и същи потребител. Следната конфигурация позволява да постигнем това ():
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: sa-frontend
spec:
host: sa-frontend
trafficPolicy:
loadBalancer:
consistentHash:
httpHeaderName: version # 1 1 — хешът ще бъде генериран на базата на съдържанието на HTTP заглавието version.
Приложете конфигурацията с помощта на следната команда:
$ kubectl apply -f resource-manifests/istio/ab-testing/destinationrule-sa-frontend.yaml
destinationrule.networking.istio.io/sa-frontend created Сега изпълнете командата по-долу и се уверете, че получавате необходимите файлове, когато задавате заглавие version:
$ curl --silent -H "version: yogo" http://$EXTERNAL_IP/ | tr '"' 'n' | grep mainЗабележка: За да добавите различни стойности в заглавието и да тествате резултатите директно в браузъра, можете да използвате за Chrome (или за Firefox — бел. пр.).
Въобще, DestinationRules имат повече възможности в областта на балансирането на натоварването — подробности в уточнявайте в .
Преди да разгледаме VirtualService по-подробно, ще премахнем "зеления вариант" на приложението и съответното правило за пренасочване на трафика, изпълнявайки следните команди:
$ kubectl delete -f resource-manifests/kube/ab-testing/sa-frontend-green-deployment.yaml
deployment.extensions "sa-frontend-green" deleted
$ kubectl delete -f resource-manifests/istio/ab-testing/destinationrule-sa-frontend.yaml
destinationrule.networking.istio.io "sa-frontend" deletedЗеркалирование: Virtual Services на практика
Shadowing ("екраниране") или Mirroring ("зеркалрение") се прилага в случаите, когато искаме да тестваме промяна в продукция, без да засягаем крайни потребители: за целта дублираме ("зеркалим") заявките на втори екземпляр, където са направени нужните промени, и наблюдаваме последствията. По-просто казано, това е когато вашият(а) колега избира най-критичния проблем и прави pull request под формата на такъв огромен купчина замърсения, че никой не може наистина да му направи преглед.
За да проверим този сценарий в действие, ще създадем втори екземпляр SA-Logic с грешки (buggy), изпълнявайки следната команда:
$ kubectl apply -f resource-manifests/kube/shadowing/sa-logic-service-buggy.yaml
deployment.extensions/sa-logic-buggy created И сега ще изпълним командата, за да се уверим, че всички екземпляри с app=sa-logic също имат и етикети с подходящите версии:
$ kubectl get pods -l app=sa-logic --show-labels
NAME READY LABELS
sa-logic-568498cb4d-2sjwj 2/2 app=sa-logic,version=v1
sa-logic-568498cb4d-p4f8c 2/2 app=sa-logic,version=v1
sa-logic-buggy-76dff55847-2fl66 2/2 app=sa-logic,version=v2
sa-logic-buggy-76dff55847-kx8zz 2/2 app=sa-logic,version=v2 Сервиз sa-logic цели pod’ове с етикет app=sa-logic, затова всички заявки ще бъдат разпределени между всички екземпляри:

… но искаме, заявките да бъдат насочвани към екземплярите с версия v1 и да бъдат зеркалирани на екземплярите с версия v2:

Ще постигнем това чрез VirtualService в комбинация с DestinationRule, където правилата ще определят подмножества и маршрути на VirtualService към конкретно подмножество.
Определение на подмножествата в Destination Rules
Подмножества (subsets) се определят чрез следната конфигурация ():
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: sa-logic
spec:
host: sa-logic # 1
subsets:
- name: v1 # 2
labels:
version: v1 # 3
- name: v2
labels:
version: v2- Хост (
host) определя, че това правило се прилага само в случаите, когато маршрутът е насочен към услугатаsa-logic; - Имената (
name) на подмножествата се използват при маршрутизация към екземплярите на подмножествата; - Етикет (
label) определя истините двойки ключ-стойност, на които екземплярите трябва да отговарят, за да станат част от подмножество.
Приложете конфигурацията с помощта на следната команда:
$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets-destinationrule.yaml
destinationrule.networking.istio.io/sa-logic създаденСега, когато подмножествата са определени, можем да продължим и да настроим VirtualService, за да приложим правилата за заявките към sa-logic, така че те:
- да се маршрутизират към подмножеството
v1, - да се огледат към подмножеството
v2.
Следващият манифест позволява да се постигне замисленото ():
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: sa-logic
spec:
hosts:
- sa-logic
http:
- route:
- destination:
host: sa-logic
subset: v1
mirror:
host: sa-logic
subset: v2Обяснения тук не са необходими, така че просто да разгледаме в действие:
$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets-shadowing-vs.yaml
virtualservice.networking.istio.io/sa-logic създаденНека добавим натоварване, като извикаме следната команда:
$ while true; do curl -v http://$EXTERNAL_IP/sentiment
-H "Content-type: application/json"
-d '{"sentence": "Обичам йогобела"}';
sleep .8; done Нека погледнем резултатите в Grafana, където можем да видим, че версията с бъгове (buggy) причинява неуспех на ~60 % от заявките, но нито един от тези неуспехи не засяга крайните потребители, тъй като им отговаря работещият сервис.

Успехът на отговорите от различните версии на сервиза sa-logic
Тук за първи път видяхме как VirtualService се прилага по отношение на Envoy на нашите сервизи: когато sa-web-app прави заявка към sa-logic, тя преминава през sidecar Envoy, който — чрез VirtualService — е настроен да маршрутизира заявката към подмножеството v1 и да огледавя заявката към подмножеството v2 на сервиса sa-logic.
Знам: вече сте помислили, че Virtual Services са прости. В следващия раздел ще разширим това мнение, като кажем, че те са наистина великолепни.
Канарски разширения
Canary Deployment — процес на разпространение на нова версия на приложението за малък брой потребители. Използва се, за да се уверим, че няма проблеми с релиза и едва след това, вече убедени в достатъчното му (на релиза) качество, да го разпространим на по-голяма аудитория.по-За демонстрация на канаречни версии ще продължим работата с подмножеството
Няма да пестим и веднага ще изпратим 20 % от потребителите на версията с бъгове (тя ще представлява нашия канаречен разпространение), а останалите 80 % — на нормалния сервис. За това ще приложим следния VirtualService ( buggy у sa-logic.
sa-logic-subsets-canary-vs.yaml):
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: sa-logic
spec:
hosts:
- sa-logic
http:
- route:
- destination:
host: sa-logic
subset: v1
weight: 80 # 1
- destination:
host: sa-logic
subset: v2
weight: 20 # 1 1 — това е тегло (weight), определящо процента на заявките, които ще бъдат насочени към приемника или подмножеството на приемника.
Нека обновим предишната конфигурация на VirtualService с sa-logic следната команда:
$ kubectl apply -f resource-manifests/istio/canary/sa-logic-subsets-canary-vs.yaml
virtualservice.networking.istio.io/sa-logic configured… и веднага ще видим, че част от заявките водят до грешки:
$ while true; do
curl -i http://$EXTERNAL_IP/sentiment
-H "Content-type: application/json"
-d '{"sentence": "I love yogobella"}'
--silent -w "Time: %{time_total}s t Status: %{http_code}n"
-o /dev/null; sleep .1; done
Time: 0.153075s Status: 200
Time: 0.137581s Status: 200
Time: 0.139345s Status: 200
Time: 30.291806s Status: 500VirtualServices активират канарени разпределения: в този случай ограничихме потенциалните последствия от проблемите до 20 % от потребителската база. Прекрасно! Сега, всеки път, когато не сме сигурни в кода си (иначе казано — винаги…), можем да използваме зеркалове и канарени разпределения.
Таймаути и повторения
Но не винаги грешките са в кода. На първо място в списъка "" е погрешното мнение, че "мрежата е надеждна". Всъщност мрежата не е надеждна и затова имаме нужда от таймаути (timeouts) и повторения (retries).
За демонстрация ще продължим да използваме същия проблем версия sa-logic (buggy), а ненадеждността на мрежата ще симулираме с произволни грешки.
Нека нашият сървис с грешки има 1/3 шанс за прекалено дълъг отговор, 1/3 шанс за грешка Internal Server Error и 1/3 шанс за успешно връщане на страницата.
За да смекчим последиците от подобни проблеми и да подобрим живота на потребителите, можем да:
- добавим таймаут, ако сървисът отговаря по-дълго от 8 секунди,
- правим повторна опитност, ако в заявката се получи грешка.
За реализация ще използваме следното определение на ресурса ():
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: sa-logic
spec:
hosts:
- sa-logic
http:
- route:
- destination:
host: sa-logic
subset: v1
weight: 50
- destination:
host: sa-logic
subset: v2
weight: 50
timeout: 8s # 1
retries:
attempts: 3 # 2
perTryTimeout: 3s # 3- Таймаутът за заявките е зададен на 8 секунди;
- Повторните опити за заявките се извършват 3 пъти;
- И всяка опит се счита за неуспешен, ако времето за отговор надвишава 3 секунди.
Така постигнахме оптимизация, тъй като потребителят не трябва да чака повече от 8 секунди и ще направим три нови опита за получаване на отговор в случай на неуспехи, увеличавайки шанса за успешен отговор.
Приложете актуализираната конфигурация с командата:
$ kubectl apply -f resource-manifests/istio/retries/sa-logic-retries-timeouts-vs.yaml
virtualservice.networking.istio.io/sa-logic конфигуриранИ проверете в графиките на Grafana, че броят на успешните отговори е надхвърлил:

Подобрения в статистиката на успешните отговори след добавяне на тайм аутове и повторни опити
Преди да преминете към следващия раздел (или по-точно — вече към следващата част от статията, тъй като в тази няма да има повече практически експерименти — бел. пр.), премахнете sa-logic-buggy и VirtualService, изпълнявайки следните команди:
$ kubectl delete deployment sa-logic-buggy
deployment.extensions “sa-logic-buggy” изтрит
$ kubectl delete virtualservice sa-logic
virtualservice.networking.istio.io “sa-logic” изтритПатерни Circuit Breaker и Bulkhead
Става въпрос за два важни патерна в микросервисната архитектура, които позволяват самостоятелно възстановяване (self-healing) на услугите.
Circuit Breaker („автоматичен прекъсвач“) се използва за прекратяване на заявките, постъпващи към инстанцията на услуга, която се счита за нездрава, и възстановяването й, докато клиентските заявки се пренасочват към здравите инстанции на тази услуга (което увеличава процента на успешните отговори). (Бел. пр.: По-подробно описание на патерна може да се намери, например, .)
Bulkhead („перегородка“) изолира провалите в услугите от засягането на цялата система. Например, услуга B е повредена, а друга услуга (клиент на услуга B) прави заявка към услуга B, в резултат на което изразходва своя пул от нишки и не може да обслужва други заявки (дори ако те не са свързани с услуга B). (Бел. пр.: По-подробно описание на патерна може да се намери, например, .)
Ще пропусна детайлите по реализирането на тези патерни, защото лесно могат да бъдат намерени в , а и много ми се иска да покажа автентикация и авторизация, за което ще става дума в следващата част от статията.
P.S. от преводача
Прочетете също в нашия блог:
- «Назад към микросервисите с Istio»: , ;
- «»;
- «».
Източник: habr.com
