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 и timeouts - запрос может не дойти, и ответом должно быть осмысленное повторение, а не падение всего вызывающего пути.
- Circuit breaker - если соседний сервис деградировал, нужно перестать его звать, чтобы не унести себя вместе с ним за счёт каскадных отказов.
- mTLS - шифрование и взаимная аутентификация трафика между сервисами; в zero-trust сетях нельзя полагаться на сетевые границы как на гарантию доверия.
- Распределённая трассировка - вопрос «где именно тормозит запрос» в системе из 20 hops неразрешим без сквозных trace-id и span-id, прокидываемых на уровне HTTP-заголовков.
- Канареечные релизы - возможность направить 5% трафика на новую версию, измерить latency и error rate, и откатить или раскатать дальше без редеплоя клиентов.
- Политики доступа - правила, какие сервисы могут звать какие endpoint-ы; в compliance-чувствительных системах это требование, а не желание.
Все эти задачи можно решить в коде каждого сервиса. Многие компании именно так и начинали: библиотека retries на стороне клиента, отдельная обвязка для mTLS, OpenTelemetry SDK в каждом репозитории. Проблема не в том, что это технически невозможно, а в том, что издержки этой работы растут квадратично с ростом организации:
- Логика дублируется во всех сервисах, и при изменении политики (например, переход с consecutive5xxErrors=5 на 3) требуется координированный редеплой.
- Разные языки (Go, Java, Python, Node, Ruby) дают разные реализации с разными edge-cases; библиотека retries для JVM ведёт себя иначе, чем для Python asyncio, и эти различия проявляются в инцидентах.
- Эволюция политики безопасности зависит от готовности владельцев каждого сервиса обновить SDK; в больших компаниях этот цикл занимает месяцы.
- Observability фрагментирована: метрики из 20 сервисов с разной семантикой не складываются в общую картину.
История возникновения: проблема с каждым сервисом
Идея, что сетевая логика должна жить отдельно от прикладной, появилась задолго до термина «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 играет три роли в зависимости от позиции:
- Ingress Gateway - стоит на границе кластера и принимает внешний трафик: TLS termination, rate limiting, глобальный роутинг по доменам и путям, аутентификация внешних запросов.
- Sidecar - запускается рядом с каждым подом (через Istio injection, обычно через mutating webhook). Перехватывает весь ingress/egress трафик конкретного сервиса: mTLS между сервисами (mutual TLS - обе стороны проверяют сертификаты друг друга, в отличие от обычного TLS, где только клиент проверяет сервер), retries и circuit breaker, канареечные правила, сбор метрик и трейсов, политики доступа уровня pod-to-pod.
- Egress Gateway - контролирует исходящий трафик из кластера во внешние сервисы: ограничение, куда сервисы могут ходить, логирование внешних вызовов, единые политики для исходящего трафика.
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
- Service discovery: синхронизируется с Kubernetes API, отслеживает Pod-ы, Service-ы, Endpoints; результат пушится в Envoy через CDS/EDS.
- Configuration: переводит Istio CRD (VirtualService, DestinationRule, AuthorizationPolicy) в xDS-конфигурацию для Envoy.
- Certificates: выступает Certificate Authority - выдаёт и автоматически ротирует сертификаты для mTLS, по умолчанию каждые 24 часа.
Управление трафиком
Канареечные релизы
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
Реальные масштабы операционной нагрузки
Абстрактные обсуждения «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. Арифметика:
- 1000 подов x ~75 MB = ~75 GB RAM кластера расходуется только на sidecar-прокси.
- В типичной микросервисной транзакции из 5-7 hops накладная latency составляет 5-20 ms - это сопоставимо с p99 многих internal SLA.
- CPU-овершед при высоких RPS (несколько тысяч RPS на под) может достигать 0.5-1 vCPU на sidecar.
Эти числа объясняют, почему 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 и алертами заранее.
Когда внедрять
Service mesh нужен, если:
- Много микросервисов (10+) на разных языках, и логика retries/timeouts/circuit breaker уже фрагментирована между языковыми SDK.
- Требуется mTLS между всеми сервисами как compliance- или zero-trust требование.
- Нужны канареечные релизы и трафик-сплиттинг без изменения кода клиентов.
- Требуется единая observability (метрики, трейсы) с одной семантикой по всему парку.
- Есть compliance-требования к шифрованию internal трафика.
Service mesh избыточен, если:
- Монолит или несколько сервисов, межсервисный трафик невелик.
- Команда не готова к operational сложности и не имеет выделенной platform-функции.
- Все сервисы на одном языке - ту же ценность даст shared library (Resilience4j, Polly, OpenTelemetry SDK) с меньшими накладными.
- Нет ресурсов на поддержку (sidecar добавляет latency и потребление памяти, control plane требует эксплуатации).
Альтернативы
| Решение | Сложность | Когда использовать |
|---|---|---|
| Библиотеки (Resilience4j, Polly) | Низкая | Один язык, мало сервисов |
| Linkerd | Средняя | Проще Istio, меньше features, Rust data plane |
| Istio | Высокая | Полный контроль, enterprise, самая широкая экосистема |
| Cilium | Высокая | eBPF-based, без sidecar overhead |
- Когда переходить на Kubernetes - service mesh имеет смысл только поверх работающего K8s.
- Монолит vs Микросервисы - когда переходить на микросервисы.
- C4 Model - как документировать архитектуру.
- Идемпотентные ключи - почему retries в mesh небезопасны без идемпотентности.
Источники и смежные статьи
Первоисточники и эссе
- Matt Klein. The Universal Data Plane API (2017). Программное эссе создателя Envoy о том, что главный продукт Envoy - не прокси, а xDS API.
- William Morgan. What's a service mesh? And why do I need one? (2017). Статья, закрепившая в индустрии сам термин «service mesh».
- Christian Posta, Lin Sun. Istio in Action (Manning, 2022). Лучшее введение в Istio с production-фокусом; разбирает не «что есть», а «что в production ломается и почему».
Документация
- Istio documentation. Официальный source of truth.
- Envoy Proxy documentation. Глубокое погружение в data plane.
- Linkerd documentation. Альтернатива Istio с фокусом на operational simplicity.
- Cilium documentation. eBPF-based подход к mesh.
- Istio Ambient Mesh overview. Sidecar-less архитектура и её trade-offs.
Реальные кейсы и инженерные блоги
- envoyproxy.io. Истории adoption Envoy в Lyft и других компаниях.
- Shopify Engineering. Публикации про Istio в платформенной обвязке.
- Buoyant Blog. Производитель Linkerd, регулярно публикует case studies (Salesforce/Heroku, HelloFresh).