- PVSM.RU - https://www.pvsm.ru -
Приветствую! Это небольшая статья, отвечающая на вопросы: "что такое 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 пытаются это исправить [1]. Идея единожды реализовать в на envoy всё тонкости(зоопарк хедеров) соответствия RFC, а для конкретных реализаций сделать интерфейс. Но пока это даже не альфа, архитектура в обсуждении, PR [2] открыт (пока я писал статью PR вмержили, но этот пункт еще актуален).
А пока используйте для статики nginx.
Можно ее использовать, но envoy был создан не для этого. Возможности в статической конфигурации не будут раскрыты. Моментов много:
Редактируя конфигурацию в yaml, Вы будете ошибаться, материть разработчиков за многословность и думать, что конфиги nginx/haproxy, пусть менее структурированы, но лаконичнее. В этом и суть. Конфигурация Nginx и Haproxy создавалась под редактирование руками, а у envoy под генерацию из кода. Вся конфигурация описана в protobuf [3], генерируя её по proto файлам ошибиться гораздо сложнее.
Сценарии canary, b/g деплоя и много другое, нормально реализуются только в динамической конфигурации. Я не говорю что это нельзя сделать в статике, мы все это делаем. Но для этого нужно обложиться костылями, в любом из балансеров, в envoy в том числе.
Задачи в которых Envoy незаменим:
И то и другое при необходимости обеспечить высокую производительность.
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 (из файла выше) имеет следующие сущности:
Каждую из этих сущностей плюс некоторые другие можно заполнить динамически, для этого в конфигурации указывается адрес сервиса от куда будет получен конфиг. Сервис может быть REST либо gRPC, предпочтительнее использовать gRPC.
Сервисы называются соответственно: LDS, VHDS, RDS, CDS и EDS. Можно комбинировать статическую и динамическую конфигурацию, с ограничением, что динамический ресурс нельзя указать в статическом.
Для большинства задач достаточно реализовать последние три сервиса, они называются ADS (Aggregated Discovery Service), для java [4] и 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. Как происходит процесс взаимодействия описано здесь [5].
Если кратко, envoy шлет запрос, с указанием типа запрашиваемого ресурса, версией и параметрами ноды. В ответ получает ресурс и версию, если на control-plane версия не поменялась, он не отвечает.
Есть 4 варианта взаимодействия:
Incremental xDS позволяет уменьшить трафик между control-plane и envoy, это актуально для больших конфигураций. Но усложняет взаимодействие, в запросе передается список ресурсов для отписки и подписки.
В нашем примере используется ADS — один стрим для RDS, CDS, EDS и не инкрементальный режим. Для включения инкрементального режима, нужно указать api_type: DELTA_GRPC
Так как в запросе есть параметры ноды, мы можем на control-plane присылать разные ресурсы для разных инстансов envoy, это удобно для построения service mesh.
На 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 минут.
Продолжение следует.
Автор: Мирослав Шевалдин
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/nginx/347496
Ссылки в тексте:
[1] исправить: https://docs.google.com/document/d/1WPuim_GzhfdsnIj_tf-fIeutK0jO4aVQfVrLJFoLN3g/view
[2] PR: https://github.com/envoyproxy/envoy/pull/7198
[3] protobuf: https://github.com/envoyproxy/data-plane-api
[4] java: https://github.com/envoyproxy/java-control-plane
[5] здесь: https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol
[6] Источник: https://habr.com/ru/post/482578/?utm_source=habrahabr&utm_medium=rss&utm_campaign=482578
Нажмите здесь для печати.