Witam! To krótki artykuł, odpowiadający na pytania: "co to jest envoy?", "po co jest potrzebny?" i "od czego zacząć?".
Co to jest?
Envoy to rozdzielacz L4-L7 napisany w C++, skoncentrowany na wysokiej wydajności i dostępności. Z jednej strony jest to w pewnym sensie odpowiednik nginx i haproxy, porównywalny z nimi pod względem wydajności. Z drugiej strony, jest bardziej dostosowany do architektury mikroserwisów i ma możliwości porównywalne z rozdzielaczami w javie i go, takimi jak zuul czy traefik.
Tabela porównawcza haproxy/nginx/envoy, która nie rości sobie prawa do absolutnej prawdy, ale daje ogólny obraz.
nginx
haproxy
envoy
traefik
gwiazdek na githubie
11.2k/mirror
1.1k/mirror
12.4k
27.6k
napisany w
C
C
C++
go
API
nie
socket only/push
dataplane/pull
pull
aktywny healthcheck
nie
tak
tak
tak
Open tracing
zewnętrzny plugin
nie
tak
tak
JWT
zewnętrzny plugin
nie
tak
nie
Rozszerzenie
Lua/C
Lua/C
Lua/C++
nie
Dlaczego
To młody projekt, brakuje w nim wielu rzeczy, a niektóre są na wczesnym etapie alfa. Ale envoy, w tym także dzięki swojej młodości, szybko się rozwija i już teraz ma wiele interesujących możliwości: dynamiczną konfigurację, wiele gotowych filtrów, prosty interfejs do pisania własnych filtrów.
Z tego wynikają obszary zastosowania, ale na początek 2 antywzorce:
- Serwowanie statycznych treści.
Faktem jest, że w tej chwili w envoy nie ma wsparcia dla caching. Ludzie z google próbują to . Idea polega na jednokrotnym wdrożeniu w na envoy wszystkich niuansów (zoo nagłówków) zgodności z RFC, a dla konkretnych wdrożeń stworzenie interfejsu. Ale na razie to nawet nie jest alfa, architektura jest w dyskusji, otwarty (w momencie pisania artykułu PR został połączony, ale ten punkt wciąż jest aktualny).
A póki co użyj nginx do statycznych treści.
- Statyczna konfiguracja.
Można jej używać, ale envoy został stworzony nie w tym celu. Możliwości w statycznej konfiguracji nie zostaną ujawnione. Jest wiele rzeczy:
Edytując konfigurację w yaml, będziesz popełniać błędy, przeklinać programistów za zawiłość i myśleć, że konfiguracje nginx/haproxy, mniej zorganizowane, ale bardziej zwięzłe. W tym jest sedno. Konfiguracja Nginx i Haproxy została stworzona do ręcznej edycji, a w przypadku envoy do generacji z kodu. Cała konfiguracja jest opisana w , generując ją z plików proto znacznie trudniej pomylić.
Scenariusze canary, podejmowanie decyzji b/g i wiele innych, są dobrze realizowane tylko w dynamicznej konfiguracji. Nie mówię, że nie można tego zrobić w statyce, wszyscy to robimy. Ale do tego trzeba się obłożyć ograniczeniami, w każdym z rozdzielaczy, w envoy tym także.
Zadania, w których Envoy jest niezastąpiony:
- Równoważenie ruchu w złożonych i dynamicznych systemach. Wchodzi w to mesh usług, ale to nie tylko to.
- Konieczność funkcjonalności rozproszonego śledzenia, złożonej autoryzacji lub innych, które są dostępne w envoy standardzie lub łatwo realizowane, a w nginx/haproxy trzeba korzystać z lua i wątpliwych wtyczek.
Obydwa te elementy należy zapewnić, aby osiągnąć wysoką wydajność.
Jak to działa
Envoy jest dystrybuowany w binarkach tylko jako obraz docker. Obraz zawiera już przykład statycznej konfiguracji. Ale interesuje nas tylko dla zrozumienia struktury.
envoy.yaml statyczna konfiguracja
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
# Skomentuj następną linijkę, aby przetestować na sieciach v6
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.comDynamiczna konfiguracja
Jakie problemy rozwiązujemy? Nie można po prostu zrestartować konfiguracji równoważnika obciążenia pod obciążeniem, pojawią się "niewielkie" problemy:
- Weryfikacja konfiguracji.
Konfiguracja może być duża, może być bardzo duża, jeśli przeładowujemy ją całą na raz, szanse, że gdzieś wystąpi błąd, wzrastają.
- Długoterminowe połączenia.
Przy inicjowaniu nowego listenera, trzeba zadbać o połączenia działające na starym, jeśli zmiany zachodzą często i są długoterminowe połączenia, trzeba będzie szukać kompromisu. Witaj, ingress kubernetes na nginx.
- Aktywne health checki.
Jeśli mamy aktywne health checki, warto je wszystkie sprawdzić w nowej konfiguracji przed wysłaniem ruchu. Jeśli jest dużo upstreamów, zajmie to czas. Witaj, haproxy.
Jak to się rozwiązuje w envoy, ładując konfigurację dynamicznie, według modelu pul, można ją podzielić na oddzielne części i nie ponownie inicjalizować tej części, która się nie zmienia. Na przykład listener, którego ponowna inicjalizacja jest kosztowna, zmienia się rzadko.
Konfiguracja envoy (z powyższego pliku) ma następujące encje:
- listener — listener przypisany do określonego IP/portu
- virtual host — wirtualny host po nazwie domeny
- trasa — zasada równoważenia
- cluster — grupa upstreamów z parametrami równoważenia
- endpoint — adres instancji upstreamu
Każdą z tych encji oraz niektóre inne można wypełnić dynamicznie, w tym celu w konfiguracji podaje się adres usługi, z której będzie uzyskana konfiguracja. Usługa może być REST lub gRPC, preferowane jest użycie gRPC.
Usługi nazywają się odpowiednio: LDS, VHDS, RDS, CDS i EDS. Można łączyć statyczną i dynamiczną konfigurację, z ograniczeniem, że zasób dynamiczny nie może być wskazany w statycznym.
Dla większości zadań wystarczy wdrożyć ostatnie trzy usługi, nazywają się ADS (Aggregated Discovery Service), dla i go jest gotowa implementacja gRPC dataplane, w której wystarczy tylko wypełnić obiekty ze swojego źródła.
Konfiguracja nabiera następującego kształtu:
envoy.yaml dynamiczna konfiguracja
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: 6565Po uruchomieniu envoy z tą konfiguracją, połączy się z control-plane i spróbuje zażądać konfiguracji RDS, CDS i EDS. Jak odbywa się proces interakcji opisano .
W skrócie, envoy wysyła żądanie, podając typ żądanego zasobu, wersję i parametry węzła. W odpowiedzi otrzymuje zasób i wersję, jeśli na control-plane wersja się nie zmieniła, nie odpowiada.
Istnieją 4 opcje interakcji:
- Jeden strumień gRPC dla wszystkich typów zasobów, przesyłany jest pełny stan zasobu.
- Oddzielne strumienie, pełny stan.
- Jeden strumień, inkrementalny stan.
- Oddzielne strumienie, inkrementalny stan.
Inkrementalny xDS pozwala na zmniejszenie ruchu pomiędzy control-plane a envoy, co jest istotne dla dużych konfiguracji. Jednakże komplikuje to interakcję, w żądaniu przesyłana jest lista zasobów do odsubskrybowania i subskrybowania.
W naszym przykładzie używamy ADS — jeden strumień dla RDS, CDS, EDS i tryb nieinkrementalny. Aby włączyć tryb inkrementalny, należy określić api_type: DELTA_GRPC
Ponieważ w żądaniu są parametry węzła, możemy na control-plane przesyłać różne zasoby dla różnych instancji envoy, co jest wygodne dla budowy service mesh.
Podgrzewanie
Na envoy przy starcie lub po otrzymaniu nowej konfiguracji z control-plane, uruchamiany jest proces podgrzewania zasobów. Jest on podzielony na podgrzewanie słuchacza i podgrzewanie klastra. Pierwsze uruchamiane jest przy zmianach w RDS/LDS, drugie przy CDS/EDS. Oznacza to, że jeśli zmieniają się tylko upstreamy, słuchacz nie jest ponownie tworzony.
W procesie podgrzewania, oczekiwane są zależne zasoby od control-plane przez czas oczekiwania. Jeśli czas oczekiwania upłynie, inicjalizacja nie będzie udana, nowy słuchacz nie zacznie nasłuchiwać portu.
Kolejność inicjalizacji: EDS, CDS, aktywne kontrole zdrowia, RDS, LDS. Przy włączonych aktywnych kontrolach zdrowia, ruch trafi do upstreamu tylko po jednym udanym teście zdrowia.
Jeśli słuchacz został ponownie utworzony, stary przechodzi w stan DRAIN i zostanie usunięty po zamknięciu wszystkich połączeń lub upływie czasu oczekiwania --drain-time-s, domyślnie 10 minut.
Ciąg dalszy nastąpi.
Źródło: habr.com
