Mikroserwisy, wszędzie te mikroserwisy…
Tak, znowu będzie o mikroserwisach, bo nie sposób nie wspomnieć o nich w kontekście opisywanego rozwiązania. Zanim jednak przejdziemy do szczegółów architektury Service Mesh, musimy zadać sobie pytanie:
„Jak obecnie wygląda cykl rozwojowy większości współczesnych systemów informatycznych?”.
Obecnie większość dużych i liczących się graczy na rynku wdraża zmiany produkcyjne do swoich strategicznych systemów kilka lub nawet kilkadziesiąt razy dziennie (vide Amazon, Netflix, LinkedIn czy Uber). Widzimy więc, że dynamika zmian ma tutaj kluczowe znaczenie.
Aby nadążyć za intensywnie zmieniającymi się wymaganiami rynku, a co najważniejsze, by cały czas być o krok przed konkurencją, systemy muszą być rozwijane w tempie dotychczas niespotykanym. Kilka lat temu wdrażanie nowych funkcjonalności raz na kwartał wystarczyło, by być w czołówce. Dziś nawet miesięczny proces wydawniczy może okazać się katastrofalny w skutkach finansowych. Podczas gdy my będziemy dopiero w połowie cyklu wydawniczego, konkurencja może być już daleko przed nami. Dlatego, chcąc zapewnić sobie konkurencyjność, musimy skrócić proces produkcyjny zmian w oprogramowaniu. Tu z pomocą przychodzą właśnie mikrousługi.
Bolączki architektury rozproszonej
W organizacjach, gdzie oprogramowanie w architekturze rozproszonej jest rozwijane przez wiele zespołów, każdy z nich niepotrzebnie powtarza część pracy. Przykładowo, duplikowane w serwisach są takie funkcjonalności, jak:
- konfiguracja timeoutów dla połączeń TCP,
- integracja z innymi usługami,
- śledzenie i szyfrowanie ruchu,
- monitoring, zbieranie metryk,
- ponawianie żądań TCP w przypadku awarii,
- …i wiele innych aspektów.
Co prawda, zgodnie z zasadą DRY, większość funkcjonalności można zamknąć w ramach zewnętrznych bibliotek i utrzymywać w jednym miejscu jako zależności. Jednak rozwiązanie takie, często stosowane w początkowych próbach zmierzenia się z problemem duplikacji kodu, nie zawsze prowadzi do oczekiwanych rezultatów. Dlaczego?
Wyzwaniem pozostaje kwestia aktualizowania zależności w poszczególnych usługach, a taka strategia nadal wymusza dodatkowe cykle wydawnicze (deployment) oprogramowania ze zaktualizowanymi zależnościami.
A co w przypadku, gdy zechcemy zmienić zachowanie aplikacji, dodać nową funkcjonalność lub w bieżącej wersji zależności wykryty zostanie błąd? Cóż, pozostaje nam wydanie nowej wersji zależności, a wraz z nią aktualizacja wszystkich usług uruchomionych na produkcji. Widzimy więc, że powyższe podejście nie rozwiązuje problemu w pełni i jest dalekie od ideału. Wspomniana wyżej reguła DRY jest tylko po części spełniona. Mimo że wyeliminowaliśmy duplikację kodu poprzez wydzielenie wspólnego kodu i zamknięcie go w oddzielnej bibliotece, nadal ponosimy koszt aktualizacji oprogramowania. Dodatkowo, jeśli używamy wielu języków programowania do implementacji różnych usług (tzw. polyglot microservices) i cloud native systemów orkiestracji (np. kubernetes) musimy powielać implementację bibliotek. Nie tędy droga.
Service Mesh
A gdyby tak oddelegować powtarzające się w każdej usłudze funkcjonalności na poziom warstwy infrastrukturalnej, zyskując przy tym dodatkowe, nieosiągalne wcześniej możliwości? Tym sposobem dotarliśmy do sedna artykułu – tu z pomocą przychodzi Service Mesh.
Przeczytaj także: Istio Service Mesh
Czym jest Service Mesh
W architekturze Service Mesh do warstwy infrastrukturalnej dokładamy dodatkowe klocki. Jednym z takich klocków jest tzw. sidecar proxy. Implementowany zazwyczaj w postaci lekkiego TCP proxy i, co ważne, jako oddzielny proces wdrażany jest wraz z każdą wymaganą dla biznesu mikrousługą. Od tego momentu usługi nie komunikują się już bezpośrednio, ale ruch sieciowy wychodzący z danej usługi kierowany jest przez infrastrukturę poprzez przyległe proxy do proxy docelowej usługi i dopiero docelowe proxy kieruje ruch przychodzący do docelowej usługi, tak jak na poniższym rysunku:
Warto zaznaczyć, że cały proces jest całkowicie przeźroczysty z punktu widzenia zespołu rozwijającego daną usługę. Usługa też nie musi „wiedzieć”, że nie komunikuje się bezpośrednio z usługą zależną, ale z odpowiadającym jej sidecar proxy. Protokół komunikacji nadal pozostaje ten sam z punktu widzenia usług. W takiej konfiguracji wszystkie wspomniane wcześniej aspekty takie jak aplikowanie timeoutów dla połączeń TCP, autentykacja z innymi usługami, śledzenie i szyfrowanie ruchu, monitoring, zbieranie metryk, ponawianie żądań TCP w przypadku awarii oraz inne oddelegowane są do proxy. Dodatkowo zmiana zachowania którejś z tych funkcjonalności (np. zmiana timeoutów) nie implikuje wydania nowej wersji usługi, a jedynie wprowadzenie zmian do konfiguracji danego proxy. Ponieważ proxy zaprojektowane jest w ten sposób, że dzięki service discovery jego rekonfiguracja nie wymaga restartu, eliminujemy całkowicie okres braku dostępu do usługi (downtime). To już jest duży zysk.
Widzimy wyraźnie, że architektura Service Mesh pozwala skrócić proces wydawniczy.
Service Mesh od środka
Architekturę Service Mesh można rozpatrywać na dwóch płaszczyznach.
- Data plane
O jednej z nich powiedzieliśmy sobie wcześniej. Jest to tzw. data plane – płaszczyzna danych, odpowiedzialna za:
- kierowanie ruchem i jego filtrowanie,
- routing,
- komunikację z usługami,
- Service Discovery,
- szyfrowanie mTLS,
- zbieranie metryk,
- wzbogacanie żądań HTTP o dodatkowe nagłówki wymagane w autentykacji albo umożliwiające rozproszone śledzenie.
W skład warstwy data plane wchodzą wszystkie instancje wspomnianych sidecar proxy w ramach danego klastra infrastruktury. Według danych za pierwszy kwartał 2021 r. najpopularniejszą implementacją tego typu proxy na rynku jest obecnie bardzo dynamicznie rozwijający się Envoy.
- Control plane
Drugą płaszczyzną, na której należy rozpatrywać architekturę Service Mesh, jest control plane. Jest to pojedynczy komponent odpowiedzialny za konfigurację i kontrolę warstwy data plane. Komponent potrafi komunikować się z każdym sidecar proxy w obrębie infrastruktury. Dzięki temu jest w stanie rozsyłać zaktualizowaną konfigurację do każdego z proxy, a w drugą stronę – zbierać i gromadzić metryki czy monitorować stan usług. Innymi słowy, posiada wiedzę o tym, co się dzieje wewnątrz klastra z topologią sieciową na poziomie wdrożonych mikrousług włącznie. Posiadając takie informacje w jednym miejscu, zyskujemy nowe możliwości:
- możemy z łatwością raportować stan klastra w postaci przyjaznych użytkownikowi wizualizacji, takich jak np. graf połączeń między poszczególnymi usługami,
- schodząc niżej, na poziom metryk, możemy zwizualizować usługi, które mają problem z obsługą żądań w zadanym czasie lub np. zagregować po kodach statusu HTTP listę tych odpowiadających największą ilością błędów.
Jakie możliwości daje Service Mesh
Użytkownik może budować własne widoki i wizualizować zbierane dane w zależności od potrzeb. Z drugiej strony możemy za pomocą rozbudowanych reguł poinstruować control plane, aby żądaną część ruchu przekierował do usług w konkretnej wersji. W ten sposób zyskujemy możliwość testowania nowych funkcjonalności na wąskiej, jasno zdefiniowanej grupie użytkowników (tzw. canary deployment).
Jeśli spojrzymy na opisane płaszczyzny Service Mesh, to możemy zauważyć, iż data plane składa się z bezstanowych proxy, które są zarządzane przez stanowy komponent control plane. Takie rozwiązanie upraszcza utrzymanie klastra, ponieważ nie musimy martwić się o synchronizację i zarządzanie rozproszonym stanem, co z definicji jest trudnym, a przy pewnych ograniczeniach wymuszonych przez wymagania nałożone na infrastrukturę, niemożliwym do rozwiązania problemem. W opisywanym podejściu każda potencjalna awaria na poziomie usługi i proxy może być rozwiązana przez control plane poprzez uruchomienie ich nowych instancji. Bezstanowe sidecar proxy zaraz po uruchomieniu pobierze wymaganą konfigurację z centralnego control plane. Proste i efektywne rozwiązanie.
Przeczytaj artykuł: Mikroserwisy – czy to jeszcze rewolucja i nowa jakość czy już standard w projektach?
Gdzie jest haczyk?
Koszty – oczywiście nic nie dostajemy za darmo. Utrzymanie dodatkowych komponentów architektury to stały koszt, na który nie każda organizacja może sobie pozwolić. Jak zawsze pomocny jest tutaj bilans zysków z posiadania dodatkowej warstwy w stosunku do kosztów jej utrzymania.
Kompetencje – wprowadzenie dodatkowych ruchomych części do infrastruktury zwiększa także potencjalne ryzyko awarii. Niepoprawnie wdrożony i skonfigurowany Service Mesh może przynieść więcej szkody niż pożytku, dlatego organizacja musi posiadać inżynierów o odpowiednich kompetencjach umożliwiających utrzymanie Service Mesha.
Większe obciążenie – dodatkowa warstwa komunikacji między usługami w klastrze zwiększa jego obciążenie. Ma to kluczowe znaczenie, zwłaszcza gdy klaster uruchomiony jest w chmurze, bo implikuje dodatkowe koszty i jednocześnie powoduje wydłużenie czasu komunikacji między usługami. Spowodowane jest to tym, że usługi nie komunikują się już bezpośrednio, ale przez dodatkowe proxy. Co prawda w większości przypadków narzut ten jest możliwy do pominięcia i nie ma wpływu na ogólną wydajność systemu, niemniej jednak musimy być świadomi takiego ryzyka.
Kiedy stosować Service Mesh?
Istnieją obszary biznesowe, w których wprowadzenie architektury Service Mesh w ogóle nie wchodzi w grę, ponieważ dodatkowy narzut czasu komunikacji jest krytyczną metryką, której nie można przekroczyć (np. HFT).
Service Mesh – podsumowanie
W odpowiedzi na dynamiczny rozwój systemów i potrzebę skrócenia procesu wydawniczego coraz więcej firm stosuje architekturę mikroserwisową. Nie jest to rozwiązanie idealne, ponieważ musimy liczyć się z dodatkowymi cyklami wydawniczymi, kosztem aktualizacji oprogramowania i powielaniem implementacji. Dzięki architekturze Service Mesh unikamy wydawania nowej wersji usługi i eliminujemy czas jej niedostępności, a cały proces jest przejrzysty. Warto mieć jednak na uwadze, że w pewnych obszarach biznesowych to rozwiązanie nie wchodzi w grę. Jeśli decydujemy się na wdrożenie go w średnich i dużych systemach rozproszonych, to warto zapoznać się z dostępnymi na rynku rozwiązaniami.
Obecnie istnieje wiele gotowych rozwiązań i implementacji architektury Service Mesh zarówno w postaci dostosowanych do wdrożenia komponentów (również open source), jak i gotowych bezpośrednio do użycia jako SaaS od dostawców chmur (AWS czy GKE). W drugiej części artykułu poświęconego Service Mesh postaram się przybliżyć i porównać konkretne rozwiązania i produkty dostępne na rynku.