
Przyp. tłum.: Ten cykl był poświęcony zapoznaniu się z możliwościami Istio oraz ich demonstracji w praktyce. Teraz skupimy się na bardziej złożonych aspektach konfiguracji i użytkowania tego service mesh, a szczególnie — na dokładnej konfiguracji routingu i zarządzaniu ruchem sieciowym.
Przypominamy również, że w artykule używane są konfiguracje (manifesty dla Kubernetes i Istio) z repozytorium .
Zarządzanie ruchem
Dzięki Istio w klastrze pojawiają się nowe możliwości, które pozwalają na:
- Dynamiczne routowanie zapytań: wdrożenia typu canary, A/B-testy;
- Równoważenie obciążenia: proste i niesprzeczne, oparte na haszach;
- Odzyskiwanie po awariach: timeouty, powtórzenia, circuit breakers;
- Wprowadzanie usterek: opóźnienia, zerwanie połączeń itd.
W dalszej części artykułu te możliwości zostaną zaprezentowane na przykładzie wybranej aplikacji, a po drodze przedstawione zostaną nowe koncepcje. Pierwszą z takich koncepcji będą DestinationRules (tj. zasady dotyczące odbiorcy ruchu/zapytań — przyp. tłum.), za pomocą których aktywujemy A/B-testy.
A/B-testy: DestinationRules w praktyce
A/B-testy są stosowane w przypadkach, gdy istnieją dwie wersje aplikacji (zwykle różnią się wizualnie) i nie jesteśmy w 100% pewni, która z nich poprawi interakcję z użytkownikiem. Dlatego jednocześnie uruchamiamy obie wersje i zbieramy metryki.
Aby zrealizować drugą wersję front-endu, niezbędną do demonstracji A/B-testów, wykonaj następujące polecenie:
$ kubectl apply -f resource-manifests/kube/ab-testing/sa-frontend-green-deployment.yaml
deployment.extensions/sa-frontend-green createdManifest deploymentu dla "zielonej wersji" różni się w dwóch miejscach:
- Obraz oparty jest na innym tagu —
istio-green, - Pod’y mają etykietę
version: green.
Ponieważ oba deploymenty mają etykietę app: sa-frontend, zapytania, które są routowane przez wirtualną usługę sa-external-services do usługi sa-frontend, będą przekierowywane do wszystkich jej instancji, a obciążenie zostanie rozdzielone za pomocą , co doprowadzi do następującej sytuacji:

Żądane pliki nie zostały znalezione
Te pliki nie zostały znalezione, ponieważ różnie nazywają się w różnych wersjach aplikacji. Upewnijmy się o tym:
$ 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 To oznacza, że index.html, żądanie jednej wersji statycznych plików może być wysyłane przez load balancer do podów, które mają inną wersję, gdzie, z oczywistych względów, takich plików nie ma. Dlatego, aby aplikacja działała, musimy wprowadzić ograniczenie: "ta sama wersja aplikacji, która zwróciła index.html, powinna obsługiwać także kolejne żądania».
Osiągniemy ten cel dzięki spójnemu balansowaniu obciążenia na podstawie hashy (Spójne balansowanie obciążenia przy użyciu hasha). W takim przypadku żądania od jednego klienta są wysyłane do tego samego egzemplarza backendu, co jest realizowane za pomocą zdefiniowanej właściwości — na przykład nagłówka HTTP. Realizowane jest z pomocą DestinationRules.
DestinationRules
Po tym, jak VirtualService pokierował żądanie do odpowiedniej usługi, za pomocą DestinationRules możemy określić polityki, które będą miały zastosowanie do ruchu przeznaczonego dla egzemplarzy tej usługi:

Zarządzanie ruchem z zasobami Istio
Uwaga: Wpływ zasobów Istio na ruch sieciowy przedstawiony jest tutaj w uproszczony sposób. Mówiąc dokładniej, decyzja, do którego egzemplarza wysłać żądanie, podejmowana jest przez Envoy w Ingress Gateway, skonfigurowanym w CRD.
Za pomocą Destination Rules możemy skonfigurować balansowanie obciążenia tak, aby używane były spójne hashe i gwarantowane były odpowiedzi tego samego egzemplarza usługi dla tego samego użytkownika. Następująca konfiguracja pozwala to osiągnąć ():
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: sa-frontend
spec:
host: sa-frontend
trafficPolicy:
loadBalancer:
consistentHash:
httpHeaderName: version # 1 1 — hash będzie generowany na podstawie zawartości nagłówka HTTP version.
Wdróż tę konfigurację następującą komendą:
$ kubectl apply -f resource-manifests/istio/ab-testing/destinationrule-sa-frontend.yaml
destinationrule.networking.istio.io/sa-frontend utworzony A teraz uruchom poniższą komendę i upewnij się, że otrzymujesz odpowiednie pliki, gdy określasz nagłówek version:
$ curl --silent -H "version: yogo" http://$EXTERNAL_IP/ | tr '"' 'n' | grep mainUwaga: Aby dodawać różne wartości w nagłówku i testować wyniki bezpośrednio w przeglądarce, można skorzystać z do Chrome (lub dla Firefox — przyp. tłum..
W rzeczywistości, DestinationRules mają więcej możliwości w zakresie balansowania obciążenia — szczegóły uzgodnij w .
Zanim przejdziemy dalej w badaniu VirtualService, usuńmy zieloną wersję aplikacji oraz odpowiednią regułę kierowania ruchem, wykonując następujące polecenia:
$ 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" deletedMirroring: Virtual Services w praktyce
Cieniowanie ("shadowing") lub Mirroring ("mirroring") stosuje się w sytuacjach, kiedy chcemy przetestować zmiany w produkcji, nie wpływając na użytkowników końcowych: dublujemy ("lustrzujemy") zapytania na drugi egzemplarz, gdzie wprowadzono potrzebne zmiany, i obserwujemy rezultaty. Mówiąc prościej, to sytuacja, w której twój(a) kolega wybiera najważniejszy problem i zgłasza pull request w postaci ogromnej plątaniny, że nikt nie może go rzeczywiście ocenić.
Aby sprawdzić ten scenariusz w akcji, stworzymy drugi egzemplarz SA-Logic z błędami (buggy), wykonując następujące polecenie:
$ kubectl apply -f resource-manifests/kube/shadowing/sa-logic-service-buggy.yaml
deployment.extensions/sa-logic-buggy created A teraz wykonamy polecenie, aby upewnić się, że wszystkie egzemplarze z app=sa-logic mają również etykiety z odpowiednimi wersjami:
$ 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 Serwis sa-logic jest ukierunkowane na pod’y z etykietą app=sa-logic, dlatego wszystkie zapytania będą rozdzielane pomiędzy wszystkie egzemplarze:

… ale chcemy, aby zapytania były kierowane na egzemplarze z wersją v1 i były lustrzowane na egzemplarze z wersją v2:

Osiągniemy to za pomocą VirtualService w połączeniu z DestinationRule, gdzie zasady określają podzbiory i trasy VirtualService do konkretnego podzbioru.
Definicja podzbiorów w Destination Rules
Podzbiory (subsets) są definiowane następującą konfiguracją ():
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 (
Najciekawsza linia — ostatnia.) określa, że zasada ta stosuje się tylko w przypadkach, gdy trasa prowadzi do usługisa-logic; - Nazwy (
name) podzbiorów są używane do kierowania na egzemplarze podzbioru; - Etykieta (
label) definiuje pary klucz-wartość, którym muszą odpowiadać instancje, aby stały się częścią podzbioru.
Wdróż tę konfigurację następującą komendą:
$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets-destinationrule.yaml
destinationrule.networking.istio.io/sa-logic utworzonoTeraz, gdy podzbiory zostały zdefiniowane, możemy przejść do konfiguracji VirtualService, aby zastosować zasady do żądań do sa-logic, aby:
- były kierowane do podzbioru
v1, - były lustrowane do podzbioru
v2.
Następny manifest pozwala osiągnąć zamierzony efekt ():
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: v2Tutaj nie są potrzebne wyjaśnienia, więc po prostu zobaczmy to w akcji:
$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets-shadowing-vs.yaml
virtualservice.networking.istio.io/sa-logic utworzonoDodamy obciążenie, wywołując następującą komendę:
$ while true; do curl -v http://$EXTERNAL_IP/sentiment
-H "Content-type: application/json"
-d '{"sentence": "I love yogobella"}';
sleep .8; done Zobaczymy wyniki w Grafanie, gdzie można zauważyć, że wersja z błędami (buggy) prowadzi do błędów w ~60 % żądań, ale żadne z tych błędów nie wpływa na użytkowników końcowych, ponieważ odpowiada im działający serwis.

Skuteczność odpowiedzi różnych wersji serwisu sa-logic
Tutaj po raz pierwszy zobaczyliśmy, jak VirtualService jest stosowany wobec Envoy naszych serwisów: kiedy sa-web-app wysyła żądanie do sa-logic, przechodzi przez sidecar Envoy, który — poprzez VirtualService — jest skonfigurowany do kierowania żądania do podzbioru v1 i lustrowania żądania do podzbioru v2 serwisu sa-logic.
Wiem: już pomyślałeś, że Virtual Services są proste. W następnym rozdziale rozszerzymy to zdanie, pokazując, że są również naprawdę wspaniałe.
Canary releases
Canary Deployment — proces wprowadzania nowej wersji aplikacji dla niewielkiej liczby użytkowników. Używa się go, aby upewnić się, że w wersji nie ma problemów i tylko po tym, będąc pewnym dostatecznej jakości wersji, rozprzestrzenić ją na większą publiczność.większąAby zademonstrować wdrożenie kanaryjne, będziemy kontynuować pracę z podzbiorem
Nie będziemy się ograniczać i od razu skierujemy 20 % użytkowników na wersję z błędami (to będzie nasza kanaryjna wersja), a pozostałe 80 % — na normalny serwis. W tym celu zastosujemy następujący VirtualService ( buggy u 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 — to waga (waga), która określa procent żądań, które będą kierowane do odbiorcy lub podzbioru odbiorcy.
Zaktualizujemy poprzednią konfigurację VirtualService za pomocą sa-logic następującego polecenia:
$ kubectl apply -f resource-manifests/istio/canary/sa-logic-subsets-canary-vs.yaml
virtualservice.networking.istio.io/sa-logic skonfigurowany… i natychmiast zobaczymy, że część żądań kończy się błędami:
$ 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 aktywują wypuszczenia kanaryjskie: w tym przypadku ograniczyliśmy potencjalne konsekwencje problemów do 20% bazy użytkowników. Świetnie! Teraz w każdym przypadku, gdy nie jesteśmy pewni naszego kodu (innymi słowy — zawsze…), możemy użyć mirroringu i wypuszczeń kanaryjskich.
Limity czasowe i próby ponowne
Jednak nie zawsze błędy występują w kodzie. Na liście „” na pierwszym miejscu znajduje się błędne przekonanie, że „sieć jest niezawodna”. W rzeczywistości sieć nie jest niezawodna, i z tego powodu potrzebujemy limitów czasowych (timeouts) i prób ponownych (retries).
Aby zademonstrować, wciąż będziemy używać tej samej problematycznej wersji sa-logic (buggy), a niestabilność sieci symulować losowymi awariami.
Niech nasza usługa z błędami ma 1/3 szans na zbyt długi czas odpowiedzi, 1/3 — na zakończenie błędem wewnętrznym serwera i 1/3 — na pomyślne zwrócenie strony.
Aby złagodzić skutki takich problemów i poprawić życie użytkowników, możemy:
- dodać limit czasowy, jeśli usługa odpowiada dłużej niż 8 sekund,
- próbować ponownie, jeśli żądanie kończy się błędem.
Aby to zaimplementować, skorzystamy z takiej definicji zasobu ():
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- Limit czasowy dla żądania wynosi 8 sekund;
- Próby ponowne żądań są podejmowane 3 razy;
- Każda próba jest uznawana za nieudaną, jeśli czas odpowiedzi przekracza 3 sekundy.
W ten sposób osiągnęliśmy optymalizację, ponieważ użytkownik nie będzie musiał czekać dłużej niż 8 sekund, a my podejmiemy trzy nowe próby uzyskania odpowiedzi w przypadku błędów, zwiększając szansę na udaną odpowiedź.
Zastosuj zaktualizowaną konfigurację następującą komendą:
$ kubectl apply -f resource-manifests/istio/retries/sa-logic-retries-timeouts-vs.yaml
virtualservice.networking.istio.io/sa-logic skonfigurowanyI sprawdź w wykresach Grafana, że liczba udanych odpowiedzi wzrosła powyżej:

Poprawa w statystykach udanych odpowiedzi po dodaniu timeoutów i ponownych prób
Przed przejściem do następnej sekcji (a dokładniej — do następnej części artykułu, ponieważ w tej części nie będzie już praktycznych eksperymentów — przyp. tłum.), usuń sa-logic-buggy i VirtualService, wykonując następujące komendy:
$ kubectl delete deployment sa-logic-buggy
deployment.extensions “sa-logic-buggy” usunięty
$ kubectl delete virtualservice sa-logic
virtualservice.networking.istio.io “sa-logic” usuniętyWzorce Circuit Breaker i Bulkhead
Mówimy o dwóch ważnych wzorcach w architekturze mikroserwisów, które pozwalają na samodzielne uzdrawianie (self-healing) usług.
Circuit Breaker („wyłącznik automatyczny”) jest używany do przerywania żądań kierowanych do instancji usługi, która jest uznawana za niezdrową, a jej przywrócenia w momencie, gdy żądania klientów są przekierowywane do zdrowych instancji tej usługi (co zwiększa procent udanych odpowiedzi). (Przyp. tłum.: Bardziej szczegółowy opis wzorca można znaleźć w .)
Bulkhead („przegroda”) izoluje awarie w usługach od wpłynięcia na cały system. Na przykład, jeśli usługa B jest uszkodzona, a inna usługa (klient usługi B) wysyła zapytanie do usługi B, w rezultacie wykorzysta swoją pulę wątków i nie będzie mogła obsłużyć innych zapytań (nawet jeśli nie dotyczą one usługi B). (Przyp. tłum.: Bardziej szczegółowy opis wzorca można znaleźć w .)
Pomińmy szczegóły dotyczące implementacji tych wzorców, ponieważ łatwo je znaleźć w , a także naprawdę chcę pokazać autoryzację i uwierzytelnienie, o których mowa będzie w następnej części artykułu.
P.S. od tłumacza
Przeczytaj także na naszym blogu:
- „Powrót do mikroserwisów z Istio”: , ;
- «»;
- «».
Źródło: habr.com
