Inviato. 1. Introduzione

Saluti! Questo è un breve articolo che risponde alle domande: “cos’è Envoy?”, “perché è necessario?” e "da dove cominciare?".

Cos'è questo?

Envoy è un bilanciatore L4-L7 scritto in C++, focalizzato su prestazioni elevate e disponibilità. Da un lato, questo è in qualche modo un analogo di nginx e haproxy, paragonabili a loro in termini di prestazioni. D'altra parte, è più orientato all'architettura dei microservizi e ha funzionalità non peggiori dei bilanciatori Java e Go, come zuul o traefik.

Tabella comparativa di haproxy/nginx/envoy, non pretende di essere la verità assoluta, ma fornisce un quadro generale.

nginx
aproxy
inviato
traefik

stelle su github
11.2k/specchio
1.1k/specchio
12.4k
27.6k

scritto in
C
C
C++
go

API
no
solo presa/push
dataplane/tirare
tirare

controllo sanitario attivo
no


Tracciamento aperto
plug-in esterno
no

J.W.T.
plug-in esterno
no

no

estensione
Lua/C
Lua/C
Lua/C++
no

Per cosa?

Questo è un progetto giovane, mancano molte cose, alcune nella prima fase alfa. Ma inviato, anche per la sua giovinezza, si sta sviluppando rapidamente e dispone già di molte funzionalità interessanti: configurazione dinamica, tanti filtri già pronti, un'interfaccia semplice per scrivere i propri filtri.
Da ciò conseguono ambiti di applicazione, ma prima ci sono 2 antipattern:

  • Rinculo statico.

Il fatto è che in questo momento inviato nessun supporto per la memorizzazione nella cache. I ragazzi di Google ci stanno provando исправить. L'idea verrà implementata una volta dentro inviato tutte le sottigliezze (intestazioni zoo) della conformità RFC e per implementazioni specifiche creano un'interfaccia. Ma per ora non è nemmeno alpha, l'architettura è in discussione, PR aperto (mentre stavo scrivendo l'articolo PR, il PR si è bloccato, ma questo punto è ancora rilevante).

Per ora, usa nginx per le statistiche.

  • Configurazione statica.

Puoi usarlo, ma inviato Non è per questo che è stato creato. Le funzionalità in una configurazione statica non verranno esposte. Ci sono molti momenti:

Quando modifichi la configurazione in yaml, ti sbaglierai, rimprovererai gli sviluppatori per la verbosità e penserai che le configurazioni nginx/haproxy, sebbene meno strutturate, siano più concise. Questo è il punto. La configurazione di Nginx e Haproxy è stata creata per la modifica manuale e inviato per la generazione dal codice. L'intera configurazione è descritta in protobuff, generandolo da file proto è molto più difficile commettere un errore.

Gli scenari di distribuzione Canary, b/g e molto altro sono normalmente implementati solo in una configurazione dinamica. Non sto dicendo che questo non si possa fare staticamente, lo facciamo tutti. Ma per questo è necessario indossare le stampelle, in uno qualsiasi dei bilanciatori, dentro inviato compresi.

Compiti per i quali Envoy è indispensabile:

  • Bilanciamento del traffico in sistemi complessi e dinamici. Ciò include la rete di servizi, ma non è necessariamente l'unica.
  • La necessità di funzionalità di tracciamento distribuito, autorizzazione complessa o altre funzionalità disponibili in inviato pronto all'uso o opportunamente implementato, ma in nginx/haproxy devi essere circondato da lua e plugin dubbi.

Entrambi, se necessario, garantiscono prestazioni elevate.

Come funziona

Envoy è distribuito in file binari solo come immagine docker. L'immagine contiene già un esempio di configurazione statica. Ma a noi interessa solo per comprenderne la struttura.

configurazione statica 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

Configurazione dinamica

A quale problema stiamo cercando una soluzione? Non puoi semplicemente ricaricare la configurazione del bilanciatore sotto carico; sorgeranno "piccoli" problemi:

  • Convalida della configurazione.

La configurazione può essere grande, può essere molto grande, se la sovraccarichiamo tutta in una volta, aumentano le possibilità che si verifichi un errore da qualche parte.

  • Connessioni di lunga durata.

Quando inizializzi un nuovo ascoltatore, devi prenderti cura delle connessioni in esecuzione su quello vecchio; se i cambiamenti si verificano frequentemente e ci sono connessioni di lunga durata, dovrai cercare un compromesso. Ciao, ingresso Kubernetes su nginx.

  • Controlli sanitari attivi.

Se abbiamo controlli di integrità attivi, dobbiamo ricontrollarli tutti nella nuova configurazione prima di inviare traffico. Se ci sono molti upstream, questo richiede tempo. Ciao aproxy.

Come si risolve questo problema? inviatoCaricando la configurazione dinamicamente, in base al modello della piscina, è possibile dividerla in parti separate e non reinizializzare la parte che non è cambiata. Ad esempio, un ascoltatore, che è costoso da reinizializzare e cambia raramente.

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

  • ascoltatore — ascoltatore bloccato su un IP/porta specifico
  • host virtuale - host virtuale per nome di dominio
  • route - regola di bilanciamento
  • gruppo — un gruppo di upstream con parametri di bilanciamento
  • endpoint — indirizzo dell'istanza upstream

Ognuna di queste entità più alcune altre possono essere compilate dinamicamente; per questo, la configurazione specifica l'indirizzo del servizio da cui verrà ricevuta la configurazione. Il servizio può essere REST o gRPC, è preferibile gRPC.

I servizi sono denominati rispettivamente: LDS, VHDS, RDS, CDS ed EDS. È possibile combinare la configurazione statica e dinamica, con la limitazione che una risorsa dinamica non può essere specificata in una risorsa statica.

Per la maggior parte dei compiti è sufficiente implementare gli ultimi tre servizi, chiamati ADS (Aggregate Discovery Service), per Giava e vai lì c'è un'implementazione già pronta del dataplane gRPC in cui devi solo compilare gli oggetti dalla tua fonte.

La configurazione assume la seguente forma:

configurazione dinamica 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

Quando si esegue inviato con questa configurazione, si connetterà al piano di controllo e proverà a richiedere la configurazione RDS, CDS ed EDS. Viene descritto come avviene il processo di interazione qui.

In breve, inviato invia una richiesta indicando il tipo di risorsa richiesta, la versione e i parametri del nodo. In risposta riceve una risorsa e una versione; se la versione sul piano di controllo non è cambiata, non risponde.
Sono disponibili 4 opzioni di interazione:

  • Un flusso gRPC per tutti i tipi di risorse, viene inviato lo stato completo della risorsa.
  • Flussi separati, condizioni complete.
  • Un flusso, stato incrementale.
  • Flussi separati, stato incrementale.

xDS incrementale consente di ridurre il traffico tra il piano di controllo e inviato, questo è rilevante per le configurazioni di grandi dimensioni. Ma complica l'interazione; la richiesta contiene un elenco di risorse per annullare l'iscrizione e iscriversi.

Il nostro esempio utilizza ADS: un flusso per RDS, CDS, EDS e modalità non incrementale. Per abilitare la modalità incrementale, è necessario specificare api_type: DELTA_GRPC

Poiché la richiesta contiene parametri del nodo, possiamo inviare risorse diverse al piano di controllo per istanze diverse inviato, questo è utile per creare una rete di servizi.

Riscaldamento

Su inviato all'avvio o quando si riceve una nuova configurazione dal piano di controllo, viene avviato il processo di riscaldamento delle risorse. È diviso in riscaldamento del listener e riscaldamento del cluster. Il primo viene lanciato quando ci sono cambiamenti in RDS/LDS, il secondo quando CDS/EDS. Ciò significa che se cambiano solo gli upstream, il listener non viene ricreato.

Durante il processo di riscaldamento, sono previste risorse dipendenti dal piano di controllo durante il timeout. Se si verifica il timeout, l'inizializzazione non avrà esito positivo e il nuovo ascoltatore non inizierà ad ascoltare sulla porta.
Ordine di inizializzazione: EDS, CDS, controllo sanitario attivo, RDS, LDS. Con i controlli di integrità attivi abilitati, il traffico andrà a monte solo dopo un controllo di integrità riuscito.

Se il listener è stato ricreato, quello vecchio entra nello stato DRAIN e verrà eliminato dopo che tutte le connessioni saranno chiuse o allo scadere del timeout --drain-time-s, predefinito 10 minuti.

Per essere continuato.

Fonte: habr.com

Aggiungi un commento