Что за Istio, так ещё и в двух режимах, и зачем оно нам надо?
Вероятно, вы уже много раз читали про Istio и service mesh на habr или в других источниках, так что у кого уже аллергия, можно переходить к разделу «Миграция».
А кому интересно кратко опишу.
В кластерах k8s без Istio один сервис общается с другим напрямую, а в случае сбоя сервис должен сам обработать его: предпринять новую попытку, предусмотреть timeout, открыть circuit breaker и т.п.
Минимизация сбоя это хорошо, но безопасники не спят и хотят, чтобы все было секьюрно: сервисы общались по mtls и проходили аутентификацию и авторизацию. А SRE и DevOps хочет наблюдать за запросами между сервисами: метрики и трассировки.
И конечно у разработчиков нет времени на доработку сервисов (а их может быть много, как и команд развития), так как бэклог бизнес-задач забит под завязку.
Здесь появляется Sidecar, через который будет идти весь трафик между сервисами.

Istio предлагает специализированное решение, полностью отделенное от сервисов и функционирующее путём вмешательства в сетевое взаимодействие. И таким образом оно реализует:
Отказоустойчивость: опираясь на код статуса в ответе, оно понимает, произошел ли сбой в запросе, и выполняет его повторно.
Канареечные выкаты: перенаправляет на новую версию сервиса лишь фиксированное процентом число запросов.
Мониторинг и метрики: за какое время сервис ответил?
Трассировка и наблюдаемость: добавляет специальные заголовки в каждый запрос и выполняет их трассировку в кластере.
Безопасность: извлекает JWT-токен, аутентифицирует и авторизует пользователей.
Вот так, без доработки кода сервисов, мы можем закрыть множество «хотелок» различных подразделений, раскатив пару новых компонентов.
Мы с помощью Service mesh решали разные проблемы: метрики и трассировка, фичи на выходе из кластера (Service Entry), выполнений требований наших безопасников (PeerAuthentication и AuthorizationPolicy). Например, пункт (3.1.2) «Необходимо реализовать внутрикластерную аутентификацию микросервисов» реализуется через mtls посредством service mesh и его CA (PeerAuthentication). Или условие (пункт 3.3.1) «Должно быть включено повсеместное использование шифрования TLS (версии не ранее 1.2) между компонентами кластера и рабочей нагрузкой (микросервисами) между собой», — реализовали
приоритетным использованием mtls и service mesh (PeerAuthentication)
До отказоустойчивости и других фишек мы не дошли, но всё впереди :)
Вы, вероятно, сразу заметили оверхед, которым мы платим за такие функции. А именно ресурсами на каждый такой Sidecar и latency (так как появляется новое звено в прохождении трафика). В интернете можно встретить цифры 1-3 мс latency и 50-100 мб RAM и 0.05-0.2 CPU по ресурсам. В среднем, у нас были примерно такие же значения по ресурсам (RAM на некоторых сервисах достигал 50 мб, в среднем 20-30 мб). Значения могут меняться в зависимости от вашей специфики по нагрузки и накрученной логики mesh.
Так мы жили 2 года и все прекрасно работало. Но просто спокойно работать же скучно любознательным инженерам и..
Мы решили посмотреть на относительно новый режим Ambient от Istio
Отвечая на логичный вопрос «Зачем?»:
уменьшить потребление ресурсов,
уменьшить latency,
посмотреть на новую технологию, где разграничивается управление L4 и L7.
Что же это за Ambient режим? Istio Ambient Mesh — это новый режим DataPlane для Istio без Sidecar.

Не буду переписывать текст из документации, тем более что есть хорошая статья с примерами, но если коротко, суть в том, чтобы уйти от Sidecar в пользу двух новых компонентов ztunnel (L4) и waypoint (L7).
Ztunnel ставится на все ноды кластера k8s и перехватывает трафик на ней (т.е. теперь не в каждом поде по компоненту, а на каждой ноде).
Если нужны L7 фичи, то можно развернуть waypoint, который ставится и перехватывает трафик на уровне namespace (или можно настроить на конкретный сервис). Т.е. если вы используете только фичи L4 service mesh, то можно развернуть только ztunnel.
Если посмотреть на фичи, которые мы используем, то их вполне можно закрыть на L4 (требования безопасности согласовали использование Ambient и метрики L4). Трассировку (L7) команды в какой-то момент решили самостоятельно отправлять только по нужным сервисам (otel библиотека), что развязало нам руки.
Получается, что в теории мы должны уменьшить latency, использование ресурсов и, конечно, потрогать новую технологию.
Фух, вроде задачу поставили, приступаем к миграции.
Миграция с Istio Sidecar на Istio Ambient Mesh
№0. Первое, о чем хочётся написать, это про версии (это относится и к Sidecar).
ВСЕ ВЕРСИИ КОМПОНЕНТОВ ISTIO, в том числе и istio-base, istiod итд должны быть одинаковы!!!!! Если версии разные, могут возникать сайдэффекты.
Мы изначально ставили версию 1.26.2. Но лучше ставить последнюю стабильную, так как было исправлено много багов (ниже про это будет).
Также после перехода на Ambient mode сервисы начинают общаться по HBONE порту 15008. Это может повлиять на сервисы, у которых существуют Network Policy. Если в этих политиках прописаны определенные порты, то надо добавить к ним порт 15008. Если этого не сделать, в логах ztunnel будут ошибки вида:
error access connection complete src.addr=10.137.57.190:59234 src.workload="service1" src.namespace="namespace" src.identity="spiffe://cluster.local/ns/<ns>/sa/default" dst.addr=10.137.1.69:15008 dst.hbone_addr=10.137.1.69:2379 dst.workload="service2" dst.namespace="namespace" dst.identity="spiffe://cluster.local/ns/<ns>/sa/<sa>" direction="outbound" bytes_sent=0 bytes_recv=0 duration="22143304ms" error="connection timed out, maybe a NetworkPolicy is blocking HBONE port 15008
И эти ошибки могут привести к бесконечному открытию файловых дескрипторов (file descriptors), что, в свою очередь, приведёт к бесконечному росту памяти у ztunnel (ниже про это будет). Проблема актуальна для версии 1.26, исправлена в 1.28.
Примечание. Ниже будут приведены Values для версии 1.28.2.
№ 1. Установка istio-cni.
Возможно он у вас уже стоит, так как его можно использовать и в sidecar-моде (тогда нужно не забыть переключить profile в values, — смотри ниже).
Раньше сеть настраивалась с помощью istio-init, — этот контейнер поднимался с каждым подом для настройки сети. Теперь же есть istio-cni на каждой ноде, который выполняет настройку.
Мы нашу инфраструктуру накатываем через Argo, но, в целом, вы можете поставить руками через helm chart. Вот Values, которые мы используем:
profile: ambient
№ 2. Перекатываем istiod c values.
В коде ниже указано только то, что нужно добавить для перехода:
pilot: cni: enabled: true profile: ambient
№ 3. Устанавливаем сам ztunnel с Values.
Нам подходили дефолтные, мы лишь изменили ресурсы:
resources: requests: cpu: 200m memory: 512Mi limits: cpu: 500m memory: 768Mi
Если у вас, как и у нас, катится через Argo, то нужно ещё добавить код ниже, чтобы не было постоянного auto-sync:
ignoreDifferences: - group: "*" kind: DaemonSet name: ztunnel namespace: istio-system jsonPointers: - /metadata/annotations jqPathExpressions: # кстати вот именно это пригодилось на версии начиная с 1.27, на более ранней не надо, может в самом начале укажем версию для которой все эти Values - '.spec.template.spec.containers[] | select(.name=="istio-proxy").env[] | select(.name=="ZTUNNEL_CPU_LIMIT").valueFrom.resourceFieldRef.divisor'
Накатили — можно приступать к переключению
№ 4. Четвёртый шаг очень простой — нужно во всех namespace заменить labels:istio-injection: enabled на istio.io/dataplane-mode: ambient.
Далее следуют два этапа.
Первый — растартовать инфраструктурные поды. Если у вас, как и у нас, уже был включен PeerAuthentication, то включить PERMISSIVE на некоторые сервисы (которые вызывает kube-api через хуки). У нас это были:
kubernetes-dashboard: kubernetes-dashboard-metrics-scraper: port: 8000 selector: kubernetes-dashboard-metrics-scraper kubernetes-dashboard-metrics-server: port: 10250 selector: metrics-server monitoring: monitoring-vmselect: port: 8481 selector: vmselect external-secrets: external-secrets-webhook: port: 10250 selector: external-secrets-webhook cert-manager: cert-manager-webhook: port: 10250 selector: webhook
Пример манифеста:
apiVersion: security.istio.io/v1 kind: PeerAuthentication metadata: name: kubernetes-dashboard-metrics-scraper namespace: kubernetes-dashboard spec: mtls: mode: STRICT portLevelMtls: '8000': mode: PERMISSIVE selector: matchLabels: app.kubernetes.io/name: kubernetes-dashboard-metrics-scraper
Второй — выполнить рестарт бизнесовых сервисов.
Проверить, что на всех подах появилась аннотация:
ambient.istio.io/redirection=enabled
Команды, которые могут помочь:
# Список всех ns с Sidecar kubectl get ns -l istio-injection=enabled # Список всех ns без Ambient kubectl get ns -l 'istio.io/dataplane-mode notin (ambient)'
Мониторинг и алертинг
Отлично, мы выполнили миграцию, — как теперь это добро мониторить?
Мы создали себе небольшой дашборд на основе default (JSON с дашбордом). Но под Istio могут подойти и ваши текущие, так как метрики не менялись (но так как у нас только ztunnel, то и метрики только L4 уровня).
Алерта в рамках эксплуатации мы сделали два и оба по логам, а не метрикам.
Алерт № 1 на ошибку istio-system/istio_converted_static_strict. Ошибка говорит, что политики mtls заблокировали соединение.
Здесь мы отловили 2 кейса:
Нужно открыть
PERMISSIVEпорт.Один из сервисов (назначения или источника) не в Ambient, и трафик между ними не может шифроваться (или расшифроваться). Нужно проверить, входят ли оба сервиса в Ambient и работают ли через HBONE port 15 008 (подробности будут в разделе «Проблемы»).
namespace:("istio-system") AND container:("istio-proxy") AND log:("error") AND log:("istio-system/istio_converted_static_strict")
Алерт №2 на ошибку connection timed out, maybe a NetworkPolicy is blocking HBONE port 15008. Означает, что сетевая политика блокирует трафик (выше про неё уже писали). Она была очень актуальна, когда ztunnel тек по памяти из‑за политик (подробнее также в разделе «Проблемы»).
namespace:("istio-system") AND container:("istio-proxy") AND log:("error") AND log:("connection timed out, maybe a NetworkPolicy is blocking HBONE port 15008")
Проблемы после миграции
Вот мы и переехали, проблем не видим, считаем, что успех достигнут.
Однако..
№ 1. Через некоторое время замечаем, что у нас падают поды Ztunnel по ООМ. Похоже на утечку:

На GitHub есть issue «File descriptors leak», в которой обсуждается большое количество файловых дескрипторов у процесса ztunnel, а также их бесконечный рост. Проблемы у них начались после того, как они перевели ingress-nginx на Ambinet.
Мы также заметили, что рост есть у компонентов, где блокируется трафик из-за NetworkPolicy. После их добавления рост прекращался.
В итоге мы обновились на 1.28.2 и больше роста у нас не было.
№ 2. В один прекрасный момент у нас перестает работать взаимодействие извне кластера. Накануне вроде ничего такого не делали. Стали копать, смотреть логи и нашли ошибку на ztunnel при запросах с ingress:
connection failed: deadline has elapsed
Нашли и установили переменную окружения ENABLE_ORIG_SRC=false.
Если верить issue, то что-то мешает ztunnel выполнять его функции по перенаправлению запросов и мы отключаем подмену IP. Нам так и не удалось понять что это было:(
№ 3. В рамках одной системы у команды была цель 20к RPS. Они тестировали на Sidecar, но на их нагрузках выходили за необходимый latency (если не ошибаюсь, то Sidecar дал 12-13к). Перешли на Ambient, — стало лучше. Но когда дошли до 15к, возникла ошибка:
2025-07-02T15:25:52.617977Z error proxy::outbound:proxy{wl=datadog/datadog-agent-lxh24} Failed TCP handshake Too many open files (os error 24)
Причина в недостатке файловых дескрипторов, ведь ztunnel использует их на каждое открытое соединение. Ограничения по файловым дескрипторам в контейнерах наследуются по следующему принципу:
systemd (/etc/systemd/system.conf) → kubelet → containerd → container.
Поэтому оказалось, что у нас есть:
soft limit в 1024 (реальное ограничение по файловым дескрипторам у контейнера может расширяться до hard limit внутри процесса);
и hard limit в 524288 (максимальное количество до которого можно будет поднять soft limit внутри процесса).
Hard limit нас бы устроили, но не хотелось использовать костыли, а в Istio 1.26.2 не предусмотрено изменение soft limit.
В процессе исследования вопроса был найден аналогичный issue с последующим PR. Для использования нового функционала (теперь автоматически soft limit поднимался при создании контейнера до hard limit) нужно было обновиться минимум до 1.27 версии Istio, поэтому было принято решение обновляться до 1.27.3 (последняя стабильная версия перед переходом на следующий минорный выпуск).
В итоге команда успешно достигла 20к RPS с допустимым latency.
Результаты нагрузочного тестирования:

№ 4 После всех возможных приколов, которые удалось поймать, мы стали внимательнее смотреть за istio.
И стали замечать ошибки:
istio-system/istio_converted_static_strict
Удалось понять, что ошибка связана с тем, что в какой-то момент поды выходят из Service Mesh. Долго пытались понять триггер и оказалось, что после рестарта ноды (если не делать drain), поды на ней выходят из сетки. Завели issue. Как оказалось поды могут подняться раньше istio-cni и тогда они будут не в сетке.
Чинит такое поведение параметр для istio-cni:
ambient: reconcileIptablesOnStartup: true
№ 5. В логах увидели ошибку:
error proxy::h2 ping error: broken pipe
Стали смотреть по ней, нашли issue. Это баг, заключается в том, что закрытый пайп логируется как ошибка. Исправили в 1.29.2, но ещё не тестировали.
Итоги
В итоге мы успешно мигрировали все наши стенды на Ambient. Наша текущая версия 1.29.0. Планируем обновление на 1.29.2 для исправления п.5 выше.
Что же мы получили?
№ 1. В 1 проекте помогли уложиться в RPS и необходимый latency, чего нам не позволил бы Istio Sidecar (не исключаю, что мы тонко не настроили envoy).
№ 2. Уменьшили потребление по CPU и RAM. Посчитав на калькуляторе по нашим параметрам кластера...

...мне кажется, мы точно выиграли.
Реально у нас сейчас используется в NS istio-system 1.91 CPU и 11.2 Gb RAM. К сожалению данных по Sidecar до перехода не осталось.

№ 3. Уменьшили latency. Приведу значения результатов команды нагрузочного тестирования:

И наши, которые мы через k6 (инструмент для нагрузочного тестирования) выполняли, 1000 RPS:

Видно, что выигрыш есть, но для каждой нагрузки он будет свой.
№ 4. И да, мы успешно потрогали и заиспользовали новую технологию?
Пока нам не понадобятся функции L7, мы так и будем продолжать жить в такой конфигурации. Если появится потребность, то будем думать над развертыванием Waypoint или Sidecar для конкретных сервисов, так как возможна гибридная конфигурация Ambient+Sidecar.
Я сознательно упускал некоторые моменты, чтобы не раздувать статью и сфокусироваться именно на нашем опыте миграции. Если будут предложения по раскрытию какой либо темы, то пишите, попробуем реализовать.
Подписывайтесь на Телеграм-канал Alfa Digital — там рассказываем о работе в IT, делимся новостями, анонсами митапов и квартирников, рассказываем о технологиях, делимся советами наших экспертов, вакансиями и стажировками, иногда шутим.
Читайте также: