Bună! Acesta este un scurt articol care răspunde la întrebările: "ce este envoy?", "de ce este necesar?" și "de unde să încep?".
Ce este
Envoy este un echilibror de încărcare L4-L7 scris în C++, dedicat performanței ridicate și disponibilității. Pe de o parte, este oarecum similar cu nginx și haproxy, comparabil cu acestea în ceea ce privește performanța. Pe de altă parte, este mai orientat spre arhitectura microserviciilor și dispune de funcționalități cel puțin la fel de bune ca echilibratoarele scrise în java și go, precum zuul sau traefik.
Tabel comparativ haproxy/nginx/envoy, nu pretinde a fi o adevărată autoritate, dar oferă o imagine de ansamblu.
nginx
haproxy
envoy
traefik
stele pe github
11.2k/mirror
1.1k/mirror
12.4k
27.6k
scris în
C
C
C++
go
API
nu
socket only/push
dataplane/pull
pull
verificare activă a sănătății
nu
da
da
da
Open tracing
plugin extern
nu
da
da
JWT
plugin extern
nu
da
nu
Extensie
Lua/C
Lua/C
Lua/C++
nu
De ce
Este un proiect tineresc, îi lipsesc multe lucruri, unele sunt în stadiul de alfa timpurie. Dar envoy, datorită tineretii, se dezvoltă rapid și deja are multe capacități interesante: configurare dinamică, multe filtre gata făcute, o interfață simplă pentru scrierea propriilor filtre.
De aici rezultă domeniile de aplicație, dar pentru început, 2 antipattern-uri:
- Servirea staticelor.
Ceea ce se întâmplă este că în prezent nu există suport pentru caching. envoy Cei de la google încearcă să facă acest lucru . Ideea este de a implementa o dată în envoy toate subtilitățile (zoo de headere) conforme RFC, iar pentru implementările specifice, să creeze o interfață. Dar deocamdată, aceasta este chiar mai puțin decât alfa, arhitectura este în discuție, deschis (între timp, pe când scriam articolul, PR-ul a fost fuzionat, dar acest punct este încă relevant).
Între timp, folosiți nginx pentru statice.
- Configurare statică.
Poate fi utilizată, dar envoy nu a fost creată pentru aceasta. Capacitățile în configurația statică nu vor fi dezvăluite. Sunt multe aspecte:
Editând configurația în yaml, veți face greșeli, veți înjura dezvoltatorii pentru verbose și veți crede că config-urile nginx/haproxy, deși mai puțin structurate, sunt mai concise. Asta este esența. Configurația Nginx și Haproxy a fost creată pentru editare manuală, în timp ce envoy a fost creată pentru generarea din cod. Toată configurația este descrisă în , generându-se prin fișiere proto, este mult mai greu să greșești.
Scenariile canary, de desplatare b/g și multe altele, sunt realizate bine doar în configurația dinamică. Nu spun că nu se poate face în statică, facem toate acestea. Dar pentru asta trebuie să te înfășori în soluții de tip workaround, în oricare dintre echilibratoarele disponibile, în envoy inclusiv.
Sarcinile în care Envoy este indispensabil:
- Îmbinarea traficului în sisteme complexe și dinamice. Aici intră și service mesh, dar nu se limitează doar la acesta.
- Necesitatea funcționalității de urmărire distribuită, autorizare complexă sau altceva care există în envoy modul standard sau este ușor de implementat, în timp ce în nginx/haproxy trebuie să ne bazăm pe lua și pluginuri discutabile.
Ambele sunt necesare pentru a asigura o performanță ridicată.
Cum funcționează
Envoy este distribuit în binare doar ca imagine docker. În imagine există deja un exemplu de configurație statică. Dar noi suntem interesați de asta doar pentru a înțelege structura.
envoy.yaml configurație statică
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
# Comentarii asupra următoarei linii pentru a testa pe rețele 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.comConfigurație dinamică
Ce problemă căutăm să rezolvăm? Nu putem pur și simplu să reluăm configurația echilibrorului de sarcină sub sarcină, vor apărea "probleme mici":
- Validarea configurației.
Configurația poate fi mare, poate fi foarte mare, iar dacă o reîncărcăm pe toată deodată, șansele de a avea o eroare undeva cresc.
- Conexiuni de lungă durată.
La inițierea unui nou listener, trebuie să ne preocupăm de conexiunile care funcționează pe vechiul listener, dacă modificările apar frecvent și sunt conexiuni de lungă durată, va trebui să găsim un compromis. Salut, ingress kubernetes pe nginx.
- Verificări active de sănătate.
Dacă avem verificări active de sănătate, ar trebui să le verificăm toate pe noua configurație înainte de a trimite traficul. Dacă sunt multe upstream-uri, aceasta necesită timp. Salut, haproxy.
Cum se rezolvă aceasta în envoy, încărcând dinamic configurația, pe baza modelului de pool, aceasta poate fi împărțită în părți separate și nu este necesară reinițializarea acelei părți care nu s-a schimbat. De exemplu, un listener, care are costuri de reinițializare mari, se schimbă rar.
Configurație envoy (din fișierul de mai sus) are următoarele entități:
- listener — un listener care ascultă pe un anumit ip/port
- virtual host — un host virtual pe baza numelui de domeniu
- route — regulă de balansare
- cluster — grup de upstream-uri cu parametrii de balansare
- endpoint — adresa instanței upstream-ului
Fiecare dintre aceste entități, plus unele altele, pot fi completate dinamic; pentru aceasta, în configurație se specifică adresa serviciului de la care va fi obținută configurația. Serviciul poate fi REST sau gRPC, preferabil este să se utilizeze gRPC.
Serviciile se numesc corespunzător: LDS, VHDS, RDS, CDS și EDS. Se poate combina configurația statică și dinamică, cu restricția că resursa dinamică nu poate fi specificată în static.
Pentru majoritatea sarcinilor, este suficient să se implementeze ultimele trei servicii, acestea se numesc ADS (Aggregated Discovery Service), pentru și go există o implementare pregătită gRPC dataplane în care este suficient să completați doar obiectele din sursa proprie.
Configurația devine următoarea:
envoy.yaml configurație dinamică
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: 6565La pornire envoy cu această configurație, se va conecta la control-plane și va încerca să solicite configurația RDS, CDS și EDS. Cum se desfășoară procesul de interacțiune este descris .
Pe scurt, envoy trimite o cerere, indicând tipul de resursă solicitat, versiunea și parametrii nodului. În răspuns, primește resursa și versiunea; dacă versiunea pe control-plane nu s-a schimbat, nu răspunde.
Există 4 variante de interacțiune:
- Un singur stream gRPC pentru toate tipurile de resurse, se trimite starea completă a resursei.
- Fluxuri separate, stare completă.
- Flux unic, stare incrementală.
- Fluxuri separate, stare incrementală.
xDS incremental permite reducerea traficului între control-plane și envoy, acest lucru fiind relevant pentru configurări mari. Însă complica interacțiunea, transmitând în cerere o listă de resurse pentru dezabonare și abonare.
În exemplul nostru, utilizăm ADS — un flux pentru RDS, CDS, EDS și modul non-incremental. Pentru a activa modul incremental, trebuie specificat api_type: DELTA_GRPC
Deoarece cererea conține parametrii nodurilor, putem trimite resurse diferite pentru diferite instanțe la control-plane envoy, ceea ce este convenabil pentru construirea unei rețele de servicii.
Încălzire
Pe envoy la pornire sau atunci când se primește o nouă configurație de la control-plane, se pornește procesul de încălzire a resurselor. Acesta este împărțit în încălzirea listener-ului și încălzirea cluster-ului. Prima se activează la schimbări în RDS/LDS, a doua în CDS/EDS. Aceasta înseamnă că, dacă se schimbă doar upstream-urile, listener-ul nu este recreat.
În timpul procesului de încălzire, se așteaptă resursele dependente de la control-plane timp de un timeout. Dacă timeout-ul expiră, inițializarea nu va fi reușită, noul listener nu va începe să asculte portul.
Ordinea inițializării: EDS, CDS, health check activ, RDS, LDS. Cu health check-urile active activate, traficul va merge la upstream, doar după un health check reușit.
Dacă listener-ul a fost recreat, cel vechi trece în stare DRAIN și va fi eliminat după ce toate conexiunile sunt închise sau după expirarea timeout-ului --drain-time-s, implicit 10 minute.
Continuarea urmează.
Sursa: habr.com
