Service Mesh

Istio, Envoy и управление трафиком в микросервисах

15 апреля 2024

Service mesh - не «ещё один компонент Kubernetes», а ответ на вполне конкретный класс проблем, который возникает у компании, дошедшей до десятков-сотен микросервисов на нескольких языках. Когда retry-логика, mTLS, circuit breaker и трассировка нужны каждому сервису одинаково, попытка реализовать их в коде каждого сервиса воспроизводится N раз, дрейфует между языками и версиями, и любое изменение политики безопасности превращается в координированный редеплой всего парка. Service mesh выносит эту повторяющуюся инфраструктурную работу из приложения в sidecar-прокси и в отдельный control plane. Статья разбирает, откуда исторически выросла эта идея (Twitter Finagle, Linkerd, Envoy в Lyft), почему именно Envoy стал стандартом, как устроена связка control plane и data plane на примере Istio, какие реальные компании и как именно это используют, во что обходится sidecar при тысячах подов и какие антипаттерны системно ломают adoption даже в технически зрелых командах.

Когда распределённость требует инфраструктурного уровня

В монолите межсервисное взаимодействие - это вызов функции: ошибки локальны, latency предсказуема, безопасность - вопрос процесса, а не сети. В микросервисах любое такое взаимодействие превращается в сетевой запрос со всеми сопутствующими свойствами распределённой системы: запрос может потеряться, замедлиться, прийти не туда, прийти дважды, быть прочитан злоумышленником. Каждое из этих свойств требует явной обработки, и эта обработка повторяется для каждого исходящего вызова в каждом сервисе.

Минимальный набор инфраструктурных задач, которые возникают на каждом межсервисном hop:

Все эти задачи можно решить в коде каждого сервиса. Многие компании именно так и начинали: библиотека retries на стороне клиента, отдельная обвязка для mTLS, OpenTelemetry SDK в каждом репозитории. Проблема не в том, что это технически невозможно, а в том, что издержки этой работы растут квадратично с ростом организации:

Service mesh выносит эту инфраструктурную работу из кода сервисов в отдельный слой - sidecar-прокси, который перехватывает весь сетевой трафик пода. Код приложения перестаёт знать про retries, mTLS, circuit breaker и canary; он отправляет HTTP-запрос на localhost, а прокси сам решает, как и куда его доставить, с какой политикой ретраев, через какой шифрованный туннель.

История возникновения: проблема с каждым сервисом

Идея, что сетевая логика должна жить отдельно от прикладной, появилась задолго до термина «service mesh». Самым прямым предком считается Twitter Finagle - Scala-библиотека, которую Twitter начал разрабатывать в 2010 году и открыл в open source в 2011. Finagle давала единый programming model для retries, timeouts, load balancing, circuit breaker, distributed tracing - всё это в виде композиции стека фильтров над сетевым вызовом. Внутри Twitter Finagle стал стандартом межсервисного общения; внешние компании пытались её адаптировать, но упирались в одно ограничение - JVM-only. Любая команда, которая хотела писать сервис на Go, Python или C++, оказывалась за пределами этой инфраструктуры и должна была воспроизводить те же гарантии своими силами.

Параллельно ту же боль решали в других компаниях. Netflix выпустил Hystrix (circuit breaker) и Ribbon (client-side load balancing) - тоже JVM-библиотеки, тоже de facto стандарт внутри Netflix. Linkedin строил Rest.li. Twitter, Netflix, LinkedIn - все три случая показывают одно и то же: достаточно крупная компания с большим количеством JVM-сервисов сама себе строит «сетевую инфраструктуру в библиотеке». Но как только этот опыт пытались перенести в компании с polyglot-стэком, библиотечная модель ломалась.

Lyft в 2014-2015 годах столкнулся с той же проблемой в острой форме: их сервисы писались на Python, PHP, Go и Node.js одновременно, и идея написать «Hystrix для каждого языка» была организационно нереалистичной. Matt Klein, инженер инфраструктуры Lyft (ранее AWS, Twitter), предложил радикально другой подход - вынести сетевую логику в отдельный процесс-прокси, который запускается рядом с каждым сервисом и перехватывает весь его трафик. Сервис общается с прокси по localhost, прокси умеет HTTP/1.1, HTTP/2, gRPC, TCP, делает retries, балансировку, трассировку, mTLS - и эта функциональность одинакова для всех языков, потому что языки в неё больше не вовлечены. Это был Envoy. Klein описал концепцию в эссе «The Universal Data Plane API» (blog.envoyproxy.io), и в 2016 году Envoy ушёл в open source. В 2017 проект был передан в CNCF.

Отдельной веткой развивалась Buoyant - стартап, основанный бывшими инженерами Twitter, многие из которых работали над Finagle. Они выпустили Linkerd 1.x в 2016 - JVM-based прокси, идейный наследник Finagle, но уже как самостоятельный продукт, а не библиотека. В апреле 2017 William Morgan, CEO Buoyant, опубликовал статью «What's a service mesh? And why do I need one?» (buoyant.io) - именно она закрепила в индустрии сам термин «service mesh» как название категории. Раньше говорили про «sidecar proxy», «service proxy», «client-side networking»; после статьи Morgan-а появилось общее имя для класса систем. В 2018 Linkerd 2.x был переписан с JVM на Rust - реакция на тот же урок, который двигал Klein-а: для прокси, который висит рядом с каждым подом, JVM с её heap-ом и GC-паузами оказывается слишком дорогой инфраструктурой.

В мае 2017 Google, IBM и Lyft анонсировали Istio - control plane поверх Envoy. Идея простая: Envoy уже есть и хорош как прокси; чтобы превратить парк прокси в единую систему, нужен централизованный мозг, который раздаёт им конфигурацию, выпускает сертификаты, синхронизируется с Kubernetes API. Istio 1.0 вышел в 2018; к 2019 он стал самым популярным service mesh в опросах CNCF. В 2019 CNCF опубликовал Service Mesh Interface (SMI) - попытку стандартизировать API между разными mesh-решениями, чтобы пользователи могли менять реализацию без переписывания манифестов. SMI остался скорее эталоном, чем доминирующим стандартом, но сама попытка показывает зрелость категории.

Отдельная ветка - Cilium от Isovalent, основанный на eBPF. Вместо sidecar-прокси Cilium использует eBPF-программы, исполняемые ядром Linux, чтобы перехватывать сетевые вызовы на уровне socket и применять политики mesh без отдельного процесса-прокси. Около 2020 года Cilium вырос из чистой CNI-плагинной роли в полноценную mesh-альтернативу. В 2022 Google анонсировал Istio Ambient Mesh - архитектуру, в которой L4-функции (mTLS, identity) реализованы общим прокси на уровне ноды (ztunnel), а L7-логика (HTTP routing, retries) добавляется по необходимости через waypoint-прокси. Это прямой ответ на главную операционную боль классического Istio - стоимость sidecar в каждом поде.

Полезный исторический урок: концепция «вынести сетевое поведение из приложения в инфраструктуру» зрела минимум 6-7 лет (2010-2017), прежде чем стала индустриальным стандартом. Сначала это были языковые библиотеки внутри отдельных компаний (Finagle, Hystrix). Потом - prosess-level proxy для polyglot-стэков (Envoy). Потом - control plane поверх прокси (Istio). Потом - переосмысление, нужен ли вообще sidecar (Ambient Mesh, Cilium). Каждая итерация решала операционную проблему предыдущей; service mesh сегодняшнего дня - результат этой эволюции, а не одномоментное изобретение.

Envoy как стандарт: почему именно Envoy

В 2016-2017, когда индустрия искала стандарт для data plane, кандидатов было несколько: nginx (production-готовый, но изначально не для service-to-service трафика), HAProxy (прекрасный L4, но менее гибкий на L7), Linkerd 1.x (JVM-based, тяжёлый), несколько внутренних решений в крупных компаниях. Envoy выиграл по нескольким причинам, и важно понимать каждую - они объясняют, почему сегодня практически все mesh-решения (Istio, Consul Connect, AWS App Mesh, Kuma, Open Service Mesh) используют Envoy как data plane.

Первая причина - L7-аware на уровне HTTP/2 и gRPC. Большинство классических прокси думали в терминах TCP-соединений: пришёл байт-поток - перенаправили байт-поток. Envoy с самого начала строился вокруг HTTP/2 как primary-протокола: он понимает, что в одном TCP-соединении живёт мультиплекс из множества stream-ов, что у каждого stream есть свой статус-код, заголовки, тело. Это критично, потому что retry на TCP-уровне бесполезен (вы не знаете, был ли запрос обработан); retry на HTTP-уровне с понятием идемпотентности - валидная стратегия. Аналогично с трассировкой: TCP-прокси не видит trace-id в заголовках, HTTP-прокси видит и может обогащать.

Вторая причина - dynamic configuration через xDS API. Envoy не требует перезапуска для применения новой конфигурации. Control plane по gRPC-протоколу xDS (LDS, RDS, CDS, EDS - Listener/Route/Cluster/Endpoint Discovery Service) пушит изменения в Envoy в реальном времени: новый upstream появился - Envoy узнал; маршрут изменился - Envoy применил; circuit breaker сработал - Envoy уведомил. Это превращает парк прокси в управляемую систему, а не в зоопарк независимых nginx-конфигов. Klein в эссе про universal data plane API специально подчёркивает: главный продукт Envoy - не сам прокси, а xDS API; именно он позволил появиться category service mesh.

Третья причина - observability как first-class citizen. Envoy из коробки выдаёт сотни метрик в Prometheus-формате: latency-гистограммы по upstream-у, retry-счётчики, статусы circuit breaker, размеры коннекшн-пулов. Эти метрики одинаковы для всех сервисов в кластере, потому что метрики собирает прокси, а не приложение. Команда платформы получает единый dashboard уровня «p99 latency между сервисом A и сервисом B» без необходимости договариваться с каждой продуктовой командой о формате метрик. Это организационный эффект, не технический: единая семантика observability снимает класс конфликтов «у нас в Java SDK эти метрики, у вас в Python такие, давайте договоримся».

Четвёртая причина - C++ и осознанная работа с памятью. Envoy написан на C++ с упором на низкое потребление ресурсов и предсказуемую latency. Это не философский выбор; это следствие того, что прокси висит рядом с каждым подом, и если он начинает аллоцировать в hot path или вставать на GC-паузу, латентность всех приложений в кластере деградирует одновременно. Linkerd 2.x перешёл с JVM на Rust по той же причине - не потому что Rust «современнее», а потому что для прокси на критическом пути предсказуемость памяти важнее удобства разработки.

Сегодня Envoy используется не только в service mesh. AWS использует его в App Mesh, частично - в ALB. Google использует Envoy внутри своей инфраструктуры. Stripe, Square, Reddit, Pinterest публично описывали Envoy как стандарт edge proxy. То есть Envoy перерос исходную роль mesh-data-plane и стал универсальным L7-прокси для современной инфраструктуры. Service mesh - крупнейший потребитель, но не единственный.

Архитектура: Control Plane vs Data Plane

Все современные service mesh (Istio, Linkerd, Consul Connect, Cilium, Kuma) разделяются на два слоя - control plane и data plane. Это разделение настолько фундаментально, что без него обсуждать mesh бессмысленно: проблемы, антипаттерны и operational цена все формулируются в терминах одного из двух слоёв.

                    +------------------+
                    |   Control Plane  |
                    |     (Istiod)     |
                    |                  |
                    | - Service        |
                    |   discovery      |
                    | - Config mgmt    |
                    | - Certificate    |
                    |   authority      |
                    +--------+---------+
                             |
            xDS API (push configs)
                             |
         +-------------------+-------------------+
         |                   |                   |
         v                   v                   v
+--------+-------+  +--------+-------+  +--------+-------+
|   Data Plane   |  |   Data Plane   |  |   Data Plane   |
|                |  |                |  |                |
| +---+ +------+ |  | +---+ +------+ |  | +---+ +------+ |
| |App| |Envoy | |  | |App| |Envoy | |  | |App| |Envoy | |
| |   |<->Proxy| |  | |   |<->Proxy| |  | |   |<->Proxy| |
| +---+ +------+ |  | +---+ +------+ |  | +---+ +------+ |
|     Pod A      |  |     Pod B      |  |     Pod C      |
+----------------+  +----------------+  +----------------+
Компонент Роль Пример
Control Plane Мозг: управляет конфигурацией, service discovery, выдаёт сертификаты Istiod, Linkerd control plane
Data Plane Руки: пропускает трафик, применяет политики, собирает метрики Envoy proxy (sidecar)

Ключевой момент: Istio сам не гоняет трафик. Он управляет конфигурацией Envoy-прокси через xDS API. Это разделение объясняет, почему сбои этих двух слоёв ведут себя по-разному: падение control plane не убивает трафик мгновенно (последняя пушенная конфигурация остаётся в data plane), но через какое-то время mesh теряет способность реагировать на изменения - новые поды не получают конфиг, сертификаты не ротируются, политики не обновляются. Падение data plane (sidecar в конкретном поде) убивает трафик именно этого пода.

Ingress Gateway, Sidecar и Egress Gateway

Envoy в mesh играет три роли в зависимости от позиции:

Service mesh и API gateway - не одно и то же, и путаница между ними - один из самых распространённых архитектурных дефектов adoption. API gateway (Kong, Apigee, AWS API Gateway, ingress nginx) живёт на границе системы и работает с внешним трафиком: аутентификация конечных пользователей, rate limiting, монетизация API, трансформация запросов. Service mesh живёт внутри системы и работает с межсервисным трафиком: mTLS между подами, retries, observability. Один не заменяет другой; в зрелой архитектуре они сосуществуют, и Ingress Gateway service mesh может играть роль L7-моста между ними.

Istio: управление mesh

Istio - control plane, который программирует Envoy-прокси. Архитектурно с версии 1.5 (2020) Istio упростился до одного бинарника Istiod, который объединил Pilot (xDS server), Citadel (certificate authority) и Galley (configuration validation) - первоначально это были три отдельных сервиса, и операционная сложность их совместной эксплуатации была одной из исторических жалоб на Istio.

Lin Sun, инженер из Google и IBM Istio team, в книге «Istio in Action» (Manning, 2022, в соавторстве с Christian Posta) специально подчёркивает: главная причина, по которой adoption Istio спотыкался в 2018-2020 годах - это операционная сложность многокомпонентной control plane. Унификация в Istiod и появление Ambient Mesh - прямые ответы на этот опыт. Если читать одну книгу про Istio, имеет смысл взять именно её, а не общую документацию: книга расставляет акценты исходя из того, что в production действительно ломается, а не исходя из того, что лучше всего демонстрирует возможности.

Минимальный пример: VirtualService, который направляет пользователя по имени jason на v2-версию сервиса reviews, остальных - на v1, без изменения кода ни клиента, ни сервиса.

kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
 - reviews
  http:
 - match:
   - headers:
        end-user:
          exact: jason
    route:
   - destination:
        host: reviews
        subset: v2
 - route:
   - destination:
        host: reviews
        subset: v1
EOF

Этот конфиг отрабатывает Envoy-прокси на стороне вызывающего сервиса: он смотрит заголовок end-user, и в зависимости от его значения отправляет запрос в один из subset-ов. Сервис reviews ничего не знает о том, что трафик расщепился; для него это просто два разных пода, которые получают запросы.

Что делает Istiod

Управление трафиком

Канареечные релизы

http:
- route:
 - destination:
      host: my-service
      subset: v1
    weight: 95
 - destination:
      host: my-service
      subset: v2
    weight: 5

Канареечный релиз с расщеплением трафика по весам - типичный кейс, ради которого многие команды и приходят к service mesh. До него тот же эффект достигался либо feature flags на уровне приложения, либо двумя deployment-ами с ручным переключением Service-селектора - оба способа требуют дисциплины и кода в приложении. Istio превращает это в декларативный YAML без участия приложения.

Circuit Breaker

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: my-service
spec:
  host: my-service
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        h2UpgradePolicy: UPGRADE
        http1MaxPendingRequests: 100
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 30s
      baseEjectionTime: 30s

Retries и Timeouts

http:
- route:
 - destination:
      host: my-service
  retries:
    attempts: 3
    perTryTimeout: 2s
    retryOn: 5xx,reset,connect-failure
  timeout: 10s
Retries и circuit breaker на уровне mesh - не замена retries и backoff в коде приложения, а дополнение. Mesh ничего не знает про идемпотентность endpoint-а; настройка retryOn: 5xx, применённая к POST /payments без идемпотентности на стороне приложения, приведёт к двойному списанию. Это один из ключевых антипаттернов adoption, разобранный ниже.

Реальные масштабы операционной нагрузки

Абстрактные обсуждения «sidecar добавляет латентность и память» стоят меньше, чем разобранные истории компаний, которые эксплуатируют mesh в production на серьёзных масштабах и публично об этом писали. Ниже - подборка кейсов с разным контекстом и разными уроками; если факт указан без ссылки, это стилизация на основе типичных паттернов.

Lyft - источник Envoy и одновременно крупнейший внутренний пользователь. На envoyproxy.io и в выступлениях Matt Klein-а на KubeCon неоднократно описывалось, что Lyft использует Envoy не только как mesh sidecar, но и как edge proxy, и как proxy между регионами. Урок Lyft не «mesh всегда работает» - они изначально строили Envoy под свои потребности, и весь их toolchain эволюционировал вместе с прокси. Для компании, которая пытается перенять Envoy/Istio готовым, путь будет существенно дороже.

Shopify - один из публично описанных deployment-ов Istio на масштабе Rails-монолита и сопутствующих сервисов. Их инженерный блог shopify.engineering регулярно публикует материалы о том, как mesh интегрируется в их платформу, и какие инвестиции потребовались поверх стандартного Istio - собственные CRD, собственный tooling над VirtualService, ChatOps-обвязка для управления политиками. Это типовой паттерн крупного adoption: Istio из коробки покрывает 70% потребностей, остальное превращается во внутреннюю платформенную работу. Команды без ресурсов на эту работу либо платят сторонним вендорам (Solo.io, Tetrate), либо постепенно отказываются от mesh.

HelloFresh публично описывал миграцию на Linkerd 2.x как пример выбора более лёгкого mesh для команды без выделенной mesh-команды. Их публикации (см. блог HelloFresh Engineering) делают акцент на том, что главным фактором выбора был не feature parity, а operational simplicity: Linkerd с его Rust-based прокси и более скромным набором CRD требовал меньше экспертизы для эксплуатации. Это хороший контрпример Istio-первой логике: для не-гигантских организаций Linkerd часто оказывается прагматичным выбором.

Salesforce / Heroku исторически использовали Linkerd в production - Buoyant в нескольких публикациях и презентациях упоминал Heroku как один из самых ранних production-пользователей. Эта связь интересна тем, что Heroku - PaaS, и для них mesh оказался способом дать customers прозрачный mTLS и observability без того, чтобы customers что-то меняли в своих приложениях. PaaS-сценарий до сих пор остаётся одним из сильнейших аргументов в пользу mesh: владелец платформы получает единый рычаг управления сетью для всех клиентских приложений.

Microsoft в 2019 году выпустил Open Service Mesh (OSM) - собственную реализацию SMI-совместимого mesh, и в 2021 передал его в CNCF. В 2023 проект был архивирован: Microsoft публично заявил о свёртывании самостоятельной разработки OSM и сосредоточении на интеграции с Istio в AKS. История OSM - полезный пример консолидации в категории: даже компания уровня Microsoft не смогла обеспечить достаточную поддержку отдельной реализации против Istio-экосистемы. Сегодня выбор практически свёлся к Istio (и форкам типа Tetrate Service Bridge), Linkerd и Cilium; всё остальное либо нишевые, либо managed-предложения cloud-провайдеров.

Booking.com в публикациях и докладах упоминал Envoy как часть собственного сетевого стэка - но не классический Istio, а собственная control plane поверх Envoy. Это второй важный паттерн: компании с очень специфичными требованиями (legacy-сети, нестандартный multi-DC, custom load-balancing логика) часто используют Envoy как data plane, но пишут свой control plane. Это стилизованный пример на основе типичных паттернов - точные детали реализации Booking.com не публичны, - но категория выбора («Envoy + custom control plane») реально существует и характерна для гипергалактических SaaS и e-commerce.

Стилизованный пример operational нагрузки. Кластер на 1000 подов, sidecar Envoy на каждом. По публично заявленным цифрам Istio документации, sidecar потребляет 50-100 MB RAM в типичной конфигурации (в зависимости от количества upstream-ов и применённых политик) и добавляет 1-3 ms к p99 latency на hop. Арифметика:

Эти числа объясняют, почему Istio Ambient Mesh появился именно в 2022 году: при росте кластеров до десятков тысяч подов sidecar-модель перестаёт окупаться. Ambient вместо sidecar в каждом поде использует общий L4-прокси на уровне ноды (ztunnel), и L7-обработка применяется только в waypoint-прокси там, где она нужна. По публикациям Solo.io и Tetrate, в типичных кластерах Ambient экономит 50-70% ресурсов прокси. Цена - зрелость: Ambient значительно моложе классического sidecar-режима, и его operational quirks ещё не настолько изучены индустрией.

Полезный вывод: operational цена mesh - это не «несколько процентов overhead», а структурная статья расходов, которая растёт линейно с количеством подов. Для кластера на 50 подов это шум; для кластера на 5000 подов - бюджетная позиция, требующая отдельного обоснования.

Антипаттерны в adoption

Большинство неудачных внедрений service mesh ломаются не на этапе «не получилось установить Istio», а на этапе «работает, но ценность не материализовалась». Ниже - типичные failure modes; каждый стоит пройти как чеклист до того, как тратить квартал на adoption.

Mesh без observability стэка. Service mesh даёт metrics endpoint в Prometheus-формате, distributed tracing через Jaeger/Zipkin, и access logs - но всё это бесполезно, если в команде нет Prometheus, Grafana, Jaeger, и нет привычки на них смотреть. Команды устанавливают Istio, не разворачивают visualization-стэк, и через полгода обнаруживают, что у них появился новый инфраструктурный слой, который они не наблюдают - то есть худший вариант: новые failure modes без новой видимости. Правильная последовательность - сначала observability, потом mesh поверх неё.

mTLS на день один с низким buy-in команд. Соблазн «давайте включим STRICT mTLS на всём кластере» очень велик - кажется, что это просто галочка в AuthorizationPolicy. Реальность: любой сервис, который не успел получить sidecar (legacy workload, batch job, DB-клиент), мгновенно перестаёт работать. Если команды не были предупреждены и не прошли через STRICT-PERMISSIVE миграцию, день включения превращается в массовый инцидент. Правильная последовательность - сначала PERMISSIVE mode (mTLS принимается, но необязателен), измерение покрытия, отдельные политики на namespace по мере готовности команд, переход к STRICT только когда покрытие близко к 100%.

Mixup ролей mesh и API gateway. Команда устанавливает Istio для межсервисного mTLS и решает заодно унести на него внешний трафик через Ingress Gateway. Через несколько месяцев обнаруживается, что Istio Ingress Gateway не предоставляет тех функций, которые ожидаются от API gateway - монетизации, developer portal, API key management, трансформации запросов. Команда либо городит костыли, либо ставит отдельный API gateway перед mesh, дублируя слой proxy. Правильное разделение: API gateway для внешних API, mesh для internal service-to-service. Граница - на edge.

Игнорирование sidecar-памяти и CPU. Команда не учитывает, что 75 MB RAM x количество подов - это бюджет, который надо явно закладывать в capacity planning. В первой же квартальной review кластер упирается в node memory, scheduler начинает evict-ить поды, и инженеры списывают это на «Kubernetes тормозит». Решение тривиальное - sidecar resource requests и limits должны быть явно прописаны, и автомасштабирование кластера должно учитывать sidecar overhead - но без сознательного шага это не происходит.

Mesh circuit breaker как замена app-level resilience. Команда настраивает outlierDetection в DestinationRule, видит, что Istio помечает «плохие» поды и перестаёт слать на них трафик, и делает вывод: «теперь у нас resilience». Через месяц обнаруживается, что приложение всё равно падает при медленных upstream-ах, потому что mesh circuit breaker реагирует на 5xx и connection errors, а медленные ответы (200 OK с latency 30 секунд) для него выглядят нормально. Mesh ничего не знает про логику timeout-а вашего приложения, про cache, про graceful degradation - всё это остаётся на стороне кода. Mesh добавляет новый слой защиты, не заменяет старый.

Retries в mesh без идемпотентности. retryOn: 5xx,reset,connect-failure звучит безобидно. Применённый к POST /payments без идемпотентного ключа на стороне backend, он приведёт к двойному списанию при первом же сетевом флапе - и это инцидент, который сложно атрибутировать, потому что в логах приложения всё выглядит нормально. Retries в mesh имеют смысл только для идемпотентных endpoint-ов или там, где приложение умеет дедуплицировать (см. идемпотентные ключи). Для остальных endpoint-ов retry-логика должна оставаться в приложении.

Upgrade control plane без отрепетированного failover. Istio - сложная распределённая система, и обновление с 1.18 на 1.19 может содержать breaking changes в CRD, в xDS-протоколе, в default-политиках. Команда обновляется в production, не отрепетировав на staging, и обнаруживает, что часть Envoy-прокси перестала получать конфигурацию - а значит новые поды не запускаются, политики не применяются, сертификаты не ротируются. Service mesh без работающего control plane деградирует постепенно (в течение часов-дней), что делает диагностику сложнее, чем при мгновенном падении. Правильная последовательность - canary upgrade на staging, отдельный canary в одном namespace production, потом постепенное раскатывание.

mTLS без стратегии ротации сертификатов. Сертификаты, выдаваемые Istiod, по умолчанию короткоживущие (24 часа). Это правильное архитектурное решение, но оно требует, чтобы control plane был доступен для ротации; если Istiod недоступен в течение 24+ часов (long-running staging-инцидент, упавший etcd, мисконфигурация webhook-а), сертификаты в data plane истекают, и mesh входит в каскадный отказ - все mTLS-соединения начинают отбиваться. Стратегия ротации - не «оно само работает», а отдельный runbook с метриками expiration и алертами заранее.

Эвристика для self-check: возьмите план adoption mesh и пройдитесь по списку выше. Если на любом пункте ответ «у нас этого нет, потому что не задумались» - это техдолг с понятной стоимостью первого инцидента. Service mesh - это не «улучшение архитектуры»; это отдельная распределённая система, которую вы добавляете к своей инфраструктуре, и она требует тех же дисциплин, что любая другая.

Когда внедрять

Service mesh нужен, если:

Service mesh избыточен, если:

Overhead: каждый sidecar Envoy потребляет ~50-100MB RAM и добавляет ~1-3ms latency. При тысячах подов это существенно. Для оценки затрат: кластер из 500 подов с sidecar-ами потребляет дополнительно 25-50 GB RAM только на прокси.
Тренд: sidecar-less mesh. Istio Ambient Mesh (с версии 1.18) предлагает альтернативу sidecar-паттерну. Вместо прокси в каждом поде используется общий L4-прокси на уровне ноды (ztunnel), а L7-обработка применяется только там, где нужна. Это значительно снижает потребление ресурсов и упрощает adoption. Cilium идёт ещё дальше, реализуя часть mesh-функций через eBPF в ядре Linux - без отдельного прокси-процесса вообще.

Альтернативы

Решение Сложность Когда использовать
Библиотеки (Resilience4j, Polly) Низкая Один язык, мало сервисов
Linkerd Средняя Проще Istio, меньше features, Rust data plane
Istio Высокая Полный контроль, enterprise, самая широкая экосистема
Cilium Высокая eBPF-based, без sidecar overhead
Связанные материалы:

Источники и смежные статьи

Первоисточники и эссе

Документация

Реальные кейсы и инженерные блоги