Salve! Questo è un breve articolo che risponde a domande come: "che cos'è envoy?", "a cosa serve?" e "da dove cominciare?".
Che cos'è
Envoy è un bilanciatore L4-L7 scritto in C++, progettato per alta performance e disponibilità. Da un lato, è in qualche modo simile a nginx e haproxy, comparabile in termini di performance. Dall'altro, è più orientato verso l'architettura a microservizi e presenta funzionalità non inferiori a quelle di bilanciatori in java e go, come zuul o traefik.
Tabella di confronto tra haproxy/nginx/envoy, che non pretende di essere la verità assoluta, ma fornisce un quadro generale.
nginx
haproxy
envoy
traefik
stelle su github
11.2k/mirror
1.1k/mirror
12.4k
27.6k
scritto in
C
C
C++
go
API
no
solo socket/push
dataplane/pull
pull
healthcheck attivo
no
sì
sì
sì
Open tracing
plugin esterno
no
sì
sì
JWT
plugin esterno
no
sì
no
Espansione
Lua/C
Lua/C
Lua/C++
no
Perché
Questo è un progetto giovane, mancano molte cose e alcune sono in fase alpha. Tuttavia, envoy, grazie anche alla sua giovinezza, si sta sviluppando rapidamente e ha già molte funzionalità interessanti: configurazione dinamica, numerosi filtri pronti all'uso, e un'interfaccia semplice per scrivere i propri filtri.
Da questo derivano le aree di applicazione, ma per iniziare con 2 antipattern:
- Fornire statico.
Infatti, attualmente in envoy non c'è supporto per la cache. I ragazzi di Google stanno cercando di . L'idea è di implementarlo una volta per tutte envoy tutte le complessità (zoo di header) di conformità agli RFC, e per implementazioni specifiche creare un'interfaccia. Ma per ora non siamo nemmeno in alpha, l'architettura è in discussione, è aperta (mentre scrivevo l'articolo, la PR è stata fusa, ma questo punto è ancora attuale).
Nel frattempo, usa nginx per la parte statica.
- Configurazione statica.
Puoi utilizzarla, ma envoy non è stata creata per questo. Le possibilità nella configurazione statica non saranno sfruttate. Ci sono molti aspetti:
Modificando la configurazione in yaml, commetterai errori, maledirai gli sviluppatori per la verbosità e penserai che le configurazioni di nginx/haproxy, sebbene meno strutturate, siano più concise. Questo è il punto. La configurazione di Nginx e Haproxy è stata creata per essere modificata a mano, mentre envoy per la generazione da codice. L'intera configurazione è descritta in , generando il codice dai file proto è molto più difficile sbagliarsi.
Gli scenari canary, b/g deployment e molto altro sono realizzati normalemente solo nella configurazione dinamica. Non dico che non si possa fare in statico, lo facciamo tutti. Ma per questo bisogna fare delle forzature, in qualsiasi bilanciatore, envoy incluso.
Attività in cui Envoy è indispensabile:
- Bilanciamento del traffico in sistemi complessi e dinamici. Qui rientra il service mesh, ma non è necessariamente solo questo.
- Necessità di funzionalità di tracciamento distribuito, autorizzazione complessa o altro che sia già presente in envoy modo nativo o facilmente implementabile, mentre in nginx/haproxy è necessario appoggiarsi a lua e a plugin discutibili.
Entrambi devono garantire alte prestazioni, se necessario.
Come funziona
Envoy è distribuito in pacchetti binari solo come immagine docker. Nell'immagine è già presente un esempio di configurazione statica. Tuttavia, ci interessa solo per comprendere la struttura.
envoy.yaml configurazione statica
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.comConfigurazione dinamica
Quale problema stiamo cercando di risolvere? Non possiamo semplicemente ricaricare la configurazione del bilanciatore sotto carico, sorgerebbero "piccole" problematiche:
- Validazione della configurazione.
La configurazione può essere grande, può essere molto grande; se la sovraccarichiamo, aumentano le probabilità di errori da qualche parte.
- Connessioni a lungo termine.
Durante l'inizializzazione di un nuovo listener, è importante tenere conto delle connessioni attive nel sistema vecchio. Se ci sono frequenti modifiche e ci sono connessioni persistenti, sarà necessario trovare un compromesso. Ciao, kubernetes ingress su nginx.
- Controlli di stato attivi.
Se abbiamo controlli di stato attivi, dovremmo ricontrollarli tutti sulla nuova configurazione prima di inviare il traffico. Se ci sono molti upstream, questo richiede tempo. Ciao, haproxy.
Come viene risolto in envoy, caricando il file di configurazione dinamicamente, secondo il modello pool, possiamo suddividerlo in parti separate e non ri-inizializzare quella parte che non è stata modificata. Ad esempio, un listener che è costoso da ri-inizializzare, ma che cambia raramente.
Configurazione envoy (dal file sopra) ha le seguenti entità:
- listener — listener attaccato a un certo ip/porta
- virtual host — virtual host per nome di dominio
- rotta — regola di bilanciamento
- cluster — gruppo di upstream con parametri di bilanciamento
- endpoint — indirizzo dell'istanza upstream
Ognuna di queste entità, insieme ad alcune altre, può essere popolata dinamicamente; a tal fine, nella configurazione deve essere specificato l'indirizzo del servizio da cui sarà ottenuta la configurazione. Il servizio può essere REST o gRPC, si consiglia di utilizzare gRPC.
I servizi sono denominati rispettivamente: LDS, VHDS, RDS, CDS e EDS. È possibile combinare configurazioni statiche e dinamiche, con la limitazione che una risorsa dinamica non può essere specificata in una configurazione statica.
Per la maggior parte delle attività, è sufficiente implementare gli ultimi tre servizi, noti come ADS (Aggregated Discovery Service), per e go esiste un'implementazione gRPC dataplane pronta in cui è sufficiente riempire solo gli oggetti dalla propria fonte.
La configurazione assume il seguente aspetto:
envoy.yaml configurazione dinamica
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: 6565All'avvio envoy Con questo config, si collegherà al control-plane e cercherà di richiedere la configurazione RDS, CDS e EDS. Il processo di interazione è descritto .
In breve, envoy invia una richiesta, specificando il tipo di risorsa richiesta, la versione e i parametri del nodo. In risposta riceve la risorsa e la versione. Se sul control-plane la versione non è cambiata, non risponde.
Ci sono 4 opzioni di interazione:
- Un flusso gRPC per tutti i tipi di risorse, viene inviato lo stato completo della risorsa.
- Flussi separati, stato completo.
- Un flusso, stato incrementale.
- Stream separati, stato incrementale.
Incremental xDS consente di ridurre il traffico tra il control-plane e envoy, questo è rilevante per configurazioni di grandi dimensioni. Tuttavia, complica l'interazione, in quanto nella richiesta viene passato un elenco di risorse per la disiscrizione e l'iscrizione.
Nel nostro esempio viene utilizzato ADS — uno stream per RDS, CDS, EDS e modalità non incrementale. Per attivare la modalità incrementale, è necessario specificare api_type: DELTA_GRPC
Poiché nella richiesta ci sono parametri del nodo, possiamo inviare risorse diverse al control-plane per diverse istanze envoy, questo è utile per costruire un service mesh.
Warmup
Su envoy all'avvio o quando si riceve una nuova configurazione dal control-plane, viene avviato il processo di warmup delle risorse. È suddiviso in warmup listener e warmup cluster. Il primo viene attivato quando ci sono modifiche in RDS/LDS, il secondo in CDS/EDS. Questo significa che se cambiano solo gli upstream, il listener non viene ricreato.
Durante il processo di riscaldamento, ci si aspetta che risorse dipendenti dal control-plane siano disponibili entro un timeout. Se il timeout scade, l'inizializzazione non sarà riuscita e il nuovo listener non inizierà a monitorare la porta.
Ordine di inizializzazione: EDS, CDS, controllo attivo della salute, RDS, LDS. Con i controlli della salute attivi abilitati, il traffico andrà all'upstream solo dopo un controllo della salute riuscito.
Se il listener è stato ricreato, il vecchio passa allo stato DRAIN e verrà rimosso dopo che tutte le connessioni saranno chiuse o dopo che è scaduto il timeout. --drain-time-s, di default 10 minuti.
Continua.
Fonte: habr.com
