Приветствую! Это небольшая статья, отвечающая на вопросы: "что такое envoy?", "зачем он нужен?" и "с чего начать?". Envoy — это L4-L7 балансировщик написанный на С++, ориентированный на высокую производительность и доступность. С одной стороны, это в некотором роде аналог nginx и haproxy, соизмеримый с ними по производительности. С другой, он больше ориентирован под микросервисную архитектуру и обладает функционалом не хуже балансировщиков на java и go, таких как zuul или traefik. Таблица сравнения haproxy/nginx/envoy, она не претендует на абсолютную истину, но дает общую картину. Это молодой проект, в нем много чего нет, что-то в ранней альфе. Но envoy, в том числе за счет молодости, быстро развивается и уже сейчас имеет много интересных возможностей: динамическую конфигурацию, много готовых фильтров, простой интерфейс для написания своих фильтров.
Из этого вытекают области применения, но для начала 2 антипаттерна: Дело в том, что на данный момент в envoy нет поддержки кэширования. Ребята из google пытаются это исправить. Идея единожды реализовать в на envoy всё тонкости(зоопарк хедеров) соответствия RFC, а для конкретных реализаций сделать интерфейс. Но пока это даже не альфа, архитектура в обсуждении, PR открыт (пока я писал статью PR вмержили, но этот пункт еще актуален). А пока используйте для статики nginx. Можно ее использовать, но envoy был создан не для этого. Возможности в статической конфигурации не будут раскрыты. Моментов много: Редактируя конфигурацию в yaml, Вы будете ошибаться, материть разработчиков за многословность и думать, что конфиги nginx/haproxy, пусть менее структурированы, но лаконичнее. В этом и суть. Конфигурация Nginx и Haproxy создавалась под редактирование руками, а у envoy под генерацию из кода. Вся конфигурация описана в protobuf, генерируя её по proto файлам ошибиться гораздо сложнее. Сценарии canary, b/g деплоя и много другое, нормально реализуются только в динамической конфигурации. Я не говорю что это нельзя сделать в статике, мы все это делаем. Но для этого нужно обложиться костылями, в любом из балансеров, в envoy в том числе. Задачи в которых Envoy незаменим: И то и другое при необходимости обеспечить высокую производительность. Envoy распространяется в бинарниках только как docker образ. В образе уже есть пример статической конфигурации. Но нам он интересен только для понимания структуры. Решение какой проблемы мы ищем? Нельзя просто так взять и перезагрузить конфигурацию балансировщика под нагрузкой, возникнут "небольшие" проблемы: Конфиг может быть большой, может быть очень большой, если мы перегружаем его весь разом, шансы что где-то ошибка возрастают. При инициализации нового листенера, нужно позаботиться о соединениях работающих на старом, если изменения происходят часто и есть долгоживущие соединения, придется искать компромисс. Привет, kubernetes ingress на nginx. Если у нас есть активные хелсчеки, надо бы их все перепроверить на новом конфиге до того как послать трафик. Если апстримов много, это требует время. Привет, haproxy. Как это решается в envoy, подгружая конфиг динамически, по пул модели, можно его поделить на отдельные части и не переинициализировать ту часть которая не менялась. Например листенер, который переинициализировать дорого, а меняется он редко. Конфигурация envoy (из файла выше) имеет следующие сущности: Каждую из этих сущностей плюс некоторые другие можно заполнить динамически, для этого в конфигурации указывается адрес сервиса от куда будет получен конфиг. Сервис может быть REST либо gRPC, предпочтительнее использовать gRPC. Сервисы называются соответственно: LDS, VHDS, RDS, CDS и EDS. Можно комбинировать статическую и динамическую конфигурацию, с ограничением, что динамический ресурс нельзя указать в статическом. Для большинства задач достаточно реализовать последние три сервиса, они называются ADS (Aggregated Discovery Service), для java и go имеется готовая имплементация gRPC dataplane в которой достаточно только заполнить объекты из своего источника. Конфигурация приобретает следующий вид: При запуске envoy с этим конфигом, он подключится к control-plane и попробует запросить конфигурацию RDS, CDS и EDS. Как происходит процесс взаимодействия описано здесь. Если кратко, 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 минут. Продолжение следует.