Envoy. 1. Introduzione

Salve! Questo è un breve articolo che risponde a domande come: "che cos'è envoy?", "a cosa serve?" e "da dove iniziare?".

Che cos'è

Envoy è un bilanciatore L4-L7 scritto in C++, progettato per alte prestazioni e disponibilità. Da un lato, è in qualche modo simile a nginx e haproxy, comparabile con loro in termini di prestazioni. Dall'altro, è maggiormente orientato all'architettura a microservizi e offre funzionalità pari a quelle dei bilanciatori in java e go, come zuul o traefik.

Tabella di confronto haproxy/nginx/envoy, non pretende di essere la verità assoluta, ma fornisce una visione 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
socket only/push
dataplane/pull
pull

verifica della salute attiva
no


Open tracing
plugin esterno
no

JWT
plugin esterno
no

no

Estensione
Lua/C
Lua/C
Lua/C++
no

Perché

Questo è un progetto giovane, mancano molte funzionalità e alcune sono ancora in fase alpha. Ma envoy, anche per via della sua giovane età, sta evolvendo rapidamente e già ora ha molte interessanti caratteristiche: configurazione dinamica, numerosi filtri già pronti, e un'interfaccia semplice per scrivere i propri filtri.
Da questo derivano i campi di applicazione, ma per iniziare ci sono due anti-pattern:

  • Consegna di statico.

Il fatto è che attualmente envoy non supporta la cache. I ragazzi di google stanno cercando di rimediare. L'idea è quella di implementare una volta sola tutte le complessità (zoo di intestazioni) di conformità agli RFC, e per le implementazioni specifiche di creare un'interfaccia. Ma per ora, è persino non in alpha, l'architettura è in discussione, envoy è aperto (mentre scrivevo l'articolo, hanno fuso una PR, ma questo punto è ancora attuale). PR Nel frattempo, utilizza nginx per il statico.

Configurazione statica.

  • Può essere usata, ma

non è stata creata per questo. Le capacità nella configurazione statica non verranno svelate. Ci sono molti aspetti: envoy Modificando la configurazione in yaml, commetterai errori, maledirai gli sviluppatori per la verbosità e penserai che le configurazioni di nginx/haproxy, anche se meno strutturate, siano più concise. Questo è il punto. La configurazione di Nginx e Haproxy è stata progettata per essere modificata a mano, mentre

è stato creato per la generazione dal codice. L'intera configurazione è descritta in envoy protobuf , generando attraverso file proto è molto più difficile commettere errori.Scenari canary, b/g deployment e molto altro si realizzano normalmente solo nella configurazione dinamica. Non dico che non si possa fare nella statica, lo facciamo tutti. Ma per questo è necessario ricorrere a vari espedienti, in qualsiasi bilanciatore,

incluso. envoy incluso.

Compiti in cui Envoy è indispensabile:

  • Bilanciamento del traffico in sistemi complessi e dinamici. Qui rientra il service mesh, ma non è necessariamente solo questo.
  • La necessità di funzionalità di tracciamento distribuito, complessa autorizzazione o altro che è presente in envoy out of the box o facilmente realizzabile, mentre in nginx/haproxy è necessario ricorrere a lua e a plugin discutibili.

Entrambi sono necessari per garantire alte prestazioni.

Come funziona

Envoy è distribuito nei binari solo come immagine docker. Nell'immagine c'è già un esempio di configurazione statica. Ma 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
    # Commenta la seguente riga per testare su reti 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.com

Configurazione dinamica

Quale problema stiamo cercando di risolvere? Non è possibile semplicemente ricaricare la configurazione del bilanciatore sotto carico, si verificheranno "piccole" problematiche:

  • Validazione della configurazione.

Il configurazione può essere grande, può essere molto grande, se la carichiamo tutta in una volta, le probabilità che ci sia un errore da qualche parte aumentano.

  • Connessioni di lunga durata.

Durante l'inizializzazione di un nuovo listener, è necessario occuparsi delle connessioni che funzionano sul vecchio, se i cambiamenti avvengono spesso e ci sono connessioni di lunga durata, sarà necessario trovare un compromesso. Ciao, ingress kubernetes su nginx.

  • Health check attivi.

Se abbiamo health check attivi, dovremmo controllarli tutti sulla nuova configurazione prima di inviare il traffico. Se ci sono molti upstream, richiede tempo. Ciao, haproxy.

Come viene risolto in envoy, caricando la configurazione in modo dinamico, attraverso un pool di modelli, è possibile suddividerla in parti separate e non re-inizializzare quella parte che non è cambiata. Ad esempio, il listener, che è costoso da reinizializzare, cambia raramente.

Configurazione envoy (dal file sopra) ha le seguenti entità:

  • listener — listener appeso a un determinato ip/porta
  • virtual host — host virtuale per nome di dominio
  • route — regola di bilanciamento
  • cluster — gruppo di upstream con parametri di bilanciamento
  • endpoint — indirizzo dell'istanza upstream

Ognuna di queste entità, più alcune altre, può essere riempita dinamicamente; a tal fine, nella configurazione viene specificato l'indirizzo del servizio da cui verrà ottenuta la configurazione. Il servizio può essere REST oppure gRPC, è preferibile utilizzare gRPC.

I servizi sono chiamati rispettivamente: LDS, VHDS, RDS, CDS e EDS. È possibile combinare configurazioni statiche e dinamiche, con la limitazione che una risorsa dinamica non può essere indicata in una configurazione statica.

Per la maggior parte dei compiti, è sufficiente implementare gli ultimi tre servizi; sono chiamati ADS (Aggregated Discovery Service), per java e go ha un'implementazione gRPC dataplane pronta in cui è sufficiente riempire solo gli oggetti dalla propria sorgente.

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: 6565

Quando viene avviato envoy con questa configurazione, si connetterà al control-plane e cercherà di richiedere la configurazione RDS, CDS e EDS. Come avviene il processo di interazione è descritto qui.

In breve, envoy invia una richiesta, specificando il tipo di risorsa richiesta, la versione e i parametri della 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.
  • Flussi separati, stato incrementale.

Incremental xDS consente di ridurre il traffico tra il control-plane e envoy, questo è rilevante per grandi configurazioni. Tuttavia, complica l'interazione, viene passato un elenco di risorse per la disiscrizione e l'iscrizione nella richiesta.

Nel nostro esempio viene utilizzato ADS — un flusso 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 istanze diverse envoy, questo è comodo per costruire una rete di servizi.

Warmup

A envoy all'avvio o quando si riceve una nuova configurazione dal control-plane, viene avviato il processo di warmup delle risorse. È diviso in warmup degli listener e warmup dei cluster. Il primo si attiva quando ci sono cambiamenti in RDS/LDS, il secondo in CDS/EDS. Questo significa che se cambiano solo gli upstream, l'ascoltatore non viene ricreato.

Nel processo di riscaldamento, si attendono risorse dipendenti dal control-plane per un timeout. Se il timeout scade, l'inizializzazione non sarà riuscita, il nuovo ascoltatore non inizierà a monitorare la porta.
Ordine di inizializzazione: EDS, CDS, controllo attivo della salute, RDS, LDS. Con i controlli attivi della salute abilitati, il traffico andrà verso l'upstream solo dopo un controllo di salute riuscito.

Se l'ascoltatore è stato ricreato, il precedente passa allo stato DRAIN e sarà rimosso dopo la chiusura di tutte le connessioni o al termine del timeout --drain-time-s, di default 10 minuti.

Continua.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster