Envoyé. 1. Introduction

Salutations! Il s'agit d'un court article qui répond aux questions : « qu'est-ce qu'un envoyé ? », « pourquoi est-il nécessaire ? et "par où commencer ?".

Qu'est ce que c'est

Envoy est un équilibreur L4-L7 écrit en C++, axé sur les hautes performances et la disponibilité. D'une part, il s'agit en quelque sorte d'un analogue de nginx et haproxy, dont les performances sont comparables. D'un autre côté, il est plus orienté vers l'architecture des microservices et n'a pas de fonctionnalités pires que les équilibreurs Java et Go, comme zuul ou traefik.

Tableau de comparaison haproxy/nginx/envoy, il ne prétend pas être la vérité absolue, mais donne une image générale.

nginx
happroxy
envoyé
Traefik

étoiles sur github
11.2k/miroir
1.1k/miroir
12.4K
27.6K

écrit en
C
C
C + +
go

API
aucun
prise uniquement/poussoir
plan de données/pull
tirer

bilan de santé actif
aucun
oui
oui
oui

Traçage ouvert
plugin externe
aucun
oui
oui

JWT
plugin externe
aucun
oui
aucun

extension
Lua/C
Lua/C
Lua/C++
aucun

Pourquoi

C'est un projet jeune, il manque beaucoup de choses, certaines en début d'alpha. Mais envoyé, également en raison de sa jeunesse, se développe rapidement et possède déjà de nombreuses fonctionnalités intéressantes : une configuration dynamique, de nombreux filtres prêts à l'emploi, une interface simple pour écrire vos propres filtres.
Les domaines d'application en découlent, mais il y a d'abord 2 anti-modèles :

  • Recul statique.

Le fait est qu'à l'heure actuelle envoyé pas de prise en charge de la mise en cache. Les gars de Google essaient ça réparer. L'idée sera mise en œuvre une fois en envoyé toutes les subtilités (en-têtes de zoo) de la conformité RFC, et pour des implémentations spécifiques, constituent une interface. Mais pour l’instant ce n’est même pas l’alpha, l’architecture est en discussion, PR ouvert (pendant que j'écrivais l'article PR, le PR s'est figé, mais ce point est toujours d'actualité).

Pour l'instant, utilisez nginx pour la statique.

  • Configuration statique.

Vous pouvez l'utiliser, mais envoyé Ce n'est pas pour cela qu'il a été créé. Les fonctionnalités dans une configuration statique ne seront pas exposées. Il y a plusieurs moments :

Lors de l'édition de la configuration dans yaml, vous vous tromperez, gronderez les développeurs pour leur verbosité et penserez que les configurations nginx/haproxy, bien que moins structurées, sont plus concises. C'est le but. La configuration de Nginx et Haproxy a été créée pour être éditée à la main, et envoyé pour la génération à partir du code. L'ensemble de la configuration est décrit dans protobuf, le générer à partir de fichiers proto est beaucoup plus difficile à commettre une erreur.

Les scénarios de déploiement Canary, b/g et bien plus encore ne sont normalement implémentés que dans une configuration dynamique. Je ne dis pas que cela ne peut pas se faire de manière statique, nous le faisons tous. Mais pour cela, vous devez mettre des béquilles, dans n'importe lequel des équilibreurs, dans envoyé y compris

Tâches pour lesquelles Envoy est indispensable :

  • Équilibrage du trafic dans des systèmes complexes et dynamiques. Cela inclut le maillage de services, mais ce n’est pas nécessairement le seul.
  • Le besoin d'une fonctionnalité de traçage distribuée, d'une autorisation complexe ou d'autres fonctionnalités disponibles dans envoyé prêt à l'emploi ou facilement implémenté, mais dans nginx/haproxy, vous devez être entouré de plugins Lua et douteux.

Les deux, si nécessaire, offrent des performances élevées.

Comment ça marche

Envoy est distribué sous forme de binaires uniquement sous forme d'image Docker. L'image contient déjà un exemple de configuration statique. Mais cela ne nous intéresse que pour en comprendre la structure.

configuration statique d'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

Configuration dynamique

À quel problème cherchons-nous une solution ? Vous ne pouvez pas simplement recharger la configuration de l’équilibreur de charge sous charge ; de « petits » problèmes surgiront :

  • Validation des configurations.

La configuration peut être volumineuse, elle peut être très volumineuse, si on la surcharge d'un coup, les risques d'erreur quelque part augmentent.

  • Des connexions durables.

Lors de l'initialisation d'un nouvel écouteur, vous devez prendre soin des connexions exécutées sur l'ancien ; si des changements se produisent fréquemment et qu'il existe des connexions de longue durée, vous devrez rechercher un compromis. Bonjour, entrée Kubernetes sur nginx.

  • Bilans de santé actifs.

Si nous avons des contrôles de santé actifs, nous devons tous les revérifier dans la nouvelle configuration avant d'envoyer du trafic. S’il y a beaucoup d’amont, cela prend du temps. Bonjour haproxy.

Comment cela est-il résolu dans envoyéEn chargeant la config dynamiquement, selon le modèle du pool, vous pouvez la diviser en parties distinctes et ne pas réinitialiser la partie qui n'a pas changé. Par exemple, un écouteur, dont la réinitialisation est coûteuse et qui change rarement.

Configuration envoyé (à partir du fichier ci-dessus) a les entités suivantes :

  • auditeur - écouteur suspendu à une adresse IP/port spécifique
  • hôte virtuel - hôte virtuel par nom de domaine
  • route - règle d'équilibrage
  • grappe — un groupe d'amonts avec des paramètres d'équilibrage
  • point final — adresse de l'instance en amont

Chacune de ces entités ainsi que quelques autres peuvent être renseignées dynamiquement ; pour cela, la configuration précise l'adresse du service d'où la configuration sera reçue. Le service peut être REST ou gRPC, gRPC est préférable.

Les services sont nommés respectivement : LDS, VHDS, RDS, CDS et EDS. Vous pouvez combiner une configuration statique et dynamique, avec la limitation qu'une ressource dynamique ne peut pas être spécifiée dans une ressource statique.

Pour la plupart des tâches, il suffit de mettre en œuvre les trois derniers services, ils sont appelés ADS (Aggregated Discovery Service), par exemple Java et allez-y, il y a une implémentation prête à l'emploi du plan de données gRPC dans laquelle il vous suffit de remplir les objets de votre source.

La configuration prend la forme suivante :

configuration dynamique 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

Lorsque vous exécutez envoyé avec cette configuration, il se connectera au plan de contrôle et tentera de demander la configuration RDS, CDS et EDS. La manière dont se produit le processus d'interaction est décrite ici.

En bref, envoyé envoie une requête indiquant le type de ressource demandée, la version et les paramètres du nœud. En réponse, il reçoit une ressource et une version ; si la version sur le plan de contrôle n'a pas changé, il ne répond pas.
Il existe 4 options d'interaction :

  • Un flux gRPC pour tous les types de ressources, l'état complet de la ressource est envoyé.
  • Flux séparés, état complet.
  • Un flux, état incrémentiel.
  • Flux séparés, état incrémentiel.

xDS incrémentiel vous permet de réduire le trafic entre le plan de contrôle et envoyé, ceci est pertinent pour les grandes configurations. Mais cela complique l'interaction : la demande contient une liste de ressources de désabonnement et d'abonnement.

Notre exemple utilise ADS - un flux pour RDS, CDS, EDS et le mode non incrémental. Pour activer le mode incrémentiel, vous devez spécifier api_type: DELTA_GRPC

Puisque la requête contient des paramètres de nœud, nous pouvons envoyer différentes ressources au plan de contrôle pour différentes instances envoyé, c'est pratique pour créer un maillage de services.

Réchauffer

Sur envoyé au démarrage ou lors de la réception d'une nouvelle configuration du plan de contrôle, le processus de préchauffage des ressources est lancé. Il est divisé en échauffement de l’auditeur et en échauffement du cluster. Le premier est lancé lorsqu'il y a des changements dans RDS/LDS, le second lorsqu'il y a des changements dans CDS/EDS. Cela signifie que si seuls les éléments en amont changent, l'écouteur n'est pas recréé.

Pendant le processus de préchauffage, des ressources dépendantes sont attendues du plan de contrôle pendant le délai d'attente. Si le délai d'attente se produit, l'initialisation échouera et le nouvel écouteur ne commencera pas à écouter sur le port.
Ordre d'initialisation : EDS, CDS, bilan de santé actif, RDS, LDS. Lorsque les contrôles de santé actifs sont activés, le trafic remontera uniquement après un contrôle de santé réussi.

Si l'écouteur a été recréé, l'ancien passe à l'état DRAIN et sera supprimé une fois toutes les connexions fermées ou le délai d'attente expiré. --drain-time-s, par défaut 10 minutes.

A suivre.

Source: habr.com

Ajouter un commentaire