API gateway против service mesh: где провести границу в реальной архитектуре
«Нам нужен gateway» и «мы переезжаем на service mesh» — две фразы, которые я слышу на проектах с микросервисами с одинаковой регулярностью. И каждый раз приходится разбираться, что именно команда хочет: маршрутизацию, авторизацию, наблюдаемость, отказоустойчивость или всё сразу. Потому что gateway и mesh решают пересекающиеся, но разные задачи.
Полтора года назад на одном из проектов я разбирал слой инфраструктуры, где жили оба: NGINX-based gateway и Istio. Часть функций дублировалась, часть конфликтовала, и команда честно не могла объяснить, кто за что отвечает. Решение пришло после того, как мы вернулись к базовым вопросам: что такое north-south трафик, что такое east-west, и почему их разделение — это не теория, а способ принять решение.
North-south и east-west
Проще всего разделить так. North-south — трафик из внешнего мира в систему: пользователи, мобильные приложения, партнёры по API. East-west — трафик между сервисами внутри системы: orders зовёт payments, payments зовёт notifications.
API gateway традиционно живёт на границе — обрабатывает north-south. Service mesh работает внутри — управляет east-west. На картинке это два разных слоя, и если у вас оба, они логически не пересекаются.
Проблема в том, что обе технологии умеют похожие вещи: оба делают маршрутизацию, оба умеют retry, circuit breaker, mTLS, распределённую трассировку. Разница — в зоне ответственности и в том, как они подключаются.
Что делает gateway
API gateway — это reverse proxy с дополнительной логикой, через который проходит весь внешний трафик. Типичный набор функций:
- Маршрутизация:
/api/orders/*→ orders-service,/api/users/*→ users-service. - Аутентификация: проверка JWT, OAuth2-токенов, API-ключей.
- Rate limiting на пользователя или API-ключ.
- Терминация TLS: внешний HTTPS, внутрь — HTTP или mTLS.
- Трансформация: версионирование URL, склейка/разделение параметров.
- Логирование и метрики на уровне публичного API.
Реализации: Kong, NGINX Ingress, Traefik, AWS API Gateway, Spring Cloud Gateway. Все делают примерно одно — отличаются стеком и моделью конфигурации.
Gateway знает про публичный API. Внутреннее устройство системы — какие сервисы между собой ходят и как — его не касается.
Что делает service mesh
Service mesh — слой инфраструктуры, который перехватывает весь трафик между сервисами через sidecar-прокси (обычно Envoy), стоящий рядом с каждым подом.
Каждый исходящий запрос сервиса A к сервису B сначала уходит в sidecar A, оттуда — в sidecar B, оттуда — в сам сервис B. Сами сервисы не знают, что между ними кто-то стоит. Mesh добавляет:
- mTLS между всеми сервисами автоматически.
- Трассировку: каждый sidecar добавляет/прокидывает trace headers.
- Метрики: latency, request rate, error rate на каждом коннекте без кода в сервисе.
- Управление трафиком: canary, mirroring, fault injection.
- Retry, circuit breaker, timeout — на уровне sidecar.
- Авторизация между сервисами: «orders может звать payments, но не billing».
Реализации: Istio, Linkerd, Consul Connect, AWS App Mesh, Cilium Service Mesh.
Mesh знает про внутреннюю топологию. Он не предназначен быть точкой входа для внешнего трафика — для этого у него есть отдельные ingress-компоненты (Istio Gateway, Linkerd для ingress).
Где они пересекаются
Главное пересечение — авторизация и наблюдаемость. И то и другое умеют оба слоя, но по-разному.
Gateway проверяет JWT клиента и передаёт результат дальше: X-User-Id в заголовке. Сервис верит этому заголовку. Mesh может дополнительно проверять, что вызов идёт от правильного сервиса (mTLS-идентичность), но не знает про конечного пользователя.
Логика разделяется так: identity пользователя — gateway, identity сервиса — mesh. Двойная защита: внешний клиент аутентифицирован, и каждый внутренний хоп подписан mTLS. Если кто-то прорвался через gateway с фейковым JWT, он не сможет подделать mTLS-сертификат сервиса.
Когда хватает только gateway
Gateway без mesh — рабочая конфигурация для:
- Стартапов и небольших систем (до 10–15 сервисов).
- Команд, у которых нет выделенного platform-ops ресурса.
- Случаев, когда mTLS внутри кластера не критичен (например, всё в одной VPC с сетевыми политиками).
- Случаев, когда наблюдаемость закрыта на уровне приложения через OpenTelemetry SDK.
Mesh — тяжёлая инфраструктура. Istio из коробки добавляет к каждому поду sidecar в 100–200 МБ памяти и удваивает количество сетевых хопов. Linkerd легче, но всё равно ресурсы.
Если ваш масштаб — 15 сервисов, 5 разработчиков, и вы не делаете canary-релизы каждый день, mesh будет стоить дороже, чем приносить.
Когда нужен только mesh
Редкая, но возможная конфигурация — система без публичного API. Например, внутренний платформенный продукт, к которому ходят только другие системы внутри компании, через стабильную сеть.
В этом случае gateway избыточен: внешнего трафика нет, аутентификация делается через service-to-service mTLS. Mesh обеспечивает всё остальное: маршрутизацию, наблюдаемость, надёжность.
Признаюсь, я видел такую конфигурацию один раз за двенадцать лет. Чаще всё-таки есть какая-то внешняя поверхность, и для неё нужен gateway.
Когда оба
Большинство зрелых систем приходят к комбинации. Gateway на границе, mesh внутри.
Конкретный сценарий — обработка запроса от пользователя:
- Запрос приходит в gateway. Тот проверяет JWT, делает rate limiting, направляет в нужный сервис.
- Между gateway и orders-service запрос идёт через sidecar mesh (gateway чаще всего тоже в mesh).
- Orders зовёт payments через свой sidecar. Mesh добавляет mTLS, проверяет authorization policy «orders может звать payments».
- Payments возвращает ответ через sidecar обратно.
В этой схеме gateway не знает, как orders ходит к payments, и не должен знать. Mesh не знает, что запрос пришёл от пользователя Васи, и тоже не должен.
Где проводить границу
Когда я разбираю существующую инфраструктуру, я смотрю на дублирование функций.
Retry/circuit breaker. Если оба настроены — выбирайте одно место. Лучше mesh, потому что он ближе к сервису и видит больше контекста. Gateway пусть только маршрутизирует.
Rate limiting. На уровне внешнего пользователя — gateway. На уровне сервис-к-сервису (защита от лавины внутренних вызовов) — mesh. Это разные задачи, и они не дублируются.
mTLS. Если у вас mesh, mTLS внутри — его работа. Gateway терминирует внешний TLS, общается с внутренним сервисом тоже через mTLS, который выписывает mesh.
Аутентификация. Внешняя — gateway. Внутренняя service-to-service — mesh. Никогда не дублируйте: внутренний сервис проверяет JWT, проверяет mTLS-идентичность, и ещё что-то — это нечитаемая мешанина.
Метрики. Mesh даёт метрики «бесплатно» на каждый коннект. Gateway — на каждый external endpoint. Не надо писать те же метрики в коде сервиса вручную.
Подводные камни
Несколько вещей, на которые я наступал.
Конфигурационная сложность. Istio из коробки — это десятки CRD: VirtualService, DestinationRule, AuthorizationPolicy, PeerAuthentication. У команды, которая поставила mesh «на всякий случай», конфигурация быстро превращается в YAML-болото. Решение — стандарты на конфигурацию: что-то типа внутренних шаблонов, через которые сервисы декларируют свои потребности.
Latency overhead. Каждый sidecar добавляет 1–5 мс на коннект. Для большинства сервисов несущественно, для high-throughput внутреннего API может быть критично. Замеряйте до и после, не верьте маркетинговым материалам.
Сетевая отладка. Когда между двумя сервисами стоят два sidecar, gateway и сетевой балансер, найти, где именно теряется запрос — занятие на час. Логи и трассировка должны покрывать каждый хоп.
Вендор-локин. Istio-конфигурация не переезжает на Linkerd за день. Перед выбором mesh оцените, насколько вы готовы к тому, что выбор будет «навсегда». То же про gateway: API Gateway от AWS — это не Kong с другой кнопкой.
«Mesh решит все проблемы». Не решит. Если у вас в коде сервиса retry на каждый внешний вызов, mesh его не уберёт. Если вы шифруете JSON-телом — mesh не расшифрует. Mesh — это инфраструктурный слой, он не заменит правильно написанный сервис.
Альтернативы и эволюция
Не всем нужен sidecar-mesh. Появляются альтернативные модели:
- Sidecar-less mesh. Cilium Service Mesh использует eBPF вместо sidecar. Меньше overhead, но сложнее в установке и привязка к ядру Linux.
- Ambient mesh. Istio Ambient — без sidecar для базовых функций, опционально с sidecar для продвинутых. Снижает накладные расходы.
- Library mesh. gRPC + xDS API даёт часть mesh-функций без отдельного процесса. Подходит для команд, готовых работать с этим в коде.
Если будете запускать mesh с нуля в 2026 — посмотрите Linkerd (самый простой) и Istio Ambient. Sidecar-only Istio уже не дефолтный выбор.
Что запомнить
Gateway — для внешнего периметра, mesh — для внутреннего. Они не взаимоисключают, но решают разные задачи. Если у вас 5 сервисов и публичный API — нужен только gateway. Если 50 сервисов с активной интеграцией — оба, с чёткой границей ответственности.
Главное — не дублировать функции. Каждая задача (rate limiting, mTLS, retry) живёт в одном слое, и каждый слой знает только свою зону. Когда оба знают про всё — это не отказоустойчивость, это путаница, которую через год команда не понимает.