Istio 0.5 в тестовом K8s-кластере: mTLS и traffic shifting - первые выводы
Разворачиваем Istio 0.5 в тестовом Kubernetes-кластере: настраиваем mTLS между сервисами и пробуем traffic shifting. Что такое service mesh на практике и чего стоит sidecar.
Istio 0.5 - релиз service mesh для Kubernetes с mTLS, traffic management и observability
Istio 0.5 вышел в начале февраля, и мы наконец выделили время нормально поковыряться с ним в тестовом кластере. Разговоров про service mesh последние полгода много, но разница между «слышали концепцию» и «запустили и потрогали» оказалась значительной.
Что такое Istio и зачем оно
Идея service mesh - вынести сетевую логику из приложения в инфраструктурный слой. Retry, timeout, circuit breaker, mTLS, трассировка запросов - всё это приложение обычно реализует само, в коде. Istio предлагает другую модель: рядом с каждым подом запускается sidecar-прокси (Envoy), и весь трафик идёт через него. Приложение ничего не знает, логика - в прокси.
Звучит красиво. Посмотрим как оно на практике.
Установка 0.5: Helm или руками
Мы ставили через Helm chart - в 0.5 это основной рекомендованный способ. В кластер разворачивается несколько компонентов:
- Pilot - управляет конфигурацией Envoy-прокси, раздаёт правила маршрутизации
- Mixer - принимает телеметрию и применяет политики (rate limiting, ACL)
- Istio-CA - управляет сертификатами для mTLS
- Ingress - отдельный под для входящего трафика
Сам Envoy-sidecar инжектируется в поды двумя способами: вручную через istioctl kube-inject или автоматически через MutatingAdmissionWebhook (в 0.5 это ещё alpha). Мы использовали ручной вариант - надёжнее и понятнее что происходит.
Первая установка заняла минут сорок с учётом возни с RBAC. В Kubernetes 1.9 RBAC включён по умолчанию, и часть Istio-компонентов требует специфических ClusterRole - это не всегда очевидно из документации 0.5.
mTLS между сервисами
Первое что хотелось проверить - mutual TLS между сервисами без изменений в коде приложения. В Istio это делается через MeshPolicy и DestinationRule.
Включаем mTLS для всего mesh:
apiVersion: config.istio.io/v1alpha2
kind: MeshPolicy
metadata:
name: default
spec:
peers:
- mtls: {}
И говорим клиентам использовать TLS при обращении к сервисам через DestinationPolicy:
apiVersion: config.istio.io/v1alpha2
kind: DestinationPolicy
metadata:
name: default
spec:
destination:
name: "*.svc.cluster.local"
trafficPolicy:
tls:
mode: MUTUAL
После применения - трафик между подами с sidecar идёт по mTLS. Проверяем через istioctl get destinationpolicies и прямо из пода через openssl s_client - сертификаты в канале выданы Istio-CA, rotation происходит автоматически. На тестовом стенде с тремя сервисами всё заработало без изменений в коде приложений.
Ограничение 0.5: поды без sidecar (системные компоненты kube-system, например) могут иметь проблемы при строгом mTLS - надо явно исключать или настраивать permissive mode. Это временами неочевидно.
Traffic shifting: канареечные деплойменты
Второй кейс который нас интересовал - управление трафиком между версиями. В чистом Kubernetes канарейку делают через соотношение реплик: 9 v1 + 1 v2 = 10% трафика на v2. Грубо и через количество подов.
Istio позволяет задать вес напрямую через RouteRule:
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-v2-rollout
spec:
destination:
name: reviews
route:
- labels:
version: v1
weight: 80
- labels:
version: v2
weight: 20
Здесь labels - это label selector подов нужной версии. 80% трафика на v1, 20% на v2, независимо от количества реплик каждой версии. Меняешь weight - меняется распределение, без изменения деплойментов.
На тестовом стенде это работает именно так как написано. Если нужно откатиться - меняешь weight на 100/0 и применяешь. Для сравнения: в нашем текущем подходе к канарейкам через количество реплик это была бы операция с деплойментами.
Накладные расходы на sidecar: честно
Тут надо говорить без прикрас. Каждый под получает дополнительный контейнер Envoy. На нашем тестовом стенде с простыми HTTP-сервисами:
- Память - Envoy просит ~50-60 MB per pod в тестовых условиях. На кластере с сотней подов это несколько гигабайт только на прокси.
- CPU - при умеренной нагрузке почти незаметно. При высоком RPS - надо мерять, мы пока не нагружали серьёзно.
- Latency - дополнительный hop через localhost через iptables rules. На наших тестах добавляет единицы миллисекунд. Для большинства сервисов - нормально, для latency-sensitive - надо проверять.
- Сложность - это главное. Дебажить проблемы с сетью теперь надо с пониманием что трафик идёт через Envoy.
kubectl logsне даёт полной картины, нуженistioctl.
Mixer в 0.5 - отдельная история. Он участвует в каждом запросе для применения политик, и это синхронный вызов. При высокой нагрузке Mixer может стать узким местом. В 0.5 есть возможность кешировать политики на стороне Envoy, но конфигурируется это вручную.
Что мы из этого вынесли
Концепция работает, но порог входа высокий. Istio 0.5 - это не «поставил и забыл». Это дополнительный операционный слой с собственными CRD, собственным CLI, собственной моделью отладки.
Для простого кластера из трёх-пяти сервисов - вероятно избыточно. Для крупного продукта где десятки сервисов говорят друг с другом, и нужны mTLS, наблюдаемость трафика, канарейки на уровне трафика - история интереснее.
На managed-кластерах мы пока смотрим на это как на инструмент для отдельных сложных случаев, а не как на стандартный компонент. Нужно больше практики с 0.5 прежде чем рекомендовать это в production. Плюс хочется посмотреть как разовьётся Mixer - он сейчас выглядит как потенциальное узкое место при серьёзных нагрузках.
Следующий шаг - прогнать нагрузочный тест на тестовом стенде и замерить реальный overhead Envoy при разных RPS. Результаты опубликуем.
- Kubernetes 1.9: Workloads API в GA - планируем миграцию stateful-сервисов · 23 января 2018
- Prometheus 2.0: новый TSDB и план миграции с 1.x без боли · 2 февраля 2018