Enviado. 1. Introducción

¡Saludos! Este es un breve artículo que responde a las preguntas: “¿qué es un enviado?”, “¿por qué es necesario?” y "¿por dónde empezar?".

¿Qué es eso

Envoy es un equilibrador L4-L7 escrito en C++, enfocado en alto rendimiento y disponibilidad. Por un lado, esto es de alguna manera un análogo de nginx y haproxy, comparable en rendimiento a ellos. Por otro lado, está más orientado a la arquitectura de microservicios y tiene una funcionalidad no peor que la de los balanceadores de java y go, como zuul o traefik.

La tabla comparativa de haproxy/nginx/envoy no pretende ser la verdad absoluta, pero ofrece una imagen general.

nginx
haproxy
enviado
traefik

estrellas en github
11.2k/espejo
1.1k/espejo
12.4k
27.6k

escrito en
C
C
C + +
go

API
no
solo enchufe/empuje
plano de datos/tirar
recogida

control de salud activo
no


rastreo abierto
complemento externo
no

JWT
complemento externo
no

no

extensión
Lua/C
Lua/C
Lua/C++
no

¿Por qué

Este es un proyecto joven, faltan muchas cosas, algunas en fase alfa temprana. Pero enviado, también debido a su juventud, se está desarrollando rápidamente y ya tiene muchas características interesantes: configuración dinámica, muchos filtros listos para usar, una interfaz simple para escribir sus propios filtros.
De esto se desprenden áreas de aplicación, pero primero hay 2 antipatrones:

  • Retroceso estático.

El caso es que en este momento en enviado sin soporte de almacenamiento en caché. Los chicos de Google están intentando esto. искреть. La idea se implementará una vez en enviado todas las sutilezas (encabezados del zoológico) del cumplimiento de RFC y, para implementaciones específicas, cree una interfaz. Pero por ahora ni siquiera es alfa, la arquitectura está en discusión. PR abierto (mientras escribía el artículo de relaciones públicas, las relaciones públicas se congelaron, pero este punto sigue siendo relevante).

Por ahora, use nginx para estática.

  • Configuración estática.

Puedes usarlo, pero enviado Para eso no fue creado. Las funciones en una configuración estática no estarán expuestas. Hay muchos momentos:

Al editar la configuración en yaml, se equivocará, regañará a los desarrolladores por su verbosidad y pensará que las configuraciones de nginx/haproxy, aunque menos estructuradas, son más concisas. Ese es el punto. La configuración de Nginx y Haproxy se creó para editarla a mano y enviado para generación a partir de código. Toda la configuración se describe en Protobuf, generarlo a partir de archivos proto es mucho más difícil de cometer un error.

Los escenarios de implementación Canary, b/g y mucho más normalmente se implementan solo en una configuración dinámica. No digo que esto no se pueda hacer de forma estática, lo hacemos todos. Pero para ello es necesario ponerse muletas, en cualquiera de los equilibradores, en enviado incluyendo

Tareas para las que Envoy es indispensable:

  • Equilibrio de tráfico en sistemas complejos y dinámicos. Esto incluye la malla de servicios, pero no necesariamente es la única.
  • La necesidad de una funcionalidad de seguimiento distribuido, una autorización compleja u otra funcionalidad que esté disponible en enviado listo para usar o implementado convenientemente, pero en nginx/haproxy necesita estar rodeado de lua y complementos dudosos.

Ambos, si es necesario, proporcionan un alto rendimiento.

¿Cómo funciona esto

Envoy se distribuye en binarios solo como una imagen acoplable. La imagen ya contiene un ejemplo de una configuración estática. Pero sólo nos interesa para comprender la estructura.

configuración estática de enviado.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

Configuración dinámica

¿A qué problema buscamos solución? No puedes simplemente recargar la configuración del balanceador de carga bajo carga; surgirán "pequeños" problemas:

  • Validación de configuración.

La configuración puede ser grande, puede ser muy grande, si la sobrecargamos toda a la vez, aumentan las posibilidades de que se produzca un error en alguna parte.

  • Conexiones duraderas.

Al inicializar un nuevo oyente, debe cuidar las conexiones que se ejecutan en el antiguo; si se producen cambios con frecuencia y hay conexiones de larga duración, tendrá que buscar un compromiso. Hola, ingreso de kubernetes en nginx.

  • Controles de salud activos.

Si tenemos comprobaciones de estado activas, debemos verificarlas todas en la nueva configuración antes de enviar tráfico. Si hay muchos flujos ascendentes, esto lleva tiempo. Hola haproxy.

¿Cómo se resuelve esto en enviadoAl cargar la configuración dinámicamente, según el modelo del grupo, puede dividirla en partes separadas y no reinicializar la parte que no ha cambiado. Por ejemplo, un oyente, cuyo reinicio es costoso y rara vez cambia.

Configuración enviado (del archivo anterior) tiene las siguientes entidades:

  • oyente — oyente colgado en una IP/puerto específico
  • anfitrión virtual - host virtual por nombre de dominio
  • ruta - regla de equilibrio
  • grupo — un grupo de aguas arriba con parámetros de equilibrio
  • punto final — dirección de instancia ascendente

Cada una de estas entidades más algunas otras se pueden completar dinámicamente; para esto, la configuración especifica la dirección del servicio desde donde se recibirá la configuración. El servicio puede ser REST o gRPC, es preferible gRPC.

Los servicios se denominan respectivamente: LDS, VHDS, RDS, CDS y EDS. Puede combinar configuración estática y dinámica, con la limitación de que no se puede especificar un recurso dinámico en uno estático.

Para la mayoría de las tareas, basta con implementar los últimos tres servicios, se denominan ADS (Aggregated Discovery Service), para Java Y listo, hay una implementación lista para usar del plano de datos gRPC en la que solo necesita completar los objetos de su fuente.

La configuración toma la siguiente forma:

configuración dinámica de enviado.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

Cuando se ejecuta enviado con esta configuración, se conectará al plano de control e intentará solicitar la configuración de RDS, CDS y EDS. Se describe cómo ocurre el proceso de interacción. aquí.

En resumen enviado envía una solicitud indicando el tipo de recurso que se solicita, la versión y los parámetros del nodo. En respuesta, recibe un recurso y una versión; si la versión en el plano de control no ha cambiado, no responde.
Hay 4 opciones de interacción:

  • Una secuencia gRPC para todos los tipos de recursos, se envía el estado completo del recurso.
  • Arroyos separados, pleno estado.
  • Una corriente, estado incremental.
  • Corrientes separadas, estado incremental.

xDS incremental le permite reducir el tráfico entre el plano de control y enviado, esto es relevante para configuraciones grandes. Pero complica la interacción: la solicitud contiene una lista de recursos para darse de baja y suscribirse.

Nuestro ejemplo utiliza ADS: una secuencia para RDS, CDS, EDS y modo no incremental. Para habilitar el modo incremental, debe especificar api_type: DELTA_GRPC

Dado que la solicitud contiene parámetros de nodo, podemos enviar diferentes recursos al plano de control para diferentes instancias. enviado, esto es conveniente para construir una malla de servicios.

Calentar

En enviado Al inicio o al recibir una nueva configuración del plano de control, se inicia el proceso de calentamiento de recursos. Se divide en calentamiento del oyente y calentamiento del grupo. El primero se lanza cuando hay cambios en RDS/LDS, el segundo cuando hay cambios en CDS/EDS. Esto significa que si solo cambian las aguas arriba, el oyente no se vuelve a crear.

Durante el proceso de calentamiento, se esperan recursos dependientes del plano de control durante el tiempo de espera. Si se agota el tiempo de espera, la inicialización no será exitosa y el nuevo oyente no comenzará a escuchar en el puerto.
Orden de inicialización: EDS, CDS, control de salud activo, RDS, LDS. Con las comprobaciones de estado activas habilitadas, el tráfico aumentará solo después de una verificación de estado exitosa.

Si se volvió a crear el oyente, el anterior pasa al estado DRENAR y se eliminará después de que se cierren todas las conexiones o expire el tiempo de espera. --drain-time-s, por defecto 10 minutos.

Esta historia continuará.

Fuente: habr.com

Añadir un comentario