Envoy. 1. Введение

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Приветствую! Это небольшая статья, отвечающая на вопросы: "что такое envoy?", "зачем он нужен?" и "с чего начать?".


Что это


Envoy — это L4-L7 балансировщик написанный С++, ориентированный на высокую производительность и доступность. С одной стороны, это в некотором роде аналог nginx и haproxy, соизмеримый с ними по производительности. С другой, он больше ориентирован под микросервисную архитектуру и обладает функционалом не хуже балансировщиков на java и go, таких как zuul или traefik.


Таблица сравнения haproxy/nginx/envoy, она не претендует на абсолютную истину, но дает общую картину.


nginx haproxy envoy traefik
звезд на github 11.2k/mirror 1.1k/mirror 12.4k 27.6k
написан на C C C++ go
API нет socket only/push dataplane/pull pull
active healthcheck нет да да да
Open tracing внешний плагин нет да да
JWT внешний плагин нет да нет
Расширение Lua/C Lua/C Lua/C++ нет

Зачем


Это молодой проект, в нем много чего нет, что-то в ранней альфе. Но envoy, в том числе за счет молодости, быстро развивается и уже сейчас имеет много интересных возможностей: динамическую конфигурацию, много готовых фильтров, простой интерфейс для написания своих фильтров.
Из этого вытекают области применения, но для начала 2 антипаттерна:


  • Отдача статики.

Дело в том, что на данный момент в envoy нет поддержки кэширования. Ребята из google пытаются это исправить. Идея единожды реализовать в на envoy всё тонкости(зоопарк хедеров) соответствия RFC, а для конкретных реализаций сделать интерфейс. Но пока это даже не альфа, архитектура в обсуждении, PR открыт (пока я писал статью PR вмержили, но этот пункт еще актуален).


А пока используйте для статики nginx.


  • Статическая конфигурация.

Можно ее использовать, но envoy был создан не для этого. Возможности в статической конфигурации не будут раскрыты. Моментов много:


Редактируя конфигурацию в yaml, Вы будете ошибаться, материть разработчиков за многословность и думать, что конфиги nginx/haproxy, пусть менее структурированы, но лаконичнее. В этом и суть. Конфигурация Nginx и Haproxy создавалась под редактирование руками, а у envoy под генерацию из кода. Вся конфигурация описана в protobuf, генерируя её по proto файлам ошибиться гораздо сложнее.


Сценарии canary, b/g деплоя и много другое, нормально реализуются только в динамической конфигурации. Я не говорю что это нельзя сделать в статике, мы все это делаем. Но для этого нужно обложиться костылями, в любом из балансеров, в envoy в том числе.


Задачи в которых Envoy незаменим:


  • Балансировка трафика в сложных и динамичных системах. Сюда попадает service mesh, но это не обязательно только он.
  • Необходимость функционала распределенной трассировки, сложной авторизации или другого, который есть в envoy из коробки или удобно реализовывается, а в nginx/haproxy нужно обложиться lua и сомнительными плагинами.

И то и другое при необходимости обеспечить высокую производительность.


Как это работает


Envoy распространяется в бинарниках только как docker образ. В образе уже есть пример статической конфигурации. Но нам он интересен только для понимания структуры.


Заголовок спойлера
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

Динамическая конфигурация


Решение какой проблемы мы ищем? Нельзя просто так взять и перезагрузить конфигурацию балансировщика под нагрузкой, возникнут "небольшие" проблемы:


  • Валидация конфигурации.

Конфиг может быть большой, может быть очень большой, если мы перегружаем его весь разом, шансы что где-то ошибка возрастают.


  • Долгоживущие соединения.

При инициализации нового листенера, нужно позаботиться о соединениях работающих на старом, если изменения происходят часто и есть долгоживущие соединения, придется искать компромисс. Привет, kubernetes ingress на nginx.


  • Активные хелсчеки.

Если у нас есть активные хелсчеки, надо бы их все перепроверить на новом конфиге до того как послать трафик. Если апстримов много, это требует время. Привет, haproxy.


Как это решается в envoy, подгружая конфиг динамически, по пул модели, можно его поделить на отдельные части и не переинициализировать ту часть которая не менялась. Например листенер, который переинициализировать дорого, а меняется он редко.


Конфигурация envoy (из файла выше) имеет следующие сущности:


  • listener — листенер висящий на определенном ip/порту
  • virtual host — виртуальный хост по имени домена
  • route — правило балансировки
  • cluster — группа апстримов с параметрами балансировки
  • endpoint — адрес инстанса апстрима

Каждую из этих сущностей плюс некоторые другие можно заполнить динамически, для этого в конфигурации указывается адрес сервиса от куда будет получен конфиг. Сервис может быть REST либо gRPC, предпочтительнее использовать gRPC.


Сервисы называются соответственно: LDS, VHDS, RDS, CDS и EDS. Можно комбинировать статическую и динамическую конфигурацию, с ограничением, что динамический ресурс нельзя указать в статическом.


Для большинства задач достаточно реализовать последние три сервиса, они называются ADS (Aggregated Discovery Service), для java и go имеется готовая имплементация gRPC dataplane в которой достаточно только заполнить объекты из своего источника.


Конфигурация приобретает следующий вид:


Заголовок спойлера
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

При запуске envoy с этим конфигом, он подключится к control-plane и попробует запросить конфигурацию RDS, CDS и EDS. Как происходит процесс взаимодействия описано здесь.


Если кратко, envoy шлет запрос, с указанием типа запрашиваемого ресурса, версией и параметрами ноды. В ответ получает ресурс и версию, если на control-plane версия не поменялась, он не отвечает.
Есть 4 варианта взаимодействия:


  • Один gRPC стрим на все типы ресурсов, присылается полное состояние ресурса.
  • Раздельные стримы, полное состояние.
  • Один стрим, инкрементальное состояние.
  • Раздельные стримы, инкрементальное состояние.

Incremental xDS позволяет уменьшить трафик между control-plane и envoy, это актуально для больших конфигураций. Но усложняет взаимодействие, в запросе передается список ресурсов для отписки и подписки.


В нашем примере используется ADS — один стрим для RDS, CDS, EDS и не инкрементальный режим. Для включения инкрементального режима, нужно указать api_type: DELTA_GRPC


Так как в запросе есть параметры ноды, мы можем на control-plane присылать разные ресурсы для разных инстансов envoy, это удобно для построения service mesh.


Warmup


На envoy при старте или при получении новой конфигурации от control-plane запускается процесс warmup ресурсов. Он разделен, на listener warmup и cluster warmup. Первый запускается при изменениях в RDS/LDS, второй при CDS/EDS. Это значит, что если меняются только апстримы, листенер не пересоздается.


В процессе прогрева, ожидаются зависимые ресурсы от control-plane в течении таймаута. Если таймаут вышел, инициализация не будет успешной, новый листенер не начнет слушать порт.
Порядок инициализации: EDS, CDS, active health check, RDS, LDS. При включенных активных хелсчеках, трафик пойдет на апстрим, только после одного успешного хелсчека.


Если пересоздавался листенер, старый переходит в состояние DRAIN, и будет удален после закрытия всех соединений или истечении таймаута --drain-time-s, по умолчанию 10 минут.


Продолжение следует.

Источник: https://habr.com/ru/post/482578/


Интересные статьи

Интересные статьи

В современном мире сложных высоконагруженных веб приложений, где компоненты одной страницы исчисляются десятками, а изменение состояния одного из них порождает цепочку различных событий п...
Принято считать, что персонализация в интернете это магия, которая создается сотнями серверов на основе БигДата и сложного семантического анализа контента.
Введение В этой статье мы, как вы уже догадались, поговорим про Screen Capture API. Этот API появился на свет в 2014 году и новым его назвать сложно, однако поддержка браузерами все еще достаточ...
Полный курс на русском языке можно найти по этой ссылке. Оригинальный курс на английском доступен по этой ссылке.
Один из самых острых вопросов при разработке на Битрикс - это миграции базы данных. Какие же способы облегчить эту задачу есть на данный момент?