Envoy. 1. Inleiding

Hallo! Dit is een kort artikel dat antwoord geeft op de vragen: "Wat is envoy?", "Waarvoor dient het?" en "Waar te beginnen?".

Wat is het

Envoy is een L4-L7 load balancer geschreven in C++, gericht op hoge prestaties en beschikbaarheid. Enerzijds is het in zekere zin een alternatief voor nginx en haproxy, qua prestaties vergelijkbaar met hen. Anderzijds is het meer gericht op microservices-architectuur en heeft het functies die niet onderdoen voor load balancers in java en go, zoals zuul of traefik.

Vergelijkingstabel haproxy/nginx/envoy, deze pretendeert niet de absolute waarheid te zijn, maar geeft een algemeen beeld.

met alles wat daarin al aanwezig is, en de inhoud van de map
haproxy
envoy
traefik

sterren op github
11.2k/mirror
1.1k/mirror
12.4k
27.6k

geschreven in
C
C
C++
go

API
nee
socket only/push
dataplane/pull
pull

actieve healthcheck
nee
ja
ja
ja

Open tracing
externe plugin
nee
ja
ja

JWT
externe plugin
nee
ja
nee

Uitbreiding
Lua/C
Lua/C
Lua/C++
nee

Waarom

Dit is een jong project, er is veel dat ontbreekt, sommige dingen zijn in vroege alpha. Maar envoy, mede dankzij de jongheid, ontwikkelt het zich snel en heeft het al veel interessante mogelijkheden: dynamische configuratie, veel kant-en-klare filters, een eenvoudige interface voor het schrijven van je eigen filters.
Hieruit volgen toepassingsgebieden, maar voor nu 2 antipatterns:

  • Statistische afhandeling.

Het punt is dat er op dit moment in de envoy geen ondersteuning voor caching is. De jongens van google proberen dit op te lossen. Het idee is om een keer in op envoy alle nuances (dierenpark van headers) in overeenstemming met RFC te implementeren, en voor specifieke implementaties een interface te maken. Maar momenteel is dit zelfs niet alpha, de architectuur is nog in discussie, PR open (terwijl ik dit artikel schreef, werd de PR samengevoegd, maar dit punt is nog steeds actueel).

Gebruik voor statische bestanden voorlopig nginx.

  • Statische configuratie.

Je kunt het gebruiken, maar envoy is niet daarvoor gemaakt. Mogelijkheden in statische configuratie zullen niet tot hun recht komen. Er zijn veel aspecten:

Als je de configuratie in yaml bewerkt, zul je fouten maken, ontwikkelaars afsnauwen omwille van de bombast en denken dat de configuraties van nginx/haproxy, zij het minder gestructureerd, maar beknopter zijn. Dat is waar het om draait. De configuratie van Nginx en Haproxy is gemaakt voor handmatige bewerking, terwijl de envoy bedoeld is voor generatie vanuit code. Alle configuratie is beschreven in protobuf, het is veel moeilijker om fouten te maken door ze uit proto-bestanden te genereren.

Canary, b/g deployments en veel meer, worden alleen goed gerealiseerd in dynamische configuratie. Ik zeg niet dat dit niet kan in statische configuratie, we doen het allemaal. Maar daarvoor moet je omwegen gebruiken, in elke load balancer, in envoy eveneens.

Taken waarin Envoy onmisbaar is:

  • Verkeersbalancering in complexe en dynamische systemen. Dit omvat service mesh, maar het is niet alleen daarmee beperkt.
  • De noodzaak van functionaliteit voor gedistribueerde tracing, complexe autorisatie of iets anders dat beschikbaar is in envoy de standaardversie of gemakkelijk kan worden geïmplementeerd, terwijl het bij nginx/haproxy nodig is om lua en twijfelachtige plug-ins te gebruiken.

Beide moeten, indien nodig, een hoge prestaties garanderen.

Hoe het werkt

Envoy wordt uitsluitend als docker-image in binaire vorm verspreid. In de afbeelding is er al een voorbeeld van statische configuratie aanwezig. Maar we zijn alleen geïnteresseerd in het om de structuur te begrijpen.

envoy.yaml statische configuratie

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
    # Zet de volgende regel uit om te testen op v6-netwerken
    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

Dynamische configuratie

Welke problemen proberen we op te lossen? Je kunt de configuratie van de balancer niet zomaar herladen onder belasting, dit zal "kleine" problemen veroorzaken:

  • Validatie van configuratie.

De configuratie kan groot zijn, of zelfs heel groot, als we alles in één keer herladen, stijgt de kans dat er ergens een fout is.

  • Langdurige verbindingen.

Bij de initialisatie van een nieuwe listener moet je rekening houden met de verbindingen die actief zijn op de oude. Als wijzigingen frequent plaatsvinden en er langdurige verbindingen zijn, moet er een compromis worden gevonden. Hallo, kubernetes ingress op nginx.

  • Actieve health checks.

Als we actieve health checks hebben, moeten we ze allemaal opnieuw controleren op de nieuwe configuratie voordat we verkeer sturen. Als er veel upstreams zijn, kost dit tijd. Hallo, haproxy.

Hoe wordt dit opgelost in envoy, door de configuratie dynamisch te laden volgens het poolmodel, kan deze in afzonderlijke delen worden verdeeld en hoeft het deel dat niet is veranderd niet opnieuw te worden geïnitialiseerd. Bijvoorbeeld een listener, die kostbaar is om opnieuw te initialiseren, verandert slechts zelden.

Configuratie envoy (uit het bovenstaande bestand) heeft de volgende entiteiten:

  • listener — een listener die aan een bepaald ip/poort hangt
  • virtual host — een virtuele host op basis van de domeinnaam
  • route — balansregel
  • cluster — een groep upstreams met balansparameters
  • om verschillende typen datastromen binnen één apparaat te scheiden. — het adres van de upstream-instantie

Elke van deze entiteiten, plus enkele andere, kan dynamisch worden ingevuld; hiervoor wordt in de configuratie het adres van de service opgegeven waaruit de configuratie zal worden verkregen. De service kan REST of gRPC zijn, waarbij gRPC de voorkeur heeft.

De services worden respectievelijk genoemd: LDS, VHDS, RDS, CDS en EDS. Statische en dynamische configuraties kunnen worden gecombineerd, met de beperking dat een dynamische resource niet in een statische kan worden opgegeven.

Voor de meeste taken is het voldoende om de laatste drie services te implementeren, deze worden ADS (Aggregated Discovery Service) genoemd, voor java en Go heeft een kant-en-klare gRPC dataplane-implementatie waarin het voldoende is om alleen de objecten uit uw bron in te vullen.

De configuratie krijgt de volgende vorm:

envoy.yaml dynamische configuratie

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

Bij het uitvoeren verschijnt het volgende: envoy met deze configuratie, zal het verbinding maken met het control-plane en proberen de configuratie RDS, CDS en EDS aan te vragen. Hoe het interactieproces verloopt, wordt beschreven hier.

Kortom, envoy stuurt een verzoek met de vermelding van het type van het gevraagde resource, de versie en de knooppuntparameters. In ruil daarvoor ontvangt het de resource en de versie; als de versie op het control-plane niet is gewijzigd, antwoordt het niet.
Er zijn 4 interactieopties:

  • Een gRPC-stream voor alle types resources, het volledige status van de resource wordt verzonden.
  • Gescheiden streams, volledige status.
  • Één stream, incrementele status.
  • Gescheiden streams, incrementele status.

Incremental xDS vermindert het verkeer tussen de control-plane en envoy, wat relevant is voor grote configuraties. Maar het compliceert de interactie, in de aanvraag wordt een lijst met bronnen voor afmelding en aanmelding verzonden.

In ons voorbeeld wordt ADS gebruikt — één stream voor RDS, CDS, EDS en niet-incrementele modus. Om de incrementele modus in te schakelen, moet worden opgegeven api_type: DELTA_GRPC

Aangezien de aanvraag knooppuntparameters bevat, kunnen we verschillende bronnen voor verschillende instanties naar de control-plane verzenden envoy, wat handig is voor het bouwen van een service mesh.

Warmup

Op envoy bij de start of bij het ontvangen van een nieuwe configuratie van de control-plane, wordt het warmupproces van de middelen gestart. Dit is verdeeld in listener warmup en cluster warmup. De eerste wordt gestart bij wijzigingen in RDS/LDS, de tweede bij CDS/EDS. Dit betekent dat als alleen upstream wordt gewijzigd, de listener niet opnieuw wordt aangemaakt.

Tijdens het opwarmen worden afhankelijkheidsbronnen van de control-plane binnen een time-out verwacht. Als de time-out is verstreken, zal de initialisatie niet succesvol zijn, en zal de nieuwe listener niet beginnen met luisteren op de poort.
De initialisatievolgorde: EDS, CDS, actieve health check, RDS, LDS. Bij ingeschakelde actieve health checks wordt het verkeer naar de upstream pas doorgegeven na één succesvolle health check.

Als de listener opnieuw werd aangemaakt, gaat de oude naar de staat DRAIN en wordt deze verwijderd nadat alle verbindingen zijn gesloten of de time-out is verstreken. --drain-time-s, standaard 10 minuten.

Wordt vervolgd.

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster