Istio 1.0: наконец в продакшн - mTLS по всему кластеру без правок в коде
С выходом Istio 1.0 перевели service mesh в продакшн: mTLS между всеми сервисами кластера, единая точка управления политиками трафика без правок в коде.
Istio 1.0 GA (31 июля 2018) - первый релиз с официальным production-ready статусом, стабильное API, поддержка мультикластерных конфигураций
31 июля вышел Istio 1.0 - и команда наконец-то объявила релиз production-ready. Мы следили за Istio с версии 0.8, когда ставили его на тестовый кластер и смотрели на mTLS и Kiali. Тогда в продакшн не потащили - Mixer под нагрузкой вызывал вопросы, и вообще нулевая мажорная версия в продакшне это спорное удовольствие. С 1.0 аргументов откладывать почти не осталось.
Перевод на managed-инфраструктуре - рабочий кластер Kubernetes, около десятка сервисов, HTTP и gRPC между ними. Задача была конкретная: mTLS между всеми сервисами кластера и единая точка управления политиками трафика - без изменений в коде приложений.
Что изменилось между 0.8 и 1.0
API стабилизировалось. Это звучит скучно, но на практике означает что манифесты, написанные под 1.0, не поедут при следующем патч-релизе. В 0.8 такой уверенности не было.
Из заметного по существу: улучшилась работа sidecar-инжектора - меньше случаев когда под поднимается, а sidecar ещё не готов и первые запросы падают. Стала стабильнее интеграция с Citadel: ротация сертификатов работает автоматически и без перезапуска подов. Документация подтянулась - стало меньше примеров с устаревшим API, которые в 0.8 встречались регулярно.
Mixer остался Mixer-ом. Архитектурно ничего не поменялось: каждый запрос через Envoy по-прежнему синхронно ходит в Mixer за policy-check-ом. Это известное узкое место, и в 1.0 оно никуда не делось. Спасает кэширование на стороне Envoy - включается в конфиге, существенно снижает количество реальных обращений к Mixer.
Установка и обновление
С тестового 0.8 мигрировали через полный переустанов - upgrade-пути между минорными версиями у Istio пока болезненные. Снесли старый namespace istio-system, задеплоили 1.0 через Helm-чарт, пересоздали поды в рабочих namespace. Downtime был - около десяти минут пока новые sidecar-ы не поднялись и mTLS не восстановился.
Автоинжектор сайдкаров работает через MutatingWebhookConfiguration. В 1.0 он стал надёжнее, но важный момент: если namespace получает аннотацию istio-injection: enabled уже после того как поды запущены - они не получат sidecar автоматически, нужен рестарт. Это не баг, это просто надо знать заранее и включать аннотацию до деплоя, а не после.
mTLS: строгий режим по всему кластеру
Основная цель - STRICT mTLS между всеми сервисами - достигается двумя манифестами:
MeshPolicy с mtls: {} включает mTLS в режиме STRICT глобально. После применения сервисы без sidecar теряют возможность принимать входящий трафик от сервисов со sidecar - Envoy отклоняет незашифрованные соединения.
DestinationRule с trafficPolicy.tls.mode: ISTIO_MUTUAL говорит Envoy на стороне клиента: при обращении к любому сервису использовать взаимный TLS с SPIFFE-сертификатами, которые выдал Citadel.
После применения обоих манифестов - весь межсервисный трафик внутри кластера идёт по шифрованному каналу с взаимной аутентификацией. В логах Envoy виден TLS handshake, в сертификатах - идентификаторы вида spiffe://cluster.local/ns/default/sa/svc-name. Разработчики со своей стороны не изменили ни строчки.
Один важный нюанс с которым столкнулись: healthcheck-и от kubelet идут не через sidecar, они plain HTTP напрямую к контейнеру. Если приложение само не слушает на нужном порту без sidecar - это всплывает именно при STRICT-режиме, когда хочется быть уверенным что весь трафик идёт через mesh. Надёжнее не полагаться на HTTP-probe, а использовать exec-probe или вынести healthcheck на отдельный порт вне перехвата iptables. Мы переключили на exec.
Наблюдаемость: Jaeger и Kiali в комплекте
В 1.0 Jaeger для распределённой трассировки и Kiali для графа трафика устанавливаются из того же Helm-чарта - не нужно искать отдельно. Включаются через values при установке.
Kiali с тех пор как мы его смотрели в 0.8 подрос: стало проще фильтровать по namespace, граф трафика обновляется быстрее. Jaeger интегрирован с Envoy через заголовки x-b3-traceid - это требует что приложение прокидывает эти заголовки дальше при обращении к другим сервисам. Если приложение их дропает - трейс разрывается. Пришлось добавить проброс заголовков в нескольких сервисах, это единственное изменение в коде которое потребовалось.
Как выглядит архитектура теперь
Раньше у нас была честная «мы думаем что знаем как сервисы общаются» ситуация. Теперь:
- каждый межсервисный вызов аутентифицирован и зашифрован - без отдельных библиотек в каждом сервисе
- политики трафика (таймауты, ретраи, circuit breaker) настраиваются в DestinationRule без деплоя приложения
- Kiali показывает реальный граф трафика, а не нарисованный вручную
- Jaeger даёт трейс запроса сквозь цепочку сервисов
Это не значит что стало легче жить в операционном смысле - Istio добавляет слой абстракции и соответствующий overhead при отладке. Когда что-то не так, сначала непонятно: это приложение, это Envoy, это Pilot не доставил конфиг, или это Citadel не обновил сертификат. Нужно знать куда смотреть.
Но архитектурная картина стала прозрачнее: есть одно место где определены политики безопасности сети, и это не раскиданные по репозиториям библиотеки.
Что пока вызывает вопросы
Mixer под высокой нагрузкой - по-прежнему открытый вопрос. На текущем трафике кэширование справляется, но запаса уверенности нет. Мониторим latency на входе в Envoy и на выходе из Mixer отдельно.
Control plane recovery - если Pilot недоступен, Envoy продолжает работать с последней известной конфигурацией. Это хорошо, но сколько эта конфигурация остаётся актуальной при активном деплое - вопрос который надо проверять, а не просто верить документации.
В целом: после нескольких недель в продакшне ничего критичного не случилось. Для нас это хороший знак.