Обратно към микросервизите с Istio. Част 2

Обратно към микросервизите с Istio. Част 2

Прим. прев.: Първа част този цикъл беше посветен на запознаването с възможностите на Istio и демонстрирането им в действие. Сега ще разгледаме по-сложни аспекти на конфигурацията и използването на тази service mesh, а именно - прецизно конфигуриране на маршрутизацията и управление на мрежовия трафик.

Напомняме също, че в статията се използват конфигурации (манифести за Kubernetes и Istio) от репозитория. istio-mastery.

Управление на трафика

С 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’а за «зелената версия» се различава на две места:

  1. Образът е базиран на друга таг — istio-green,
  2. Pod'овете имат етикет version: green.

Тъй като и двата deployment-а имат етикет app: sa-frontend, заявките, маршрутизирани от виртуалната услуга sa-external-services към услугата sa-frontend, ще бъдат пренасочени към всичките й инстанции и натоварването ще се разпредели посредством алгоритъма round-robin, което ще доведе до следната ситуация:

Обратно към микросервизите с Istio. Част 2
Заявяваните файлове не са намерени

Тези файлове не са намерени, защото по различен начин се наричат в различните версии на приложението. Нека се уверим в това:

$ 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. Част 2
Управление на трафика с ресурси Istio

Забележка: Влиянието на ресурсите Istio върху мрежовия трафик е представено тук по опростен начин за разбиране. За да бъдем точни, решението за това на кой екземпляр да се изпрати заявката се взема от Envoy в Ingress Gateway, настроен в CRD.

С помощта на Destination Rules можем да настроим балансиране на натоварването така, че да се използват последователни хешове и да се гарантира, че отговорите от един и същи екземпляр на услугата отиват при един и същи потребител. Следната конфигурация позволява да постигнем това (destinationrule-sa-frontend.yaml):

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, затова всички заявки ще бъдат разпределени между всички екземпляри:

Обратно към микросервизите с Istio. Част 2

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

Обратно към микросервизите с Istio. Част 2

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

Определение на подмножествата в Destination Rules

Подмножества (subsets) се определят чрез следната конфигурация (sa-logic-subsets-destinationrule.yaml):

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

  1. Хост (host) определя, че това правило се прилага само в случаите, когато маршрутът е насочен към услугата sa-logic;
  2. Имената (name) на подмножествата се използват при маршрутизация към екземплярите на подмножествата;
  3. Етикет (label) определя истините двойки ключ-стойност, на които екземплярите трябва да отговарят, за да станат част от подмножество.

Приложете конфигурацията с помощта на следната команда:

$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets-destinationrule.yaml
destinationrule.networking.istio.io/sa-logic създаден

Сега, когато подмножествата са определени, можем да продължим и да настроим VirtualService, за да приложим правилата за заявките към sa-logic, така че те:

  1. да се маршрутизират към подмножеството v1,
  2. да се огледат към подмножеството v2.

Следващият манифест позволява да се постигне замисленото (sa-logic-subsets-shadowing-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      
    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 % от заявките, но нито един от тези неуспехи не засяга крайните потребители, тъй като им отговаря работещият сервис.

Обратно към микросервизите с Istio. Част 2
Успехът на отговорите от различните версии на сервиза 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.yamlsa-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: 500

VirtualServices активират канарени разпределения: в този случай ограничихме потенциалните последствия от проблемите до 20 % от потребителската база. Прекрасно! Сега, всеки път, когато не сме сигурни в кода си (иначе казано — винаги…), можем да използваме зеркалове и канарени разпределения.

Таймаути и повторения

Но не винаги грешките са в кода. На първо място в списъка "8 заблуждения в разпределените изчисления" е погрешното мнение, че "мрежата е надеждна". Всъщност мрежата не е надеждна и затова имаме нужда от таймаути (timeouts) и повторения (retries).

За демонстрация ще продължим да използваме същия проблем версия sa-logic (buggy), а ненадеждността на мрежата ще симулираме с произволни грешки.

Нека нашият сървис с грешки има 1/3 шанс за прекалено дълъг отговор, 1/3 шанс за грешка Internal Server Error и 1/3 шанс за успешно връщане на страницата.

За да смекчим последиците от подобни проблеми и да подобрим живота на потребителите, можем да:

  1. добавим таймаут, ако сървисът отговаря по-дълго от 8 секунди,
  2. правим повторна опитност, ако в заявката се получи грешка.

За реализация ще използваме следното определение на ресурса (sa-logic-retries-timeouts-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: 50
    - destination: 
        host: sa-logic
        subset: v2
      weight: 50
    timeout: 8s           # 1
    retries:
      attempts: 3         # 2
      perTryTimeout: 3s # 3

  1. Таймаутът за заявките е зададен на 8 секунди;
  2. Повторните опити за заявките се извършват 3 пъти;
  3. И всяка опит се счита за неуспешен, ако времето за отговор надвишава 3 секунди.

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

Приложете актуализираната конфигурация с командата:

$ kubectl apply -f resource-manifests/istio/retries/sa-logic-retries-timeouts-vs.yaml
virtualservice.networking.istio.io/sa-logic конфигуриран

И проверете в графиките на Grafana, че броят на успешните отговори е надхвърлил:

Обратно към микросервизите с Istio. Част 2
Подобрения в статистиката на успешните отговори след добавяне на тайм аутове и повторни опити

Преди да преминете към следващия раздел (или по-точно — вече към следващата част от статията, тъй като в тази няма да има повече практически експерименти — бел. пр.), премахнете 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. от преводача

Прочетете също в нашия блог:

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

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