Kontenery, mikroserwisy i siatki usług

W Internecie stos artykuły о siatka serwisowa (siatka serwisowa), a oto kolejny. Brawo! Ale dlaczego? Następnie chcę wyrazić swoją opinię, że byłoby lepiej, gdyby siatki usług pojawiły się 10 lat temu, przed pojawieniem się platform kontenerowych takich jak Docker i Kubernetes. Nie twierdzę, że mój punkt widzenia jest lepszy lub gorszy od innych, ale ponieważ siatki usług są dość złożonymi zwierzętami, wiele punktów widzenia pomoże je lepiej zrozumieć.

Opowiem o platformie dotCloud, która została zbudowana na ponad stu mikroserwisach i obsługiwała tysiące skonteneryzowanych aplikacji. Wyjaśnię wyzwania, przed którymi stanęliśmy podczas jego opracowywania i uruchamiania oraz w jaki sposób siatki usług mogą (lub nie mogą) pomóc.

Historia dotCloud

Pisałem już o historii dotCloud i wyborach architektury dla tej platformy, jednak niewiele mówiłem o warstwie sieciowej. Jeśli nie chcesz zagłębiać się w czytanie ostatni artykuł o dotCloud, oto sedno w skrócie: jest to platforma PaaS jako usługa, która umożliwia klientom uruchamianie szerokiej gamy aplikacji (Java, PHP, Python...) z obsługą szerokiego zakresu danych usług (MongoDB, MySQL, Redis...) i przepływ pracy taki jak Heroku: przesyłasz swój kod na platformę, ona tworzy obrazy kontenerów i wdraża je.

Opowiem Wam jak ruch został przekierowany na platformę dotCloud. Nie dlatego, że był szczególnie fajny (chociaż system jak na swoje czasy działał dobrze!), ale przede wszystkim dlatego, że przy nowoczesnych narzędziach taki projekt może być łatwo wdrożony w krótkim czasie przez skromną ekipę, jeśli potrzebuje sposobu na poprowadzenie ruchu pomiędzy paczką mikroserwisów lub zestawu aplikacji. W ten sposób możesz porównać opcje: co się stanie, jeśli wszystko opracujesz samodzielnie lub skorzystasz z istniejącej siatki usług. Standardowym wyborem jest samodzielne wykonanie lub zakup.

Routing ruchu dla hostowanych aplikacji

Aplikacje w dotCloud mogą udostępniać punkty końcowe HTTP i TCP.

Punkty końcowe HTTP dynamicznie dodawane do konfiguracji klastra modułu równoważenia obciążenia Hipache. Jest to podobne do tego, co robią dzisiaj zasoby Ingres w Kubernetes i moduł równoważenia obciążenia podobny Traefik.

Klienci łączą się z punktami końcowymi HTTP za pośrednictwem odpowiednich domen, pod warunkiem, że nazwa domeny wskazuje na moduły równoważenia obciążenia dotCloud. Nic specjalnego.

Punkty końcowe TCP powiązany z numerem portu, który jest następnie przekazywany do wszystkich kontenerów na tym stosie za pośrednictwem zmiennych środowiskowych.

Klienci mogą łączyć się z punktami końcowymi TCP, używając odpowiedniej nazwy hosta (na przykład gateway-X.dotcloud.com) i numeru portu.

Ta nazwa hosta jest tłumaczona na klaster serwerów „nats” (niezwiązany z NATS), który będzie kierował przychodzące połączenia TCP do odpowiedniego kontenera (lub, w przypadku usług z równoważeniem obciążenia, do właściwych kontenerów).

Jeśli znasz Kubernetes, prawdopodobnie przypomni Ci to o usługach Port węzła.

Na platformie dotCloud nie było równoważnych usług IP klastra: Dla uproszczenia dostęp do usług był taki sam zarówno z poziomu platformy, jak i z zewnątrz.

Wszystko było zorganizowane po prostu: początkowe implementacje sieci routingowych HTTP i TCP składały się prawdopodobnie z zaledwie kilkuset linii Pythona. Proste (powiedziałbym naiwne) algorytmy, które udoskonalano wraz z rozwojem platformy i pojawianiem się dodatkowych wymagań.

Nie była wymagana rozległa refaktoryzacja istniejącego kodu. W szczególności, Aplikacje 12-czynnikowe może bezpośrednio użyć adresu uzyskanego poprzez zmienne środowiskowe.

Czym to się różni od nowoczesnej siatki usług?

Ograniczony widoczność. W ogóle nie mieliśmy żadnych metryk dla siatki routingu TCP. Jeśli chodzi o routing HTTP, w późniejszych wersjach wprowadzono szczegółowe metryki HTTP z kodami błędów i czasami odpowiedzi, ale nowoczesne siatki usług idą jeszcze dalej, zapewniając integrację z systemami gromadzenia metryk, takimi jak na przykład Prometheus.

Widoczność jest ważna nie tylko z punktu widzenia operacyjnego (pomaga w rozwiązywaniu problemów), ale także podczas udostępniania nowych funkcji. Chodzi o bezpieczeństwo wdrożenie niebiesko-zielone и rozmieszczenie kanarków.

Wydajność routingu jest również ograniczona. W siatce routingu dotCloud cały ruch musiał przechodzić przez klaster dedykowanych węzłów routingu. Oznaczało to potencjalne przekroczenie wielu granic AZ (strefy dostępności) i znaczne zwiększenie opóźnień. Pamiętam, jak rozwiązywałem problemy z kodem, który powodował ponad sto zapytań SQL na stronę i otwierał nowe połączenie z serwerem SQL dla każdego zapytania. Przy uruchomieniu lokalnym strona ładuje się natychmiast, natomiast w dotCloud ładowanie zajmuje kilka sekund, ponieważ każde połączenie TCP (i kolejne zapytanie SQL) zajmuje dziesiątki milisekund. W tym konkretnym przypadku trwałe połączenia rozwiązały problem.

Nowoczesne siatki usługowe lepiej radzą sobie z takimi problemami. Przede wszystkim sprawdzają, czy połączenia są trasowane w źródle. Przebieg logiczny jest taki sam: клиент → меш → сервис, ale teraz siatka działa lokalnie, a nie na odległych węzłach, więc połączenie клиент → меш jest lokalny i bardzo szybki (mikrosekundy zamiast milisekund).

Nowoczesne siatki usług implementują również inteligentniejsze algorytmy równoważenia obciążenia. Monitorując stan backendów, mogą wysyłać większy ruch do szybszych backendów, co skutkuje lepszą ogólną wydajnością.

bezpieczeństwo też lepiej. Siatka routingu dotCloud działała w całości na EC2 Classic i nie szyfrowała ruchu (w oparciu o założenie, że jeśli komuś udało się umieścić sniffera na ruchu sieciowym EC2, to już miał duże kłopoty). Nowoczesne siatki usług w przejrzysty sposób chronią cały nasz ruch, na przykład poprzez wzajemne uwierzytelnianie TLS i późniejsze szyfrowanie.

Kierowanie ruchu dla usług platformy

OK, omówiliśmy ruch pomiędzy aplikacjami, ale co z samą platformą dotCloud?

Sama platforma składała się z około stu mikroserwisów odpowiedzialnych za różne funkcje. Niektórzy przyjmowali prośby od innych, a niektórzy byli pracownikami działającymi w tle, którzy łączyli się z innymi usługami, ale sami nie akceptowali połączeń. W każdym razie każda usługa musi znać punkty końcowe adresów, z którymi ma się połączyć.

Wiele usług wysokiego poziomu może korzystać z siatki routingu opisanej powyżej. W rzeczywistości wiele z ponad stu mikrousług dotCloud zostało wdrożonych jako zwykłe aplikacje na samej platformie dotCloud. Jednak niewielka liczba usług niskiego poziomu (szczególnie tych, które implementują tę siatkę routingu) potrzebowała czegoś prostszego, z mniejszą liczbą zależności (ponieważ nie mogły polegać na sobie w działaniu – stary, dobry problem kury i jajka).

Te niskopoziomowe usługi o znaczeniu krytycznym zostały wdrożone poprzez uruchomienie kontenerów bezpośrednio w kilku kluczowych węzłach. W tym przypadku nie wykorzystano standardowych usług platformy: linkera, harmonogramu i runnera. Jeśli chcesz porównać z nowoczesnymi platformami kontenerowymi, to tak, jakby uruchomić płaszczyznę kontrolną docker run bezpośrednio na węzłach, zamiast delegować zadanie Kubernetesowi. Koncepcja jest dość podobna moduły statyczne (strąki), z którego korzysta kubeadm lub bootkube podczas uruchamiania samodzielnego klastra.

Usługi te zostały ujawnione w prosty i prymitywny sposób: plik YAML zawierał ich nazwy i adresy; i każdy klient musiał wziąć kopię tego pliku YAML do wdrożenia.

Z jednej strony jest niezwykle niezawodny, ponieważ nie wymaga obsługi zewnętrznego magazynu kluczy/wartości takiego jak Zookeeper (pamiętaj, itp. lub Consul nie istniał w tamtym czasie). Z drugiej strony utrudniało to przenoszenie usług. Za każdym razem, gdy wykonywano ruch, wszyscy klienci otrzymywali zaktualizowany plik YAML (i potencjalnie restartowali). Niezbyt wygodne!

Następnie zaczęliśmy wdrażać nowy schemat, w którym każdy klient łączył się z lokalnym serwerem proxy. Zamiast adresu i portu wystarczy znać numer portu usługi i połączyć się przez localhost. Lokalny serwer proxy obsługuje to połączenie i przekazuje je do rzeczywistego serwera. Teraz, podczas przenoszenia backendu na inną maszynę lub skalowania, zamiast aktualizować wszystkich klientów, wystarczy zaktualizować wszystkie lokalne serwery proxy; i ponowne uruchomienie nie jest już wymagane.

(Planowano także enkapsulację ruchu w połączeniach TLS i umieszczenie po stronie odbierającej innego serwera proxy oraz weryfikację certyfikatów TLS bez udziału usługi odbierającej, która jest skonfigurowana tak, aby akceptować połączenia tylko po stronie localhost. Więcej na ten temat później).

To jest bardzo podobne do Inteligentny stos od Airbnb, ale znacząca różnica polega na tym, że SmartStack jest wdrożony i wdrożony w środowisku produkcyjnym, podczas gdy wewnętrzny system routingu dotCloud został odłożony na półkę, gdy dotCloud stał się Dockerem.

Osobiście uważam SmartStack za jednego z poprzedników systemów takich jak Istio, Linkerd i Consul Connect, ponieważ wszystkie działają według tego samego schematu:

  • Uruchom serwer proxy w każdym węźle.
  • Klienci łączą się z serwerem proxy.
  • Płaszczyzna kontroli aktualizuje konfigurację serwera proxy w przypadku zmiany zaplecza.
  • ... Zysk!

Nowoczesna implementacja siatki usług

Gdybyśmy dzisiaj musieli wdrożyć podobną siatkę, moglibyśmy zastosować podobne zasady. Na przykład skonfiguruj wewnętrzną strefę DNS, mapując nazwy usług na adresy w przestrzeni 127.0.0.0/8. Następnie uruchom HAProxy na każdym węźle klastra, akceptując połączenia pod każdym adresem usługi (w tej podsieci 127.0.0.0/8) i przekierowanie/równoważenie obciążenia do odpowiednich backendów. Można kontrolować konfigurację HAProxy konfd, umożliwiając przechowywanie informacji o backendie w etcd lub Consul i w razie potrzeby automatyczne przesyłanie zaktualizowanej konfiguracji do HAProxy.

Tak mniej więcej działa Istio! Ale z pewnymi różnicami:

  • Zastosowania Pełnomocnik wysłannika zamiast HAProxy.
  • Przechowuje konfigurację backendu poprzez Kubernetes API zamiast etcd lub Consul.
  • Usługom przydzielane są adresy w wewnętrznej podsieci (adresy Kubernetes ClusterIP) zamiast 127.0.0.0/8.
  • Posiada dodatkowy komponent (Citadel) umożliwiający dodanie wzajemnego uwierzytelniania TLS pomiędzy klientem a serwerami.
  • Obsługuje nowe funkcje, takie jak przerywanie obwodów, śledzenie rozproszone, wdrażanie Canary itp.

Rzućmy okiem na niektóre różnice.

Pełnomocnik wysłannika

Envoy Proxy został napisany przez Lyfta [konkurenta Ubera na rynku taksówkowym – ok. uliczka]. Jest pod wieloma względami podobny do innych serwerów proxy (np. HAProxy, Nginx, Traefik...), ale Lyft napisał własne, ponieważ potrzebował funkcji, których brakowało innym proxy, i wydawało się mądrzej zrobić nowy, zamiast rozszerzać istniejący.

Envoy może być używany samodzielnie. Jeśli mam konkretną usługę, która wymaga połączenia z innymi usługami, mogę ją skonfigurować tak, aby łączyła się z Envoy, a następnie dynamicznie konfigurować i rekonfigurować Envoy z lokalizacją innych usług, uzyskując jednocześnie wiele świetnych dodatkowych funkcjonalności, takich jak widoczność. Zamiast tworzyć niestandardową bibliotekę klienta lub wstrzykiwać ślady wywołań do kodu, wysyłamy ruch do Envoy, który zbiera za nas metryki.

Ale Envoy może również pracować jako płaszczyzna danych (płaszczyzna danych) dla siatki usług. Oznacza to, że Envoy jest teraz skonfigurowany dla tej siatki usług sterowanie samolotem (sterowanie samolotem).

Sterowanie samolotem

W przypadku płaszczyzny kontrolnej Istio opiera się na interfejsie API Kubernetes. Nie różni się to zbytnio od używania confd, który opiera się na etcd lub Consul w celu wyświetlenia zestawu kluczy w magazynie danych. Istio używa interfejsu API Kubernetes do przeglądania zestawu zasobów Kubernetes.

Między tym a potem: Osobiście uznałem to za przydatne Opis API Kubernetesaktóry brzmi:

Serwer Kubernetes API to „głupi serwer”, który oferuje przechowywanie, wersjonowanie, sprawdzanie poprawności, aktualizację i semantykę zasobów API.

Istio zostało zaprojektowane do współpracy z Kubernetesem; a jeśli chcesz używać go poza Kubernetesem, musisz uruchomić instancję serwera Kubernetes API (i usługę pomocniczą etcd).

Adresy serwisowe

Istio opiera się na adresach ClusterIP przydzielanych przez Kubernetes, więc usługi Istio otrzymują adres wewnętrzny (nie z zakresu 127.0.0.0/8).

Ruch do adresu ClusterIP dla określonej usługi w klastrze Kubernetes bez Istio jest przechwytywany przez kube-proxy i wysyłany do zaplecza tego serwera proxy. Jeśli interesują Cię szczegóły techniczne, kube-proxy konfiguruje reguły iptables (lub moduły równoważenia obciążenia IPVS, w zależności od konfiguracji), aby przepisać docelowe adresy IP połączeń prowadzących do adresu ClusterIP.

Po zainstalowaniu Istio w klastrze Kubernetes nic się nie zmienia, dopóki nie zostanie jawnie włączone dla danego konsumenta lub nawet całej przestrzeni nazw poprzez wprowadzenie kontenera sidecar w niestandardowe kapsuły. Kontener ten uruchomi instancję Envoy i skonfiguruje zestaw reguł iptables do przechwytywania ruchu kierowanego do innych usług i przekierowywania go do Envoy.

Po integracji z Kubernetes DNS oznacza to, że nasz kod może łączyć się po nazwie usługi i wszystko „po prostu działa”. Innymi słowy, nasz kod generuje zapytania typu http://api/v1/users/4242wtedy api rozwiązać prośbę o 10.97.105.48, reguły iptables będą przechwytywać połączenia z adresu 10.97.105.48 i przekazywać je do lokalnego serwera proxy Envoy, który ten lokalny serwer proxy przekaże żądanie do rzeczywistego interfejsu API zaplecza. Uff!

Dodatkowe fanaberie

Istio zapewnia również kompleksowe szyfrowanie i uwierzytelnianie za pośrednictwem mTLS (wzajemny TLS). Komponent tzw Cytadela.

Jest też komponent Mikser, o które Poseł może poprosić każdy poproś o podjęcie specjalnej decyzji dotyczącej tego żądania w zależności od różnych czynników, takich jak nagłówki, obciążenie backendu itp. (nie martw się: istnieje wiele sposobów na utrzymanie działania Mixera i nawet jeśli ulegnie awarii, Envoy będzie nadal działać dobrze jako proxy).

I oczywiście wspomnieliśmy o widoczności: Envoy gromadzi ogromną ilość danych, zapewniając rozproszone śledzenie. W architekturze mikrousług, jeśli pojedyncze żądanie API musi przejść przez mikrousługi A, B, C i D, to po zalogowaniu funkcja śledzenia rozproszonego doda unikalny identyfikator do żądania i zapisze ten identyfikator w podżądaniach do wszystkich tych mikrousług, umożliwiając przechwytywanie wszystkich powiązanych połączeń, opóźnień itp.

Rozwijaj lub kupuj

Istio ma reputację osoby złożonej. Natomiast zbudowanie siatki routingu, którą opisałem na początku tego wpisu, jest stosunkowo proste przy użyciu istniejących narzędzi. Czy zatem ma sens tworzenie własnej siatki usług?

Jeśli mamy skromne potrzeby (nie potrzebujemy widoczności, wyłącznika i innych subtelności), to przychodzą nam do głowy myśli o stworzeniu własnego narzędzia. Ale jeśli skorzystamy z Kubernetesa, może to nawet nie być potrzebne, ponieważ Kubernetes zapewnia już podstawowe narzędzia do wykrywania usług i równoważenia obciążenia.

Jeśli jednak mamy zaawansowane wymagania, to „zakup” usługi mesh wydaje się dużo lepszym rozwiązaniem. (Nie zawsze jest to „kup”, ponieważ Istio jest oprogramowaniem typu open source, ale nadal musimy zainwestować czas inżynieryjny, aby go zrozumieć, wdrożyć i zarządzać nim.)

Czy powinienem wybrać Istio, Linkerd czy Consul Connect?

Do tej pory mówiliśmy tylko o Istio, ale nie jest to jedyna siatka usług. Popularna alternatywa - Linkerda, a jest tego więcej Konsul Connect.

Co wybrać?

Szczerze mówiąc, nie wiem. W tej chwili nie uważam się za wystarczająco kompetentnego, aby odpowiedzieć na to pytanie. Jest kilka ciekawy artykuły z porównaniem tych narzędzi, a nawet punkty odniesienia.

Obiecującym podejściem jest użycie narzędzia takiego jak SuperGloo. Implementuje warstwę abstrakcji, aby uprościć i ujednolicić interfejsy API udostępniane przez siatki usług. Zamiast uczyć się specyficznych (i moim zdaniem stosunkowo skomplikowanych) interfejsów API różnych siatek usług, możemy używać prostszych konstrukcji SuperGloo - i łatwo przełączać się między nimi, tak jakbyśmy mieli pośredni format konfiguracji opisujący interfejsy HTTP i backendy obsługujące wygenerowania aktualnej konfiguracji dla Nginx, HAProxy, Traefik, Apache...

Zajmowałem się trochę Istio i SuperGloo, a w następnym artykule chcę pokazać, jak dodać Istio lub Linkerd do istniejącego klastra za pomocą SuperGloo i jak ten ostatni wykonuje to zadanie, to znaczy pozwala na przełączanie się z jednej siatki usług na drugą bez nadpisywania konfiguracji.

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

Dodaj komentarz