L7-балансировщик читает прикладной протокол (HTTP/1.1, HTTP/2, gRPC или WebSocket) и решает, куда направить конкретный запрос: по URL, домену, заголовкам, cookie, весам или содержимому. L4-балансировщик работает с потоком TCP или UDP, видит только IP-адрес и порт, а прикладной слой не разбирает. Отсюда расходятся и возможности, и стоимость решения.
Гибкость L7 оплачивается ресурсами. Балансировщику приходится завершать TLS, парсить заголовки, держать состояние сессий и иногда буферизовать тела запросов. Вычислительная работа на каждый запрос выше, чем при простой пересылке байтов на L4, а точные цифры зависят от профиля трафика, размера запросов и включённых функций. Публичные замеры по этой теме в приведённых источниках не собраны, поэтому перед продакшеном опирайтесь на собственные нагрузочные тесты, а не на общие оценки.
Выбирайте L7, когда за одним входом стоит несколько сервисов, когда нужны gRPC, WebSocket, канареечные релизы или маршрутизация по cookie. Оставайтесь на L4 для чистого TCP-проброса, кластеров баз данных и задач, где приоритет у пропускной способности и минимальной задержки. WebSocket удобен как наглядный пример: он работает поверх TCP и начинается как обычный HTTP-запрос с предложением изменить протокол соединения (механика рукопожатия WebSocket). Значит, L7-балансировщик видит это рукопожатие и может направить его по URL и заголовкам, а L4 видит только поток.
Фрагменты конфигураций ниже даны как справочные шаблоны типовых задач. Синтаксис и доступные директивы зависят от версии продукта: сверяйте параметры с документацией именно вашей версии Nginx, Traefik и Envoy перед применением в продакшене.
Что такое L7-маршрутизация и чем она отличается от L4
L7-маршрутизация смотрит на прикладной протокол и принимает решение по его содержимому: метод, путь, домен, заголовки, cookie, конкретный gRPC-сервис или WebSocket-рукопожатие. L4-маршрутизация работает с транспортным потоком и оперирует парой IP-адрес плюс порт. Разница практическая: L7 разведёт десять сервисов под одним адресом на порту 443, а L4 разведёт их только по отдельным портам или адресам. Разбор уровней с привязкой к модели OSI и конкретным протоколам собран в материале про уровни маршрутизации L2-L7.
Если нужен чистый TCP-проброс без чтения прикладного слоя, смотрите на балансировку TCP/UDP через HAProxy и Nginx Stream. L4 не завершает TLS, не парсит заголовки и не хранит состояние HTTP-сессий, поэтому пропускает больше соединений на том же железе.
Ключевые отличия L4 и L7 в одной таблице
| Критерий | L4 | L7 |
|---|---|---|
| Уровень модели OSI | Транспортный (4) | Прикладной (7) |
| Что видит балансировщик | IP-адрес, порт, TCP/UDP-поток | Метод, URL, Host, заголовки, cookie, HTTP/2-фреймы |
| Типичные решения | HAProxy в режиме TCP, IPVS, Nginx stream | Nginx (http), Traefik, Envoy |
| Правила маршрутизации | По IP и порту | По пути, домену, заголовкам, cookie, весам |
| TLS | Passthrough: шифрование сохраняется | Терминация или re-encryption до бэкенда |
| Health-check | Успешное TCP-подключение | HTTP GET с кодом 200, gRPC Health, анализ кодов ответов |
| Стоимость обработки | Пересылка байтов, минимум вычислений | Разбор протокола, TLS, буферизация, состояние сессий |
| Типичный сценарий | Базы данных, SSH, DNS, произвольный TCP | Ingress микросервисов, API-шлюз, real-time трафик |
Обратите внимание на строку про стоимость: L7 не «медленнее» в абсолюте, он делает больше работы на запрос. На статике и больших файлах разница малозаметна, на тысячах мелких API-вызовов с TLS-терминацией она проявляется сильнее.
Когда L7 оправдан, а когда избыточен
Задачи, где L7 экономит время и упрощает схему:
- Мультисервисный ingress: домен api.example.com и пути /api, /media, /ws ведут на разные бэкенды под общим сертификатом.
- gRPC: протокол требует HTTP/2, а распределять его запросы можно только на прикладном уровне.
- WebSocket: рукопожатие проходит по HTTP, поэтому балансировщик может направить его по пути и заголовкам, а дальше держать долгоживущее соединение.
- Канареечные релизы и A/B: 5 процентов трафика уходит на новую версию, остальные 95 на стабильную.
- Маршрутизация по cookie: сессия привязана к конкретной реплике, а внутренние тестировщики получают новую версию по флагу.
- Единая точка TLS: сертификаты Let's Encrypt и правила редиректа живут в одном месте, а не на десятке бэкендов.
Случаи, где L7 добавляет сложность без пользы:
- Один TCP-сервис без прикладных правил: проксировать его на L7 дороже, чем пробросить на L4.
- Кластеры PostgreSQL, MySQL, Redis: балансировщику нечего разбирать в бинарном протоколе на уровне HTTP.
- Файловые хранилища и медиапотоки, где важна максимальная пропускная способность.
- Сценарии, где по требованиям нельзя завершать TLS на периметре.
Про задержки стоит сказать честно: WebSocket не отменяет задержку маршрутизации, шифрования и обработки данных, а сокращает накладные расходы и ожидание следующего запроса (разбор протокола WebSocket). Хорошо настроенное соединение обычно передаёт небольшое событие за десятки миллисекунд внутри одного региона, тогда как постоянные опросы могут добавлять секунды, создавать лишний трафик и нагружать сервер.
Маршрутизация HTTP по URL, заголовкам и cookie
Три механизма закрывают большинство задач прикладной маршрутизации. Путь и хост разделяют сервисы и версии API. Заголовки дают тонкое управление для тестов и внутренних клиентов. Cookie обеспечивают привязку сессии и канареечные сценарии. Ниже приведены типовые фрагменты для Nginx, Traefik и Envoy; готовые схемы с тюнингом для высоких нагрузок есть в статье про маршрутизацию процессов и health checks.
Маршрутизация по URL и хосту в Nginx, Traefik и Envoy
Nginx сопоставляет запрос с блоками location, а домен проверяет через server_name. Порядок важен: префиксные location выбираются по самому длинному совпадению, регулярные выражения проверяются после префиксных, если не указан модификатор ^~. Шаблон для двух версий API:
server {
listen 443 ssl;
http2 on;
server_name api.example.com;
location /v1/ {
proxy_pass http://backend_v1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /v2/ {
proxy_pass http://backend_v2;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Директива http2 on включает HTTP/2 и ALPN на слушателе. Забытый proxy_set_header Host ломает виртуальные хосты на бэкенде: приложение получает внутреннее имя апстрима вместо публичного домена.
Traefik описывает правила в routers и ссылается на сервисы. Файловый провайдер читает YAML:
http:
routers:
api-v2:
rule: "Host(`api.example.com`) && PathPrefix(`/v2`)"
service: api-v2-service
entryPoints:
- websecure
priority: 100
api-v1:
rule: "Host(`api.example.com`)"
service: api-v1-service
entryPoints:
- websecure
priority: 10
services:
api-v2-service:
loadBalancer:
servers:
- url: "http://10.0.0.12:8080"
api-v1-service:
loadBalancer:
servers:
- url: "http://10.0.0.11:8080"
Приоритет в Traefik по умолчанию вычисляется из длины правила, но его лучше задавать явно. Иначе общее правило Host может перехватить запрос раньше, чем более точное правило с PathPrefix.
Envoy строит маршруты внутри virtual_hosts, где домены заданы полем domains, а путь разбирается в match:
virtual_hosts:
- name: api
domains: ["api.example.com"]
routes:
- match: { prefix: "/v2" }
route:
cluster: api_v2
timeout: 15s
- match: { prefix: "/v1" }
route:
cluster: api_v1
timeout: 15s
- match: { prefix: "/" }
route:
cluster: api_v1
timeout: 15s
Envoy проверяет маршруты сверху вниз и берёт первое совпадение, поэтому специфичные префиксы ставят выше общего. Таймаут по умолчанию для маршрута составляет 15 секунд, и для медленных операций его нужно увеличивать осознанно.
Маршрутизация по заголовкам и cookie
Заголовки удобны для канареечных релизов и внутреннего тестирования: клиент явно помечает свой запрос, и балансировщик отправляет его на новую версию. В Nginx проверку удобно вынести в map:
map $http_x_canary $api_pool {
default api_stable;
"true" api_canary;
}
map $cookie_canary $cookie_pool {
default api_stable;
"1" api_canary;
}
upstream api_stable {
server 10.0.0.11:8080 max_fails=3 fail_timeout=10s;
}
upstream api_canary {
server 10.0.0.21:8080 max_fails=3 fail_timeout=10s;
}
Дальше в location используется переменная в proxy_pass. Учтите: proxy_pass с переменной меняет поведение Nginx, ему нужен resolver, а имя апстрима разбирается во время выполнения. Это плата за гибкую маршрутизацию, и её стоит проверить на стенде.
location /api/ {
proxy_pass http://$api_pool;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
В Traefik то же самое выражается правилом на заголовок и приоритетом выше общего правила:
http:
routers:
api-canary:
rule: "Host(`api.example.com`) && Headers(`X-Canary`, `true`)"
service: api-canary-service
priority: 200
api-cookie:
rule: "Host(`api.example.com`) && HeadersRegexp(`Cookie`, `canary=1`)"
service: api-canary-service
priority: 190
Envoy сопоставляет заголовки внутри match и поддерживает точное совпадение, префикс и регулярные выражения:
routes:
- match:
prefix: "/api"
headers:
- name: "x-canary"
exact_match: "true"
route: { cluster: api_canary }
- match: { prefix: "/api" }
route: { cluster: api_stable }
Две частые ошибки в этом блоке. Первая: балансировщик снимает заголовок X-Forwarded-For или X-Forwarded-Proto, и приложение строит ссылки и определяет схему неверно. Вторая: cookie не доходит до бэкенда, потому что прокси переписывает заголовок Cookie целиком. Пропускайте заголовки явно и проверяйте их на стороне приложения.
Балансировка gRPC на L7
gRPC работает поверх HTTP/2, а HTTP/2 мультиплексирует множество параллельных запросов внутри одного TCP-соединения. Для L4-балансировщика это выглядит как один долгоживущий поток, поэтому все вызовы от клиента уходят на один бэкенд и нагрузка перекашивается. L7-балансировщик видит отдельные HTTP/2-стримы и распределяет их по бэкендам независимо.
Почему gRPC требует HTTP/2 и L7
Клиент открывает одно TCP-соединение и шлёт по нему десятки одновременных вызовов. L4 выбирает бэкенд один раз при установке соединения, а дальше только пересылает байты, поэтому распределение заканчивается на этом шаге. L7 разбирает HTTP/2-фреймы, выделяет стримы и применяет правила маршрутизации к каждому вызову.
Отдельно про WebSocket, чтобы не путать протоколы. WebSocket тоже начинается как HTTP-запрос с предложением изменить протокол, но после подтверждения канал перестаёт быть обычным обменом «запрос-ответ», и обе стороны могут отправлять сообщения в любой момент. gRPC же остаётся RPC поверх HTTP/2 с привычной схемой запрос-ответ по стримам. Набор директив балансировщика для них разный.
Конфигурации gRPC для Nginx, Traefik и Envoy
Nginx проксирует gRPC через директивы grpc_pass и родственные настройки. Для шифрованного бэкенда указывается схема grpcs, для незашифрованного grpc:
server {
listen 443 ssl;
http2 on;
server_name grpc.example.com;
location / {
grpc_pass grpcs://grpc_backend;
grpc_set_header X-Real-IP $remote_addr;
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
grpc_read_timeout 300s;
grpc_send_timeout 300s;
}
}
Если слушатель не поднят с поддержкой HTTP/2, клиент получит ошибку при попытке согласовать h2. Для длительных серверных стримов дефолтные таймауты обрывают соединение, поэтому grpc_read_timeout и grpc_send_timeout увеличивают под профиль сервиса.
Traefik требует явно указать схему подключения к бэкенду. Для незашифрованного gRPC это h2c, для TLS-бэкенда используется https с HTTP/2:
http:
services:
grpc-service:
loadBalancer:
serversTransport: grpc-transport
servers:
- url: "h2c://10.0.0.20:50051"
serversTransports:
grpc-transport:
protocols:
http2: true
Путаница h2c и h2 приводит к ошибке согласования протокола: h2c означает HTTP/2 без TLS, h2 требует шифрованного канала. Если бэкенд слушает незашифрованный порт, оставляйте h2c.
Envoy объявляет HTTP/2 в самом кластере и умеет проверять здоровье gRPC-сервиса:
clusters:
- name: grpc_backend
type: STRICT_DNS
connect_timeout: 1s
http2_protocol_options: {}
load_assignment:
cluster_name: grpc_backend
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: 10.0.0.20, port_value: 50051 }
health_checks:
- timeout: 1s
interval: 5s
unhealthy_threshold: 3
healthy_threshold: 1
grpc_health_check:
service_name: "grpc.health.v1.Health"
Без http2_protocol_options Envoy пойдёт к бэкенду по HTTP/1.1, и gRPC-вызов завершится ошибкой. Health-check через grpc_health_check опрашивает стандартный сервис grpc.health.v1.Health, который приложение должно реализовать.
Проксирование WebSocket через L7-балансировщик
WebSocket работает поверх TCP и начинается как обычный HTTP-запрос с предложением изменить протокол соединения. Если сервер согласен, он отвечает специальным статусом переключения протокола, после чего канал перестаёт быть обычным HTTP-обменом «запрос-ответ», и обе стороны могут отправлять сообщения в любой момент (установка соединения и обмен кадрами). Балансировщик видит только рукопожатие, а дальше держит длительное двустороннее соединение.
Заголовки Upgrade и Connection: что и зачем
Клиент отправляет заголовки Upgrade: websocket и Connection: Upgrade. Сервер подтверждает переход статусом 101 Switching Protocols. Прокси обязан пробросить оба заголовка, иначе браузер получит 400 Bad Request и соединение не поднимется. Nginx по умолчанию приводит Connection к значению close и заголовки не передаёт, поэтому их задают вручную:
location /ws/ {
proxy_pass http://ws_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
Директива proxy_http_version 1.1 обязательна: механизм Upgrade не поддерживается в HTTP/1.0, который Nginx использует по умолчанию при проксировании. Буферизацию отключают, чтобы кадры уходили клиенту сразу.
Envoy описывает апгрейд отдельной конструкцией в маршруте:
routes:
- match: { prefix: "/ws" }
route:
cluster: ws_backend
timeout: 0s
upgrade_configs:
- upgrade_type: websocket
enabled: true
Значение timeout: 0s отключает таймаут маршрута для долгоживущего соединения. Traefik поддерживает WebSocket без дополнительных директив, ему достаточно обычного HTTP-роутера.
Таймауты, keepalive и sticky sessions
Соединение чата или мониторинга живёт часами, а стандартные таймауты прокси рассчитаны на короткие HTTP-запросы. В Nginx по умолчанию proxy_read_timeout равен 60 секундам, и молчащий WebSocket разрывается примерно на этой отметке. Увеличение до 3600 секунд закрывает типовой случай, а период активности соединения дополнительно поддерживают пингами на уровне приложения.
При нескольких репликах бэкенда нужна привязка клиента к конкретному экземпляру. Traefik умеет это через sticky cookie:
http:
services:
ws-service:
loadBalancer:
sticky:
cookie:
name: ws_affinity
httpOnly: true
secure: true
servers:
- url: "http://10.0.0.31:8080"
- url: "http://10.0.0.32:8080"
Без привязки балансировщик может отправить повторное подключение на другую реплику, и состояние сессии потеряется, если оно хранится в памяти процесса. Для деплоя без обрывов нужен механизм drain: под перестаёт принимать новые соединения, существующие закрываются по таймауту, и только потом процесс останавливается. При rolling update чата с 10 тысячами соединений отсутствие drain даёт одномоментный разрыв всех клиентов и шторм переподключений.
TLS-терминация на L7-балансировщике
Завершение TLS на балансировщике собирает управление сертификатами в одной точке. Выпуск через Let's Encrypt, продление и единые наборы шифров настраиваются один раз, а бэкенды общаются с прокси по внутренней сети. Обратная сторона: балансировщик видит расшифрованный трафик, а значит, держит ключи и несёт нагрузку на криптографию.
Терминация, re-encryption или passthrough
Модель passthrough пропускает зашифрованный поток насквозь и не расшифровывает его. L7 при этом не видит ни URL, ни заголовков, ни cookie, поэтому маршрутизация по пути и содержимому становится невозможной: остаётся выбор бэкенда по SNI или по порту. Это ключевой компромисс модели.
Терминация снимает шифрование на периметре и отдаёт трафик внутрь по HTTP. Re-encryption делает то же самое, но восстанавливает TLS до бэкенда, что полезно при требованиях к защите внутреннего периметра. Прокидывайте X-Forwarded-Proto, чтобы приложение знало исходную схему и не строило ссылки на http вместо https.
ALPN и SNI: почему gRPC ломается без них
Клиент и сервер выбирают протокол прикладного уровня через ALPN на этапе TLS-рукопожатия. Для gRPC согласование h2 обязательно: без него клиент получает HTTP/1.1 и вызов завершается ошибкой. В Nginx поддержку включают вместе с HTTP/2 на слушателе:
server {
listen 443 ssl;
http2 on;
server_name grpc.example.com;
ssl_certificate /etc/letsencrypt/live/grpc.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/grpc.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
}
В Envoy список протоколов задаётся прямо в контексте TLS:
downstream_tls_context:
common_tls_context:
alpn_protocols: ["h2", "http/1.1"]
tls_certificates:
- certificate_chain: { filename: "/certs/fullchain.pem" }
private_key: { filename: "/certs/priv.key" }
SNI решает вторую задачу: по имени домена из рукопожатия балансировщик выбирает нужный сертификат. Если SNI не передаётся или не совпадает, клиент получит сертификат другого домена и ошибку проверки. WebSocket по схеме wss работает через тот же слушатель, что и обычный HTTPS, отдельных сертификатов для него не нужно.
Health-check и канареечные релизы
Health-check определяет, попадёт ли запрос на бэкенд, а канареечный релиз ограничивает радиус поражения при плохом обновлении. Обе функции опираются на прикладные данные, которые видит только L7-балансировщик.
Health-check для HTTP, gRPC и WebSocket
Проверки делятся на активные и пассивные. Активный health-check сам опрашивает эндпоинт с заданным интервалом, пассивный анализирует коды ответов реального трафика и выводит бэкенд из ротации после серии ошибок. Для HTTP достаточно ручки liveness и readiness, которая возвращает 200 и проверяет зависимости.
Nginx в открытой версии умеет пассивные проверки через параметры апстрима:
upstream api_backend {
server 10.0.0.11:8080 max_fails=3 fail_timeout=10s;
server 10.0.0.12:8080 max_fails=3 fail_timeout=10s;
keepalive 32;
}
После трёх неудач за fail_timeout сервер исключается из ротации на 10 секунд. Активные проверки с HTTP-запросами доступны в коммерческой версии Nginx Plus и через сторонние модули. В Traefik проверка описывается в сервисе: путь, интервал и таймаут. В Envoy health_checks задаёт интервал, таймаут и пороги unhealthy_threshold и healthy_threshold.
Для gRPC правильный способ проверки - стандартный сервис grpc.health.v1.Health/Check, а не попытка открыть обычный HTTP-порт. Для WebSocket проверять нужно отдельный HTTP-эндпоинт или TCP-подключение, потому что само WebSocket-соединение долгоживущее: балансировщик не может считать разрыв канала за время между проверками признаком падения, иначе живые бэкенды будут постоянно исключаться из ротации.
Канареечный релиз через веса и заголовки
Весовое распределение отправляет фиксированную долю трафика на новую версию. В Nginx это делается через split_clients по хешу от адреса и User-Agent:
split_clients "${remote_addr}${http_user_agent}" $api_pool {
5% api_canary;
* api_stable;
}
В Envoy веса задаются в weighted_clusters внутри маршрута:
route:
weighted_clusters:
clusters:
- name: api_stable
weight: 95
- name: api_canary
weight: 5
В Traefik используется сервис типа weighted со списком сервисов и весами, а для внутреннего тестирования удобнее правило на заголовок X-Canary или cookie canary=1: тестировщики видят новую версию, остальные пользователи остаются на стабильной.
Решение об откате принимают по метрикам: доля ответов 5xx, задержка p99 и частота ошибок gRPC-статусов. Порог вроде роста 5xx выше 1 процента на канареечной группе при стабильных 95 процентах основного трафика даёт достаточный сигнал для автоматического возврата весов.
Типичные ошибки и как их избежать
Список собран по симптомам, которые чаще всего встречаются при настройке L7-маршрутизации.
- 400 Bad Request на WebSocket. Прокси не пробросил заголовки Upgrade и Connection. Исправление: proxy_http_version 1.1 и явная передача обоих заголовков.
- 502 или 504 на gRPC. Бэкенд слушает HTTP/1.1 либо сработал дефолтный таймаут 60 секунд. Исправление: включить HTTP/2 на подключении к апстриму и увеличить grpc_read_timeout и grpc_send_timeout.
- WebSocket рвётся примерно через минуту. Виноват proxy_read_timeout по умолчанию. Исправление: поднять значение до 3600 секунд и добавить пинги на уровне приложения.
- gRPC-клиент получает HTTP/1.1. Не согласован ALPN h2. Исправление: включить HTTP/2 на слушателе и проверить список alpn_protocols на стороне балансировщика.
- Перекос трафика на gRPC. Вместо L7 используется L4-балансировка по TCP. Исправление: перевести сервис на L7-прокси, который видит отдельные HTTP/2-стримы.
- Cookie не доходит до бэкенда. Прокси переписывает заголовок Cookie целиком. Исправление: сохранять исходное значение и явно пропускать нужные cookie.
- Health-check выводит живые бэкенды. Слишком агрессивный интервал и низкий порог. Исправление: увеличить unhealthy_threshold и интервал, проверять отдельный лёгкий эндпоинт.
- Канареечная версия получает 100 процентов трафика. Неверный вес или порядок правил, общее правило перехватывает запрос раньше точного. Исправление: задать явный приоритет и проверить распределение по метрикам.
Как выбрать балансировщик: Nginx, Traefik или Envoy
Выбор сводится к способу управления конфигурацией и требуемой динамике. Расширенное сравнение по производительности и поддержке современных протоколов приведено в отдельном материале про сравнение Nginx, HAProxy и Traefik.
| Критерий | Nginx | Traefik | Envoy |
|---|---|---|---|
| Порог входа | Низкий, много примеров и статей | Низкий в Kubernetes, правила на аннотациях и метках | Выше: конфигурация многословна, нужен опыт |
| Динамическая конфигурация | Перезагрузка файлов, ограниченные динамические API | Нативная реакция на события Docker и Kubernetes | xDS API, обновления без перезапуска |
| gRPC | grpc_pass, требует HTTP/2 на слушателе | Схема h2c или https с HTTP/2 | http2_protocol_options, grpc_health_check |
| WebSocket | Ручная настройка Upgrade, Connection и таймаутов | Работает без дополнительных директив | upgrade_configs в маршруте |
| Канареечные релизы | split_clients или map по cookie | Сервис weighted и правила на заголовках | weighted_clusters |
| TLS-автоматизация | Через внешние инструменты и certbot | Встроенный ACME с Let's Encrypt | Через SDS и внешний менеджер сертификатов |
| Гибкость маршрутизации | Достаточно для классических задач | Хорошо для микросервисов и ingress | Максимальная, включая service mesh |
Практическое правило: для виртуальных машин и простых ingress берите Nginx, для Kubernetes с частыми деплоями и автоматическим TLS удобнее Traefik, для сложной прикладной маршрутизации, тонких весов и service mesh подходит Envoy. Перед переходом в продакшен поднимите стенд с реальным профилем трафика, проверьте рукопожатие WebSocket, gRPC-вызовы и поведение при деплое. Нагрузочный тест на ваших данных даст больше, чем любое сравнение из документации.