Wysłannik. 1. Wstęp

Cześć! To krótki artykuł, który odpowiada na pytania: „Czym jest envoy?”, „Dlaczego go potrzebuję?” i „Od czego zacząć?”.

Co to jest?

Envoy to moduł równoważenia obciążenia L4-L7 napisany w C++, skoncentrowany na wysokiej wydajności i dostępności. Z jednej strony jest nieco analogiczny do nginx i haproxy, porównywalny pod względem wydajności. Z drugiej strony jest bardziej skoncentrowany na architekturze mikrousług i ma funkcjonalność nie gorszą niż moduły równoważenia obciążenia w Javie i Go, takie jak zuul lub traefik.

Tabela porównawcza haproxy/nginx/envoy nie podaje absolutnej prawdy, ale daje ogólny obraz.

nginx
haproxy
wysłannik
Traefik

gwiazdki na githubie
11.2 tys./lustro
1.1 tys./lustro
12.4k
27.6k

napisane w
C
C
C + +
go

API
nie
tylko gniazdo/wtyk
płaszczyzna danych/pull
Ciągnąć

aktywna kontrola stanu zdrowia
nie
tak
tak
tak

Otwórz śledzenie
wtyczka zewnętrzna
nie
tak
tak

JWT
wtyczka zewnętrzna
nie
tak
nie

rozbudowa
Lua/C
Lua/C
Lua/C++
nie

Po co

To młody projekt, brakuje wielu rzeczy, niektóre są na wczesnym etapie alfa. Ale wysłannik, również z uwagi na swoją młodość, rozwija się bardzo szybko i już teraz posiada wiele ciekawych funkcji: dynamiczną konfigurację, wiele gotowych filtrów, prosty interfejs do pisania własnych filtrów.
Z tego wynikają obszary zastosowań, ale najpierw omówimy 2 antywzorce:

  • Odrzut statyczny.

Rzecz w tym, że w tej chwili w wysłannik brak obsługi buforowania. Ludzie z Google próbują naprawićPomysł jest taki, żeby to wdrażać od czasu do czasu wysłannik wszystkie subtelności (zoo nagłówków) zgodności z RFC i dla konkretnych implementacji, aby utworzyć interfejs. Ale jak dotąd nie jest to nawet alfa, architektura jest w trakcie dyskusji, PR otwarte (podczas gdy pisałem artykuł, PR został scalony, ale ten punkt jest nadal aktualny).

W międzyczasie użyj nginx do statystyk.

  • Konfiguracja statyczna.

Możesz go użyć, ale wysłannik nie został stworzony do tego. Możliwości w konfiguracji statycznej nie zostaną ujawnione. Jest wiele punktów:

Podczas edycji konfiguracji w yaml popełnisz błędy, przeklniesz programistów za rozwlekłość i pomyślisz, że konfiguracje nginx/haproxy, mimo że mniej ustrukturyzowane, są bardziej zwięzłe. O to właśnie chodzi. Konfiguracje Nginx i Haproxy zostały stworzone do ręcznej edycji, a wysłannik do generowania z kodu. Cała konfiguracja jest opisana w protobufgenerowanie go z plików proto znacznie utrudnia popełnienie błędu.

Scenariusze kanarkowe, wdrożenie b/g i wiele innych są zazwyczaj implementowane tylko w konfiguracji dynamicznej. Nie mówię, że nie można tego zrobić w statyce, wszyscy to robimy. Ale w tym celu musisz otoczyć się kulami, w dowolnym z balanserów, w wysłannik włącznie z.

Zadania, w których Envoy jest niezastąpiony:

  • Równoważenie ruchu w złożonych i dynamicznych systemach. Obejmuje to siatkę usług, ale niekoniecznie jest jedyną.
  • Potrzeba rozproszonego śledzenia, złożonej autoryzacji lub innej funkcjonalności dostępnej w wysłannik gotowe do użycia lub wygodnie zaimplementowane, natomiast w przypadku nginx/haproxy trzeba otaczać się lua i wątpliwymi wtyczkami.

Oba rozwiązania zapewniają wysoką wydajność w razie potrzeby.

Jak to działa

Envoy jest dystrybuowany w plikach binarnych wyłącznie jako obraz dokera. Obraz zawiera już przykład konfiguracji statycznej. Ale nas interesuje tylko zrozumienie struktury.

konfiguracja statyczna envoy.yaml

static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address:
        protocol: TCP
        address: 0.0.0.0
        port_value: 10000
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                route:
                  host_rewrite: www.google.com
                  cluster: service_google
          http_filters:
          - name: envoy.router
  clusters:
  - name: service_google
    connect_timeout: 0.25s
    type: LOGICAL_DNS
    # Comment out the following line to test on v6 networks
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: service_google
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: www.google.com
                port_value: 443
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext
        sni: www.google.com

Dynamiczna konfiguracja

Jakiego problemu szukamy? Nie możesz po prostu zrestartować konfiguracji balancera pod obciążeniem, pojawią się „drobne” problemy:

  • Walidacja konfiguracji.

Konfiguracja może być duża, może być bardzo duża, jeśli przeciążymy ją całą naraz, prawdopodobieństwo, że gdzieś wystąpi błąd, wzrasta.

  • Trwałe połączenia.

Podczas inicjowania nowego nasłuchiwacza musisz zadbać o połączenia działające na starym, jeśli zmiany występują często i istnieją długotrwałe połączenia, będziesz musiał znaleźć kompromis. Cześć, kubernetes ingress na nginx.

  • Aktywne kontrole stanu zdrowia.

Jeśli mamy aktywne kontrole kondycji, powinniśmy sprawdzić je wszystkie ponownie w nowej konfiguracji przed wysłaniem ruchu. Jeśli jest wiele upstreamów, to zajmuje trochę czasu. Cześć, haproxy.

Jak to jest rozwiązane w wysłannik, ładując konfigurację dynamicznie, zgodnie z pulą modeli, możesz podzielić ją na oddzielne części i nie inicjalizować ponownie części, która się nie zmieniła. Na przykład, nasłuchiwacz, którego ponowne inicjalizowanie jest kosztowne i zmienia się rzadko.

Konfiguracja wysłannik (z pliku powyżej) zawiera następujące podmioty:

  • słuchacz — słuchacz zawieszony na określonym adresie IP/porcie
  • wirtualny host - wirtualny host według nazwy domeny
  • trasa — zasada równowagi
  • grupa — grupa upstreamów z parametrami równoważącymi
  • Punkt końcowy — adres instancji nadrzędnej

Każdy z tych bytów plus kilka innych może być wypełniany dynamicznie, w tym celu adres usługi, z której zostanie odebrana konfiguracja, jest określony w konfiguracji. Usługa może być REST lub gRPC, preferowane jest użycie gRPC.

Usługi są nazwane odpowiednio: LDS, VHDS, RDS, CDS i EDS. Możliwe jest łączenie konfiguracji statycznej i dynamicznej, z ograniczeniem, że zasób dynamiczny nie może być określony w zasobie statycznym.

Do większości zadań wystarczy wdrożenie trzech ostatnich usług, nazywanych ADS (Aggregated Discovery Service), Jawa and go ma gotową implementację płaszczyzny danych gRPC, w której wystarczy tylko wypełnić obiekty ze źródła.

Konfiguracja wygląda następująco:

dynamiczna konfiguracja envoy.yaml

dynamic_resources:
  ads_config:
    api_type: GRPC
    grpc_services:
      envoy_grpc:
        cluster_name: xds_clr
  cds_config:
    ads: {}
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address:
        protocol: TCP
        address: 0.0.0.0
        port_value: 10000
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
          stat_prefix: ingress_http
          rds:
            route_config_name: local_route
            config_source:
              ads: {}
          http_filters:
          - name: envoy.router
  clusters:
  - name: xds_clr
    connect_timeout: 0.25s
    type: LOGICAL_DNS
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: xds_clr
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: xds
                port_value: 6565

Podczas uruchamiania wysłannik z tą konfiguracją połączy się z płaszczyzną sterowania i spróbuje zażądać konfiguracji RDS, CDS i EDS. Opisano, jak przebiega proces interakcji tutaj.

Krótko mówiąc, wysłannik wysyła żądanie, wskazując typ żądanego zasobu, wersję i parametry węzła. W odpowiedzi otrzymuje zasób i wersję; jeśli wersja na płaszczyźnie sterowania nie uległa zmianie, nie odpowiada.
Istnieją 4 możliwości interakcji:

  • Jeden strumień gRPC dla wszystkich typów zasobów. Wysyłany jest pełny stan zasobu.
  • Oddzielne strumienie, stan kompletny.
  • Jeden strumień, stan przyrostowy.
  • Oddzielne strumienie, stan przyrostowy.

Przyrostowy xDS pozwala na zmniejszenie ruchu pomiędzy płaszczyzną sterowania a wysłannik, jest to istotne dla dużych konfiguracji. Ale komplikuje interakcję, żądanie przesyła listę zasobów do anulowania subskrypcji i subskrypcji.

W naszym przykładzie używamy ADS - jednego strumienia dla RDS, CDS, EDS i trybu nieinkrementalnego. Aby włączyć tryb inkrementalny, należy określić api_type: DELTA_GRPC

Ponieważ żądanie zawiera parametry węzła, możemy wysłać różne zasoby do płaszczyzny sterowania dla różnych instancji. wysłannik, jest to wygodne przy budowaniu siatki usług.

Rozgrzewka

Na wysłannik podczas uruchamiania lub po otrzymaniu nowej konfiguracji z płaszczyzny sterowania uruchamiany jest proces rozgrzewania zasobów. Jest on podzielony na rozgrzewanie słuchacza i rozgrzewanie klastra. Pierwszy uruchamiany jest, gdy zachodzą zmiany w RDS/LDS, drugi, gdy zachodzą zmiany w CDS/EDS. Oznacza to, że jeśli zmieniają się tylko strumienie nadrzędne, słuchacz nie jest tworzony ponownie.

Podczas rozgrzewki, zasoby zależne z płaszczyzny sterowania są oczekiwane podczas limitu czasu. Jeśli limit czasu zostanie osiągnięty, inicjalizacja nie powiedzie się, nowy nasłuchujący nie rozpocznie nasłuchiwania portu.
Kolejność inicjalizacji: EDS, CDS, aktywna kontrola kondycji, RDS, LDS. Przy włączonej aktywnej kontroli kondycji ruch będzie kierowany w górę strumienia dopiero po jednej udanej kontroli kondycji.

Jeśli obiekt nasłuchujący zostanie utworzony ponownie, stary przejdzie w stan DRAIN i zostanie usunięty po zamknięciu wszystkich połączeń lub upłynięciu limitu czasu. --drain-time-s, domyślnie 10 minut.

Aby być kontynuowane.

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

Dodaj komentarz