Перейти к содержимому
Hogin Hogin
Назад

Istio ambient: service mesh без сайдкаров

9 мин чтения

Главный аргумент против service mesh был один и тот же годами: чтобы получить mTLS и наблюдаемость, нужно засунуть Envoy-прокси в каждый под. Это лишний процесс, лишняя память, лишний рестарт при каждом деплое sidecar-инжектора и отдельная головная боль с init-контейнерами и порядком старта. Ambient-режим Istio убирает сайдкар из этого уравнения: mTLS и часть телеметрии приходят почти даром на уровне ноды, а тяжёлая L7-логика включается только там, где она реально нужна.

Содержание

Открыть содержание

Зачем вообще нужен service mesh

Service mesh решает узкий, но болезненный набор задач: шифрование трафика между сервисами без правок в коде приложений, единая точка для ретраев и таймаутов, наблюдаемость на уровне “кто с кем и как долго говорил” без инструментирования каждого сервиса вручную. Istio стал де-факто стандартом для Kubernetes именно потому, что решал всё это декларативно — через CRD, а не через библиотеки, которые нужно тащить в каждый сервис на любом языке.

Но всю эту функциональность годами покупали одной и той же ценой — сайдкаром в каждом поде, независимо от того, нужна ли конкретному сервису хоть одна из продвинутых возможностей. Ambient — это попытка Istio разделить цену и функциональность так, чтобы платить только за то, что реально используется.

Что не так с сайдкарами

Классический Istio работает через sidecar injection: вебхук на этапе создания пода добавляет к контейнеру приложения ещё один контейнер — Envoy. Дальше весь трафик пода идёт через iptables-редирект в этот Envoy, который и делает mTLS, ретраи, метрики и маршрутизацию.

Модель рабочая, но с ощутимой ценой:

Sidecar-инъекция vs ztunnel: один процесс на под против одного процесса на ноду

Архитектура ambient: ztunnel и waypoint

Ambient разбивает то, что раньше делал один Envoy-сайдкар, на два независимых слоя, которые включаются раздельно.

ztunnel (L4) — маленький прокси, написанный на Rust, один экземпляр на ноду (DaemonSet), а не на под. Он забирает у сайдкара самую частую и самую дешёвую по логике работу: устанавливает mTLS-туннель между подами, собирает базовые L4-метрики (кто с кем говорит, сколько байт, какой статус соединения) и применяет L4-политики авторизации (allow/deny по identity). ztunnel не парсит HTTP, не делает ретраи, не смотрит заголовки — и именно поэтому он маленький и дешёвый.

waypoint (L7) — полноценный Envoy-прокси, но теперь не привязанный к каждому поду. Он разворачивается на сервис или namespace, только когда кому-то нужна L7-функциональность: маршрутизация по заголовкам, ретраи, таймауты, canary по весам, AuthorizationPolicy по HTTP-методу или пути. Если такой функциональности не требуется — waypoint просто не разворачивается, и трафик идёт напрямую через ztunnel-ы отправителя и получателя.

Архитектура ambient: ztunnel на каждой ноде даёт mTLS всем, waypoint появляется точечно там, где нужен L7

Ключевое отличие от sidecar-модели: L4-возможности (mTLS, базовые метрики, идентичность) есть у всех бесплатно, а L7-возможности (маршрутизация, ретраи, детальные HTTP-политики) — только у тех сервисов, которые их явно попросили. Раньше это была бинарная развилка «весь Istio целиком или ничего», теперь — шкала с двумя ступенями.

mTLS между подами без перезапуска приложений

Самое ощутимое отличие на практике — mTLS в ambient появляется без единого перезапущенного пода. Поскольку ztunnel живёт вне пода приложения и перехватывает трафик на уровне ноды через CNI-плагин, включение mesh для namespace сводится к тому, чтобы ztunnel начал видеть трафик этих подов — сами поды при этом не трогаются.

Это принципиально меняет операционную модель внедрения: в sidecar-режиме включение mesh на существующем namespace означало rolling restart всех деплойментов (чтобы вебхук успел заинжектить сайдкар). В ambient — это лейбл на namespace, который подхватывается на лету.

Механика редиректа тоже другая. Sidecar перенаправляет трафик пода на себя через iptables-правила, которые генерирует init-контейнер при старте пода. Ambient использует CNI-плагин Istio, который на уровне ноды заворачивает трафик подов из размеченного namespace в локальный ztunnel через HBONE-туннель (HTTP-based Overlay Network Encapsulation) — специальный протокол поверх mTLS, разработанный именно для ambient. Для приложения разница невидима: оно как отправляло и принимало обычные TCP-соединения, так и продолжает, только по пути они теперь всегда идут через ztunnel и всегда зашифрованы.

Постепенное включение: от L4 ко всему остальному

Ambient проектировался как модель с явными, обратимыми ступенями, а не «всё или ничего»:

  1. L4 на весь namespace. Один лейбл — и все поды в namespace получают mTLS, идентичность и базовые метрики через ztunnel. Приложения продолжают работать как ни в чём не бывало.
  2. Waypoint там, где нужен L7. Как только конкретному сервису требуется маршрутизация по заголовку, канареечный релиз по весам или AuthorizationPolicy с условием по HTTP-пути — для него разворачивается waypoint. Остальные сервисы в том же namespace продолжают работать через один ztunnel, без L7-оверхеда.
  3. Отключение так же просто, как включение. Убрать лейбл или удалить waypoint — не rolling restart, а обратимая операция на уровне конфигурации.

Три шага: лейбл на namespace даёт mTLS всем, waypoint добавляется точечно, L7-политика — только там, где нужна

Практически это значит, что миграция с “mesh нет вообще” до “mesh с L7 на критичных сервисах” больше не требует единого решения на весь кластер — можно двигаться сервис за сервисом, оценивая, где L7 действительно окупается.

Сравнение с mesh на базе Cilium/eBPF

Ambient — не единственный ответ на “сайдкары дорого”. Cilium Service Mesh и похожие eBPF-решения атакуют ту же проблему с другой стороны: вместо прокси-процесса на ноду они используют eBPF-программы в ядре для L3/L4-маршрутизации и политик. У обоих подходов общая идея — вынести дешёвую, частую работу из userspace-прокси, но механика и trade-off’ы разные.

Sidecar (Istio classic)Istio ambientCilium (eBPF-mesh)
L4-балансировка и политикиEnvoy-сайдкар в каждом подеztunnel, один на нодуeBPF-программы в ядре, без userspace-прокси на L4
mTLSсайдкар, per-podztunnel, per-nodeобычно через отдельный L7-прокси (например, отдельный waypoint)
L7 (HTTP-маршрутизация, ретраи)сайдкар всегдаwaypoint, точечноотдельный L7-прокси при необходимости
Оверхед на под без L7-нуждвысокий (полный Envoy)минимальный (только ztunnel на ноде)минимальный (eBPF в ядре)
Обновление без рестарта приложениянетда (ztunnel вне пода)да (eBPF не в поде)
Зрелость на 2026стабильна много летGA с 1.24, multi-cluster в alpha с 1.27зрелая для L3/L4, L7-часть моложе
Экосистема политик и CRDсамая широкая (Istio CRD)те же CRD, что и classic Istioсвоя модель (CiliumNetworkPolicy и родственные)

Практический вывод: если вы уже используете Istio CRD (VirtualService, AuthorizationPolicy) и хотите сохранить экосистему — ambient даёт тот же API с меньшим оверхедом. Если вы выбираете mesh с нуля и CNI уже Cilium — есть смысл посмотреть на его встроенный mesh, чтобы не тащить второй компонент поверх уже работающего eBPF-датаплейна.

Есть и менее очевидный фактор выбора — операционная зрелость команды. eBPF-программы работают в ядре хоста, а значит требуют более свежих ядер и аккуратной работы с обновлениями узлов: баг в eBPF-коде потенциально роняет не под, а всю ноду. ztunnel — обычный userspace-процесс, который падает и перезапускается как любой другой под, а отлаживать его можно привычными средствами (логи, kubectl exec, профилировщики). Для команд, которые уже умеют эксплуатировать Istio classic, миграция на ambient — это смена профиля установки, а не смена инструментария и ментальной модели.

Что нужно, чтобы попробовать ambient

Минимум для проверки на существующем кластере: Kubernetes 1.28+, CNI, совместимый с ambient-редиректом (Istio ставит собственный CNI-плагин), и istioctl актуальной версии.

# установка control plane с ambient-профилем
istioctl install --set profile=ambient --skip-confirmation

# проверяем, что ztunnel и istio-cni поднялись как DaemonSet на каждой ноде
kubectl get pods -n istio-system -l app=ztunnel -o wide
kubectl get pods -n istio-system -l k8s-app=istio-cni-node -o wide

Включаем ambient на namespace — приложения не трогаем:

kubectl label namespace shop istio.io/dataplane-mode=ambient

Добавляем waypoint для одного сервиса, которому нужен L7 (например, канареечный роутинг по заголовку):

istioctl waypoint apply --namespace shop --for service --name checkout

L7-политика на конкретный сервис — только он теперь идёт через waypoint, остальные сервисы namespace продолжают работать через один ztunnel:

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: checkout-only-internal
  namespace: shop
spec:
  targetRef:
    kind: Service
    name: checkout
  action: ALLOW
  rules:
    - from:
        - source:
            namespaces: ["shop", "gateway"]
      to:
        - operation:
            methods: ["GET", "POST"]

Как убедиться, что работает

Проверяем, что трафик между подами в namespace действительно зашифрован mTLS, не заходя ни в один под:

# смотрим на статус ztunnel-соединений для namespace
istioctl ztunnel-config connections <ztunnel-pod-name> -n istio-system

# ждём, что протокол соединений — HBONE (mTLS-туннель ambient),
# а не голый TCP

Второй быстрый способ — заглянуть в логи ztunnel и убедиться, что он видит трафик нужного namespace:

kubectl logs -n istio-system -l app=ztunnel --tail=50 | grep shop

Если waypoint развёрнут, проверяем, что трафик к сервису идёт именно через него, а не напрямую — в istioctl proxy-config для waypoint-пода должны появиться маршруты для checkout.

Полезная проверка на трезвость перед тем, как включать ambient на проде, — сравнить потребление памяти mesh-слоя до и после на staging-копии реального namespace. Разница между суммой памяти всех sidecar-контейнеров и памятью одного ztunnel на ноду — это и есть тот самый выигрыш, о котором говорят бенчмарки, и на конкретной нагрузке он может отличаться от заявленных ~70%.

Итог

Ambient снимает главный аргумент, из-за которого команды годами откладывали внедрение service mesh: mTLS и базовая видимость трафика теперь стоят одного ztunnel на ноду, а не Envoy-процесса на каждый под, и включаются без единого перезапуска приложения. Плата за это — модель стала двухслойной, и придётся осознанно решать, каким сервисам нужен waypoint и L7, а каким хватит L4. Это честная сделка: сложность теперь пропорциональна тому, что реально нужно сервису, а не размазана поровну по всему кластеру.


Поделиться:

Следующая статья
Karpenter: ноды по требованию и счёт за кластер вдвое меньше