Netramesh - lekkie rozwiązanie siatki usługowej

Przechodząc od aplikacji monolitycznej do architektury mikroserwisowej, stajemy przed nowymi wyzwaniami.

W aplikacji monolitycznej zazwyczaj dość łatwo jest określić, w której części systemu wystąpił błąd. Najprawdopodobniej problem tkwi w kodzie samego monolitu lub w bazie danych. Kiedy jednak zaczynamy szukać problemu w architekturze mikroserwisowej, wszystko nie jest już takie oczywiste. Musimy znaleźć całą ścieżkę, którą przeszło żądanie od początku do końca i wybrać ją spośród setek mikroserwisów. Co więcej, wiele z nich ma także własne magazyny, co również może powodować błędy logiczne, a także problemy z wydajnością i odpornością na awarie.

Netramesh - lekkie rozwiązanie siatki usługowej

Długo szukałem narzędzia, które pomogłoby uporać się z takimi problemami (pisałem o tym na Habré: 1, 2), ale ostatecznie stworzyłem własne rozwiązanie open source. W tym artykule mówię o zaletach podejścia Service Mesh i dzielę się nowym narzędziem do jego wdrożenia.

Śledzenie rozproszone jest powszechnym rozwiązaniem problemu znajdowania błędów w systemach rozproszonych. Ale co jeśli takie podejście do zbierania informacji o interakcjach sieciowych nie zostało jeszcze zaimplementowane w systemie, lub co gorsza, w części systemu już działa poprawnie, a w części nie, ponieważ nie zostało dodane do starych usług ? Aby określić dokładną przyczynę problemu, niezbędny jest pełny obraz tego, co dzieje się w systemie. Szczególnie ważne jest zrozumienie, które mikrousługi są zaangażowane w kluczowe ścieżki krytyczne dla biznesu.

Tutaj z pomocą może nam przyjść podejście Service Mesh, które zajmie się całą maszynerią zbierającą informacje sieciowe na poziomie niższym niż same usługi. Takie podejście pozwala nam przechwytywać cały ruch i analizować go na bieżąco. Co więcej, aplikacje nawet nie muszą nic o tym wiedzieć.

Podejście oparte na siatce usług

Główną ideą podejścia Service Mesh jest dodanie kolejnej warstwy infrastruktury w sieci, która pozwoli nam zrobić wszystko z interakcją między usługami. Większość implementacji działa w następujący sposób: do każdej mikrousługi dodawany jest dodatkowy kontener sidecar z przezroczystym proxy, przez który przechodzi cały ruch przychodzący i wychodzący usługi. I to właśnie w tym miejscu możemy dokonać balansu klientów, zastosować polityki bezpieczeństwa, nałożyć ograniczenia na liczbę żądań i zebrać ważne informacje na temat interakcji usług w produkcji.

Netramesh - lekkie rozwiązanie siatki usługowej

Solutions

Istnieje już kilka implementacji tego podejścia: Podobnie и linkerd2. Zapewniają wiele funkcji od razu po wyjęciu z pudełka. Ale jednocześnie wiąże się to z dużym obciążeniem zasobów. Co więcej, im większy klaster, w którym taki system funkcjonuje, tym więcej zasobów będzie potrzebnych do utrzymania nowej infrastruktury. W Avito obsługujemy klastry kubernetes, które zawierają tysiące instancji usług (a ich liczba stale rośnie). W obecnej implementacji Istio zużywa ~ 300 MB pamięci RAM na instancję usługi. Ze względu na dużą liczbę możliwości przejrzyste równoważenie wpływa również na całkowity czas reakcji usług (do 10ms).

W rezultacie sprawdziliśmy, jakich dokładnie możliwości potrzebujemy w tej chwili i zdecydowaliśmy, że głównym powodem, dla którego rozpoczęliśmy wdrażanie takich rozwiązań, była możliwość przejrzystego zbierania informacji śledzących z całego systemu. Chcieliśmy także mieć kontrolę nad interakcją usług i wykonywać różne manipulacje na nagłówkach przesyłanych między usługami.

W rezultacie doszliśmy do naszej decyzji:  Netramesh.

Netramesh

Netramesh to lekkie rozwiązanie Service Mesh z możliwością nieograniczonego skalowania, niezależnie od liczby usług w systemie.

Głównymi celami nowego rozwiązania były niskie obciążenie zasobów i wysoka wydajność. Wśród głównych funkcji od razu chcieliśmy mieć możliwość przejrzystego wysyłania rozpiętości śledzenia do naszego systemu Jaeger.

Obecnie większość rozwiązań chmurowych wdrażana jest w Golang. I oczywiście istnieją ku temu powody. Pisanie aplikacji sieciowych w Golangu, które działają asynchronicznie z wejściami/wyjściami i skalują się pomiędzy rdzeniami w miarę potrzeb, jest wygodne i całkiem proste. I co również bardzo ważne, wydajność jest wystarczająca, aby rozwiązać ten problem. Dlatego też wybraliśmy Golang.

produktywność

Skoncentrowaliśmy nasze wysiłki na osiągnięciu maksymalnej produktywności. W przypadku rozwiązania wdrażanego obok każdej instancji usługi wymagane jest niewielkie zużycie pamięci RAM i czasu procesora. I oczywiście opóźnienie reakcji również powinno być niewielkie.

Zobaczmy, jakie rezultaty uzyskaliśmy.

RAM

Netramesh zużywa ~10Mb bez ruchu i maksymalnie 50Mb przy obciążeniu do 10000 XNUMX RPS na instancję.

Serwer proxy Istio envoy zawsze zużywa ~ 300 MB w naszych klastrach z tysiącami instancji. Nie pozwala to na skalowanie go do całego klastra.

Netramesh - lekkie rozwiązanie siatki usługowej

Netramesh - lekkie rozwiązanie siatki usługowej

Dzięki Netramesh uzyskaliśmy ~10-krotne zmniejszenie zużycia pamięci.

CPU

Użycie procesora jest stosunkowo równe pod obciążeniem. Zależy to od liczby żądań w jednostce czasu kierowanych do wózka bocznego. Wartości przy 3000 żądań na sekundę w szczycie:

Netramesh - lekkie rozwiązanie siatki usługowej

Netramesh - lekkie rozwiązanie siatki usługowej

Jest jeszcze jeden ważny punkt: Netramesh - rozwiązanie bez płaszczyzny sterującej i bez obciążenia nie zużywa czasu procesora. Dzięki Istio przyczepy boczne zawsze aktualizują punkty końcowe usługi. W rezultacie możemy zobaczyć ten obraz bez obciążenia:

Netramesh - lekkie rozwiązanie siatki usługowej

Do komunikacji pomiędzy usługami używamy protokołu HTTP/1. Wydłużenie czasu odpowiedzi Istio przy proxy poprzez envoy wyniosło nawet 5-10 ms, co jest całkiem sporo jak na usługi, które są gotowe odpowiedzieć w ciągu milisekundy. W przypadku Netramesh czas ten spadł do 0.5-2 ms.

Skalowalność

Niewielka ilość zasobów zużywanych przez każdy serwer proxy pozwala na umieszczenie go obok każdej usługi. Netramesh został celowo stworzony bez komponentu płaszczyzny sterującej, aby po prostu zachować lekkość każdego wózka bocznego. Często w rozwiązaniach Service Mesh płaszczyzna sterowania dystrybuuje informacje o wykrywaniu usług do każdego wózka bocznego. Wraz z nim pojawiają się informacje o przekroczeniach limitów czasu i ustawieniach balansu. Wszystko to pozwala zrobić wiele przydatnych rzeczy, ale niestety zawyża rozmiary wózków bocznych.

Wykrywanie usług

Netramesh - lekkie rozwiązanie siatki usługowej

Netramesh nie dodaje żadnych dodatkowych mechanizmów wykrywania usług. Cały ruch jest przekazywany w sposób przejrzysty poprzez netra sidecar.

Netramesh obsługuje protokół aplikacji HTTP/1. Do jego zdefiniowania wykorzystywana jest konfigurowalna lista portów. Zazwyczaj system posiada kilka portów, poprzez które odbywa się komunikacja HTTP. Na przykład do interakcji pomiędzy usługami i żądaniami zewnętrznymi używamy liczb 80, 8890, 8080. W tym przypadku można je ustawić za pomocą zmiennej środowiskowej NETRA_HTTP_PORTS.

Jeśli używasz Kubernetesa jako koordynatora i jego mechanizmu jednostki usługi do komunikacji wewnątrz klastra między usługami, mechanizm pozostaje dokładnie taki sam. Najpierw mikroserwis uzyskuje adres IP usługi za pomocą kube-dns i otwiera z nim nowe połączenie. To połączenie jest najpierw nawiązywane z lokalnym netra-sidecar i wszystkie pakiety TCP początkowo docierają do netry. Następnie netra-sidecar nawiązuje połączenie z pierwotnym miejscem docelowym. NAT na IP poda na węźle pozostaje dokładnie taki sam jak bez Netry.

Rozproszone śledzenie i przekazywanie kontekstu

Netramesh zapewnia funkcjonalność potrzebną do wysyłania zakresów śledzenia interakcji HTTP. Netra-sidecar analizuje protokół HTTP, mierzy opóźnienia żądań i wyodrębnia niezbędne informacje z nagłówków HTTP. Ostatecznie wszystkie ślady uzyskujemy w jednym systemie Jaeger. W przypadku szczegółowej konfiguracji można również użyć zmiennych środowiskowych dostarczonych przez oficjalną bibliotekę biblioteka jaeger go.

Netramesh - lekkie rozwiązanie siatki usługowej

Netramesh - lekkie rozwiązanie siatki usługowej

Ale jest problem. Dopóki usługi nie wygenerują i nie wyślą specjalnego nagłówka Uber, nie zobaczymy w systemie połączonych zakresów śledzenia. A tego nam potrzeba, żeby szybko znaleźć przyczynę problemów. Tutaj ponownie Netramesh ma rozwiązanie. Serwery proxy odczytują nagłówki HTTP i, jeśli nie zawierają identyfikatora śledzenia Uber, generują go. Netramesh przechowuje również informacje o żądaniach przychodzących i wychodzących w przyczepce bocznej i dopasowuje je, wzbogacając je o niezbędne nagłówki żądań wychodzących. Wszystko, co musisz zrobić w usługach, to wysłać tylko jeden nagłówek X-Request-Id, który można skonfigurować za pomocą zmiennej środowiskowej NETRA_HTTP_REQUEST_ID_HEADER_NAME. Aby kontrolować rozmiar kontekstu w Netramesh, możesz ustawić następujące zmienne środowiskowe: NETRA_TRACING_CONTEXT_EXPIRATION_MILLISECONDS (czas, przez jaki kontekst będzie przechowywany) oraz NETRA_TRACING_CONTEXT_CLEANUP_INTERVAL (częstotliwość czyszczenia kontekstu).

Możliwe jest także łączenie wielu ścieżek w systemie poprzez oznaczenie ich specjalnym tokenem sesji. Netra umożliwia instalację HTTP_HEADER_TAG_MAP aby zamienić nagłówki HTTP w odpowiednie znaczniki zakresu śledzenia. Może to być szczególnie przydatne podczas testowania. Po przejściu testu funkcjonalnego możesz zobaczyć, na którą część systemu wpływa filtrowanie według odpowiedniego klucza sesji.

Określanie źródła żądania

Aby ustalić skąd przyszło żądanie, możesz skorzystać z funkcjonalności automatycznego dodania nagłówka ze źródłem. Korzystanie ze zmiennej środowiskowej NETRA_HTTP_X_SOURCE_HEADER_NAME Możesz określić nazwę nagłówka, który zostanie zainstalowany automatycznie. Używając NETRA_HTTP_X_SOURCE_VALUE możesz ustawić wartość, na którą będzie ustawiony nagłówek X-Source dla wszystkich żądań wychodzących.

Umożliwia to równomierną dystrybucję tego użytecznego nagłówka w całej sieci. Następnie możesz użyć go w usługach i dodać do dzienników i metryk.

Routing ruchu i elementy wewnętrzne Netramesh

Netramesh składa się z dwóch głównych komponentów. Pierwszy, netra-init, ustawia reguły sieciowe w celu przechwytywania ruchu. On używa reguły przekierowań iptables do przechwytywania całości lub części ruchu na wózku bocznym, co jest drugim głównym elementem Netramesh. Możesz skonfigurować, które porty mają być przechwytywane dla przychodzących i wychodzących sesji TCP: INBOUND_INTERCEPT_PORTS, OUTBOUND_INTERCEPT_PORTS.

Narzędzie posiada również ciekawą funkcję - routing probabilistyczny. Jeśli używasz Netramesh wyłącznie do zbierania rozpiętości śledzenia, to w środowisku produkcyjnym możesz zaoszczędzić zasoby i włączyć routing probabilistyczny za pomocą zmiennych NETRA_INBOUND_PROBABILITY и NETRA_OUTBOUND_PROBABILITY (od 0 do 1). Wartość domyślna to 1 (cały ruch jest przechwytywany).

Po pomyślnym przechwyceniu, Netra Sidecar akceptuje nowe połączenie i używa SO_ORIGINAL_DST opcję gniazda, aby uzyskać oryginalne miejsce docelowe. Następnie Netra otwiera nowe połączenie z pierwotnym adresem IP i ustanawia dwukierunkową komunikację TCP pomiędzy stronami, nasłuchując całego przechodzącego ruchu. Jeśli port jest zdefiniowany jako HTTP, Netra próbuje go przeanalizować i prześledzić. Jeśli analiza HTTP nie powiedzie się, Netra powróci do protokołu TCP i w przezroczysty sposób przesyła bajty przez serwer proxy.

Budowanie grafu zależności

Po otrzymaniu dużej ilości informacji o śledzeniu w Jaegerze chcę uzyskać pełny wykres interakcji w systemie. Jeśli jednak Twój system jest dość obciążony i każdego dnia gromadzą się miliardy zakresów śledzenia, ich agregowanie nie będzie już takim łatwym zadaniem. Jest na to oficjalny sposób: zależności od iskier. Jednak utworzenie pełnego wykresu zajmie wiele godzin i zmusi Cię do pobrania całego zestawu danych z Jaegera z ostatnich XNUMX godzin.

Jeśli używasz Elasticsearch do przechowywania zakresów śledzenia, możesz użyć proste narzędzie Golang, który w ciągu kilku minut zbuduje ten sam wykres, korzystając z funkcji i możliwości Elasticsearch.

Netramesh - lekkie rozwiązanie siatki usługowej

Jak korzystać z Netramesha

Netrę można łatwo dodać do dowolnej usługi obsługującej dowolnego orkiestratora. Możesz zobaczyć przykład tutaj.

Na chwilę obecną Netra nie ma możliwości automatycznego wdrożenia sidecarów do usług, ale są plany ich wdrożenia.

Przyszłość Netramesha

główny cel Netramesh jest osiągnięcie minimalnych kosztów zasobów i wysokiej wydajności, zapewniając podstawowe możliwości obserwowalności i kontroli komunikacji między usługami.

W przyszłości Netramesh będzie obsługiwał inne protokoły warstwy aplikacji oprócz HTTP. Routing L7 będzie dostępny w najbliższej przyszłości.

Skorzystaj z Netramesh jeśli napotkasz podobne problemy i napisz do nas z pytaniami i sugestiami.

Źródło: www.habr.com

Dodaj komentarz