Envoy Proxy 1.6: разбираем xDS API и sidecar-паттерн для observability без правок в коде
Погружаемся в архитектуру Envoy 1.6: как xDS API и sidecar-паттерн дают полную observability между микросервисами без единого изменения в коде приложений.
Envoy Proxy 1.6 - рост популярности как data plane для service mesh на базе xDS API и sidecar-паттерна
Когда мы в феврале разбирались с Istio 0.5, Envoy там был как данность - «sidecar-прокси, который инжектируется в поды». Мы его не трогали, просто пользовались. После нескольких недель работы с Istio возникло ощущение что не понимаем ключевой части механизма: как именно Envoy получает конфигурацию, как он решает куда слать трафик, и почему за этим стоит целый слой абстракции который называется xDS API. Решили разобраться.
Envoy 1.6 вышел в начале апреля. Lyft разработал его изначально для себя, сейчас он под крышей CNCF и используется как data plane в Istio, в Ambassador, и в ряде других проектов. Мы потратили несколько дней на то чтобы понять архитектуру - не как пользователи Istio, а как люди которые хотят понимать что происходит под капотом.
Что такое data plane и причём тут xDS
Service mesh делится на две части: data plane (собственно трафик) и control plane (управление). Envoy - это data plane. Он перехватывает трафик, применяет правила, собирает метрики. Control plane (в случае Istio - Pilot) говорит ему что делать.
Разговаривают они через xDS API - набор gRPC-стримов, где «x» - это разные типы дескрипторов:
- LDS (Listener Discovery Service). Envoy получает описание listeners - на каких портах слушать и что с входящими соединениями делать.
- RDS (Route Discovery Service). Правила маршрутизации: какой URL куда направлять, веса для traffic splitting, retry-политики.
- CDS (Cluster Discovery Service). Описание upstream-кластеров - набор эндпоинтов к которым Envoy будет подключаться.
- EDS (Endpoint Discovery Service). Конкретные IP:port для каждого кластера - обновляется динамически когда меняется состав подов.
Важное свойство xDS: это push-модель через gRPC bidirectional streaming. Pilot не ждёт пока Envoy спросит - он сам толкает обновления при изменении конфигурации. Новый под появился в кластере - EDS-обновление прилетает в Envoy за секунды. Это сильно отличается от poll-based подходов где прокси периодически ходит за конфигом.
Sidecar-паттерн: как это выглядит изнутри пода
В Kubernetes схема такая. В каждый pod кроме контейнера с приложением добавляется контейнер с Envoy. Трафик перехватывается через iptables-правила - init-контейнер при старте пода прописывает правила которые редиректят все входящие и исходящие соединения на Envoy (порт 15001 в случае Istio).
Приложение об этом не знает. Оно открывает соединение на service-b:8080 - ядро перехватывает его и отдаёт Envoy. Envoy смотрит в свою конфигурацию, находит cluster для service-b, выбирает эндпоинт через load balancing, устанавливает реальное соединение. В обратную сторону - входящий трафик тоже идёт через Envoy прежде чем попасть в приложение.
Это даёт кое-что важное: Envoy видит весь трафик в обоих направлениях и может собирать по нему статистику. Без изменений в коде приложения.
Что Envoy даёт по observability
Вот здесь начинается практически полезная часть. Каждый Envoy-sidecar автоматически генерирует:
- Метрики - request rate, error rate, latency percentiles (p50/p95/p99) для каждой пары (источник, назначение). Экспортируются в Prometheus-формате.
- Access logs - структурированные логи каждого запроса: timestamp, source, destination, HTTP-метод, статус, latency, bytes.
- Distributed tracing - Envoy инжектирует и пробрасывает заголовки для Zipkin/Jaeger (b3-заголовки). Приложение должно только пробрасывать эти заголовки дальше при исходящих вызовах - это минимальное требование к коду.
На практике это означает что в кластере с Envoy-sidecar у вас есть service dependency graph с реальным трафиком, а не нарисованный вручную. Istio визуализирует это через Servicegraph, и первый раз когда видишь живой граф с метриками - это производит впечатление. Особенно если в кластере сервисов пятнадцать и раньше про некоторые связи никто особо не задумывался.
Администрирование: что можно посмотреть в runtime
У Envoy есть admin-интерфейс на порту 9901 (по умолчанию). Там можно посмотреть:
/config_dump- полная текущая конфигурация: все listeners, routes, clusters, endpoints. Это отладочный инструмент первого выбора когда что-то идёт не так с маршрутизацией./clusters- состояние каждого upstream-кластера: количество активных соединений, статистика запросов, статус health check./stats- все метрики в текстовом формате. Их несколько тысяч, зато там есть буквально всё.
Когда мы разбирали проблему с неработающим traffic shifting в Istio - /config_dump показал что Pilot не успел раздать обновлённый RouteConfiguration в sidecar проблемного пода. Это была гонка при рестарте пода, не баг в конфигурации. Без admin API это было бы сложно поймать.
Overhead: честная оценка
По нашим наблюдениям на тестовом стенде Envoy потребляет порядка 50 MB памяти per pod при базовой нагрузке, и ощутимо меньше CPU чем мы ожидали при умеренном RPS. Latency-overhead - единицы миллисекунд на hop, на нашей нагрузке это в пределах погрешности. При высоком RPS картина другая - нужно мерять конкретно.
Главный overhead не технический, а операционный. Когда у вас сотня подов, каждый с Envoy, и что-то идёт не так - нужно понимать где искать: в приложении, в Envoy, в Pilot. Это другая модель отладки. kubectl logs показывает только половину картины. К этому надо привыкнуть и надо иметь правильные инструменты рядом.
Где мы сейчас
Для managed-кластеров Envoy выглядит как серьёзная история. Не для каждого клиента и не прямо сейчас, но для крупных инсталляций где десятки сервисов и observability - реальная боль, а не теоретическая проблема, это уже рабочий вариант.
Istio с Envoy под капотом даёт observability-слой который раньше нужно было собирать из нескольких разных инструментов и кода в каждом сервисе. Это реальная ценность. Вопрос в том готова ли операционная команда к дополнительной сложности - потому что Envoy добавляет её честно, без скидок.
Следующий шаг у нас - нагрузочный тест на реальном профиле трафика и проверка поведения при сбое Pilot: что происходит с трафиком если control plane недоступен. Это критичный сценарий для production, и документация по нему пока скупая.