service mesh istio что это
Серия постов по Istio Service Mesh
Мы начинаем серию постов, в которой продемонстрируем некоторые из множества возможностей сервисной сетки Istio Service Mesh в сочетании с Red Hat OpenShift и Kubernetes.
Часть первая, сегодняшняя:
Инструментарий мониторинга и управления Istio – все необходимое для координации микросервисов в сервисной сетке service mesh.
Что такое сервисная сетка Istio
Сервисная сетка реализует для группы сервисов такие функции, как мониторинг трафика, контроль доступа, обнаружение, безопасность, отказоустойчивость и другие полезные вещи. Istio позволяет сделать все это без малейших изменений в коде самих сервисов. В чем секрет волшебства? Istio цепляет к каждому сервису свой прокси в виде sidecar-контейнера (sidecar – мотоциклетная коляска), после чего весь трафик к этому сервису идет через прокси, который, руководствуясь заданными политиками, решает как, когда и должен ли вообще этот трафик дойти до сервиса. Istio также дает возможность реализовать продвинутые техники DevOps, такие как canary deployments, circuit breakers, fault injection и многие другие.
Как Istio работает с контейнерами и Kubernetes
Сервисная сетка Istio – это sidecar’ная реализация всего того, что требуется для создания и управления микросервисами: мониторинг, трассировка, circuit breakers, маршрутизация, балансировка нагрузки, fault injection, повторы, тайм-ауты, зеркалирование, контроль доступа, ограничение скорости и многое другое. И хотя сегодня есть масса библиотек, чтобы реализовать эти функции непосредственно в коде, с Istio вы можете получить все то же самое, ничего не меняя в своем коде.
Согласно sidecar’ной модели, Istio выполняется в Linux-контейнере, который располагается в одном Kubernetes-pod’е с контролируемым сервисом и внедряет (inject) и извлекает (extract) функциональность и информацию согласно заданной конфигурации. Подчеркнем, это ваша собственная конфигурация, и она живет вне вашего кода. Поэтому код становится гораздое проще и короче.
Что еще важно, операционная составляющая микросервисов при этом оказывается никак не связана с самим кодом, а значит их эксплуатацию можно спокойно передать ИТ-специалистам. В самом деле, почему разработчик должен отвечать за circuit breaker’ы и fault injection? Реагировать – да, но обрабатывать их и создавать? Если убрать всё этого из кода, программисты смогут полностью сосредоточиться на прикладном функционале. Да и сам код станет короче и проще.
Сервисная сетка
Istio, реализующий функции управления микросервисами вне их кода – это и есть концепция сервисной сетки Service Mesh. Иначе говоря, это скоординированная группа из одного или нескольких бинарников, которые образуют сетку сетевых функций.
Как Istio работает с микросервисами
Вот как выглядит работа sidecar-контейенров с связке с Kubernetes и Minishift с высоты птичьего полета: запускаете экземпляр Minishift, создаете проект для Istio (назовем его «istio-system»), устанавливаете и запускаете все связанные с Istio компоненты. Затем, по мере создания проектов и pod’ов, добавляете конфигурационные сведения в свои deployment’’ы, и ваши pod’ы начинают использовать Istio. Упрощенно диаграмма выглядит так:
Теперь можно изменять настройки Istio, чтобы, например, организовать fault injection, поддержку Canary Deployment или другие возможности Istio – и все это совершенно не трогая код самих приложений. Допустим, вы хотите перенаправить весь веб-трафик от пользователей своего крупнейшего клиента (Foo Corporation) на новую версию сайта. Для этого достаточно создать правило маршрутизации Istio, которое будет искать @foocorporation.com в идентификаторе пользователя и выполнять соответствующее перенаправление. Для всех остальных пользователей ничего не изменится. А вы тем временем будете спокойно тестировать новую версию сайта. И заметьте, для этого совершенно не надо привлекать разработчиков.
И дорого за это придется заплатить?
Отнюдь. Istio работает довольно быстро, он написан на Go и создает совсем небольшой оверхед. Кроме того, возможный проигрыш в онлайн-производительности компенсируется приростом производительности труда разработчиков. По крайней мере, в теории: не забывайте, что время разработчиков стоит дорого. Что касается затрат на ПО, то Istio – это софт с открытым кодом, поэтому получить и использовать его можно бесплатно.
Освойте сами
Команда Red Hat Developer Experience Team разработала углубленное практическое руководство по Istio (на английском). Оно работает на Linux, MacOS и Windows, а код представлен в вариантах на Java и Node.js.
10 интерактивных занятий по Istio
Блок 1 — Для начинающих
Введение в Istio
30 минут
Знакомимся с Service Mesh, учимся устанавливать Istio в Kubernetes кластере OpenShift.
Начать
Развертывание микросервисов в Istio
30 минут
Используем Istio, чтобы развернуть три микросервиса с Spring Boot и Vert.x.
Начать
Блок 2 – средний уровень
Мониторинг и трассировка в Istio
60 минут
Изучаем встроенные средства мониторинга Istio, настраиваемые метрики, а также OpenTracing через Prometheus и Grafana.
Начать
Простая маршрутизация в Istio
60 минут
Учимся управлять маршрутизацией в Istio с помощью простых правил.
Начать
Расширенные правила маршрутизации
60 минут
Знакомимся с умной маршрутизацией в Istio, управлением доступом, балансировкой нагрузки и ограничением скорости.
Начать
Блок 3 – опытный пользователь
Fault Injection в Istio
60 минут
Изучаем сценарии обработки отказов в распределенных приложений, создавая ошибки HTTP и сетевые задержки, учимся применять хаос-инжиниринга для восстановления среды.
Начать
Circuit Breaker в Istio
30 минут
Устанавливаем Siege для стресс-тестирования сайтов и учимся обеспечить отказоустойчивость бэкенда с помощью повторов, circuit breaker и pool ejection.
Начать
Egress и Istio
10 минут
Используем маршруты Egress для создания правил взаимодействия внутренних сервисов с внешними API и сервисами.
Начать
Istio и Kiali
15 минут
Учимся использовать Kiali для получения общей картины сервисной сетки и изучения потоков запросов и данных.
Начать
Mutual TLS в Istio
15 минут
Создаем Istio Gateway и VirtualService, затем подробно изучаем mutual TLS (mTLS) и его настройки.
Начать
Блок 3.1 — Глубокое погружение: Istio Service Mesh для микросервисов
Серия статей по сервисным сеткам и Istio
Попробуйте сами
Эта серия постов не ставит целью обеспечить глубокое погружение в мир Istio. Мы просто хотим познакомить вас с самой концепцией и, может быть, вдохновить самостоятельно попробовать Istio. Это можно сделать совершенно бесплатно, и Red Hat предоставляет все необходимые инструменты, чтобы начать осваивать OpenShift, Kubernetes, Linux-контейнеры и Istio, а именно: Red Hat Developer OpenShift Container Platform, наше руководство по Istio и другие ресурсы на нашем микро-сайте по Service Mesh. Не стоит откладывать, начните прямо сегодня!
Правила маршрутизации Istio: направляем сервис-запросы туда, куда нужно
OpenShift и Kubernetes прекрасно справляются с тем, чтобы обращения к микросервисам маршрутизировались к нужным pod’ам. В этом и есть одна из целей существования Kubernetes – маршрутизация и балансировка нагрузки. А что, если вам нужна более тонкая и изощренная маршрутизация? Например, чтобы одновременно использовать две версии микросервиса. Как здесь помогут правила маршрутизации Istio Route Rules?
Правила маршрутизации – это правила, которые, собственно, задают выбор маршрута. При любом уровне сложности системы общий принцип работы этих правил остается простым: запросы маршрутизируются на основе определенных параметров и значений заголовков HTTP.
Посмотрим на примерах:
Kubernetes умолчанию: тривиальное «50 на 50»
В нашем примере мы покажем, как одновременно использовать в OpenShift две версии микросервиса, назовем их v1 и v2. Каждая версия запускается в собственном pod’е Kubernetes, и по умолчанию здесь работает равномерно сбалансированная циклическая маршрутизация (evenly balanced round robin routing). Каждый pod получает свою долю запросов по числу его экземпляров микросервиса, иначе говоря, реплик. Istio же позволяет поменять этот баланс вручную.
Допустим, мы развернули на OpenShift две версии нашего рекомендационного сервиса, recommendation-v1 и recommendation-v2.
На рис. 1 видно, что, когда каждый сервис представлен в одном экземпляре, запросы равномерно чередуются между ними: 1-2-1-2-… Именно так маршрутизация Kubernetes работает по умолчанию:
Взвешенное распределение между версиями
Игнор версии с помощью Istio
Istio позволяет легко изменить распределение запросов нужным нам образом. Например, отправлять весь трафик только на recommendation-v1 с помощью следующего yaml-файла Istio:
Здесь надо обратить внимание вот на что: pod’ы выбираются согласно меткам. В нашем примере используется метка v1. Параметр «weight: 100» означает, что 100% трафика будет маршрутизироваться на все pod’ы сервиса, у которых есть метка v1.
Директивное распределение между версиями (Canary Deployment)
Далее, используя параметр weight, можно направлять трафик к обоим pod’ам, игнорируя количество экземпляров микросервисов, запущенных в каждом из них. Например, здесь мы директивно направляем 90% трафика на v1 и 10% – на v2:
Отдельная маршрутизация мобильных пользователей
В заключение покажем, как принудительно маршрутизировать трафик мобильных пользователей на сервис v2, а всех остальных – на v1. Для этого мы помощью регулярных выражений анализируем значение user-agent в заголовке запроса:
Теперь ваша очередь
Пример с регулярными выражениями для анализа заголовков должен мотивировать вас на поиск собственных вариантов применения правил маршрутизации Istio. Тем более что возможности здесь открываются весьма обширные, поскольку значения заголовков можно формировать в исходном коде приложений.
И помните, что Ops, а не Dev
Всё, что мы показали в примерах выше, делается без малейших изменений в исходном коде, ну за исключением тех случаев, когда надо формировать особые заголовки запросов. Istio пригодится как разработчикам, которые, например, смогут применять его на этапе тестировании, так и специалистам по эксплуатации ИТ-систем, которым он сильно поможет в продакшне.
Так что повторим лейтмотив этой серии постов: вам не надо ничего менять в своем коде. Не нужно собирать новые образы или запускать новые контейнеры. Все это реализуется вне кода.
Включите воображение
Только представьте, какие перспективы открывает анализ заголовков с помощью регулярных выражений. Хотите перенаправлять вашего крупнейшего клиента на специальную версию своих микросервисов? Легко! Нужна отдельная версия для браузера Chrome? Не проблема! Вы можете маршрутизировать трафик практически по любой его характеристике.
Что ждать от внедрения Istio? (обзор и видео доклада)
Istio — частный случай «сервисной сетки» (Service Mesh), понятия, о котором наверняка все слышали, и многие даже знают, что это такое. Мой доклад на Kuber Conf 2021 (мероприятие Yandex.Cloud, которое проходило 24 июня в Москве) посвящен возможным проблемам, к которым надо готовиться при внедрении Istio. Среди прочего я рассказал о том, как Istio влияет на трафик, какие есть возможности для его мониторинга, насколько безопасен mTLS.
Доклад отчасти отражает наш опыт работы с Istio как с одним из компонентов Kubernetes-платформы Deckhouse, отчасти основан на результатах внутренних нагрузочных тестов.
30 минут) и основную выжимку в текстовом виде.
Что такое Service Mesh
Для начала синхронизируем понимание, что такое Service Mesh.
Допустим, у вас есть приложение, оно живет своей жизнью, развивается. Вы нанимаете новых программистов, от бизнеса поступают новые задачи. Нормальная эволюция.
При этом объемы сетевого трафика растут, и в этой ситуации вы просто не можете не столкнуться с задачами по его управлению. Разберем эти задачи подробнее.
Типовая сетевая задача №1
Возьмем небольшой кусочек архитектуры — фронтенд и бэкенд. С точки зрения кластера он выглядит как две группы Pod’ов, которые общаются друг с другом.
Предположим, одному из Pod’ов стало плохо — не терминально, но приложение глючит. Health check’и в Kubernetes этого не замечают: сохраняют Pod в балансировке, и трафик на него продолжает поступать. От этого становится только хуже.
Было бы здорово выявлять эти пограничные состояния, когда вроде бы почти всё окей, но все-таки что-то идет не так. А выявив — дать нашему Pod’у отдохнуть, временно перенаправив трафик на другие Pod’ы. Такой подход называется circuit breaking.
Типовая сетевая задача №2
Вы написали новую версию бэкенда, прошли все тесты, на stage бэкенд показал себя хорошо. Но прежде, чем катить его в production, хорошо бы обкатать на настоящем трафике. То есть выделить группу пользователей для новой версии бэкенда и протестировать его на этой группе.
Если тесты прошли успешно — выкатываем бэкенд в production и обновляем все Pod’ы. Такая стратегия деплоя называется canary deployment. (Подробнее о разных стратегиях деплоя в Kubernetes мы писали здесь.)
Типовая сетевая задача №3
Например, ваш кластер развернут в публичном облаке. Из соображений отказоустойчивости вы «размазали» Pod’ы по разным зонам. При этом фронтенд- и бэкенд-Pod’ы общаются друг с другом беспорядочно, не обращая внимание на зоны. В мире облачных платформ такое беспорядочное общение не будет для вас бесплатным. Например, в AWS это выльется в дополнительные финансовые расходы, в Яндекс.Облаке — в дополнительный latency.
Нужно сделать так, чтобы фронтенды выбирали такие бэкенды, которые находятся в их зоне, а остальные бэкенды оставались бы для них «запасными». Такой способ маршрутизации называется locality load balancing.
Решаем сетевые задачи систематически
Подобные задачи по управлению сетевым трафиком можно перечислять ещё долго и каждый хотя бы раз сталкивался хотя бы с одной из них. Безусловно, у каждой из задач есть решение, которое можно реализовать на уровне приложения. Но проблема в том, что среднестатистическое приложение состоит из множества компонентов, или «шестеренок». И все они разные, к каждой нужен свой подход.
Именно для систематического решения подобных задач и придумали Service Mesh.
Service Mesh дает набор «кирпичей», из которых мы можем собирать собственные паттерны управления сетью. По-другому: Service Mesh — это фреймворк для управления любым TCP-трафиком с собственным декларативном языком. А в качестве бонуса Service Mesh предлагает дополнительные возможности для мониторинга (observability).
Благодаря Service Mesh вам не нужно задумываться о нюансах сетевого взаимодействия на уровне отдельных компонентов.
Вы можете рассматривать ваше приложение просто как дерево компонентов с очень примитивными связями. А все нюансы вынести за скобки и описать их с помощью Service Mesh.
Как это работает
Представим, что мы — большие любители «велосипедов»: мы не ищем готовые решения, потому что любим писать свои. И мы решили написать свой Service Mesh — supermesh.
Предположим, у нас есть приложение. Оно принимает запросы, генерирует новые — вот этим трафиком мы и хотим управлять. Но чтобы им управлять, нам нужно его перехватить. Для этого:
проникаем в сетевое окружение приложения;
внедряем туда наш перехватчик;
DNAT’ом перенаправляем на него входящий и исходящий трафик.
Поскольку мы работаем с Kubernetes, наше приложение «живет» в Pod’e, то есть внутри контейнера. Это значит, что мы для удобства можем «подселить» к приложению наш перехватчик в качестве sidecar-контейнера.
Теперь с этим перехваченным трафиком нужно что-то сделать, как-то его модифицировать. Очевидное решение — использовать прокси (например, nginx, HAProxy или Envoy).
Мы можем написать и свой прокси, но мы не настолько велосипедисты, поэтому остановимся на Envoy. Он хорошо вписывается в эту архитектуру, потому что умеет конфигурироваться удаленно, «на лету», и у него есть множество готовых удобных API.
В итоге мы можем перехватить трафик и влиять на него. Осталось сделать с ним что-то полезное, то есть реализовать какой-то из упомянутых выше паттернов управления — причем не абы какой, а такой, который выберет разработчик нашего приложения.
Мы могли бы передать разработчику «пульт управления» нашими sidecar’ами, чтобы он сам настраивал все нюансы. Но изначально вроде бы не этого хотели: нам нужна систематизация.
Очевидно, что нам не хватает какого-то промежуточного компонента — условного контроллера (supermeshd), который взял бы на себя управление, а разработчику предоставил декларативный язык, с помощью которого тот строил бы свои стратегии.
Теперь мы можем взять от разработчика набор паттернов, которые он хочет, а контроллер будет бегать по sidecar’ам и настраивать их. В мире Service Mesh такой контроллер называется Control Plane, а sidecar’ы с нашим перехватчиком — Data Plane.
Именно по такому принципу — естественно, с большими допущениями — и работает большинство реализаций Service Mesh: Kuma, Istio, Linkerd и пр.
В этом докладе я рассматриваю только Istio и делаю это «без объяснения причин». Кстати, согласно прошлогоднему опросу CNCF, это самое популярное Service Mesh-решение, которое используется в production.
Перед внедрением Istio к нему сразу же возникает ряд вопросов:
Как он повлияет на приложение? А на кластер?
Какие у него возможности по observability?
Надежен ли его Mutual TLS?
Как правильно засетапить Istio? А обновить?
Что, если что-то сломается?
Конечно, вопросов гораздо больше. Постараюсь ответить хотя бы на некоторые из них.
Как Istio влияет на приложение
Раньше, когда не было Istio, всё было просто и прозрачно: пользователь генерирует запрос, фронтенд генерирует дочерний запрос к бэкенду.
Как только появляется Istio, схема усложняется:
По сравнению с чистой инсталляцией «хопов» стало гораздо больше. Понятно, что это не может быть бесплатным. И самая очевидная цена — это latency.
Разработчики Istio обещают, что задержка не будет превышать 2,65 мс. Мне эти цифры показались не очень убедительными, поэтому я решил сделать свои замеры.
Нагрузочное тестирование
Для начала я написал примитивное клиент-серверное приложение. В качестве клиента использовал утилиту для нагрузочного тестирования k6, а в качестве сервера — nginx, который просто отдает статичные файлы. k6 отправляла по 10 тыс. запросов с предварительным прогревом в сторону сервера. Важно: клиент и сервер работали на разных узлах, чтобы не мешать друг другу.
Из этого приложения я собрал несколько вариантов инсталляции:
«чистую» схему, без Istio и всяких sidecar’ов;
сетап с Envoy в качестве sidecar’а;
сетап, в котором просто включен Istio;
еще один сетап с Istio, в котором задействованы 1000 правил авторизации — чтобы понять, влияют ли какие-то дополнительные правила на задержку, и как именно.
Что еще я посчитал важным сделать:
отключил логи на sidecar’ах;
включил сбор метрик sidecar’ах Istio;
использовал разные версии TLS;
включал и выключал HTTP/2;
включал и выключал keepalive на стороне клиента и между sidecar’ами;
использовал JSON-файлы трех размеров: 500Б, 200КБ, 1,7МБ;
экспериментировал с многопоточностью.
В итоге получилось 252 теста. Попробуем оценить собранные метрики.
1. Классический сценарий: примитивный клиент отправляет запросы серверу в один поток и не умеет держать keepalive.
Посмотрим на картину задержек:
Самый первый и очевидный вывод: действительно, перехват трафика не бесплатен. Причем в случае с Envoy в качестве sidecar’а и «тяжелым» файлом мы видим пятикратный рост задержки.
Уровень драматичности снижается, если мы посмотрим на полный round-trip (RTD) запроса. В самом худшем случае мы увидим троекратный оверхэд. А в некоторых случаях он почти не заметен:
Важно учитывать, что в моем случае естественный фон задержки у бэкенда крайне мал — 0,23 мс. В этом случае прирост в 2 мс кажется огромным — 800%. Но если у вас более сложный бэкенд, у которого задержка составляет десятки мс, то эти лишние 2 мс вы не заметите.
Второй вывод: по сравнению с «голым» Envoy’ем Istio все-таки дает небольшие накладные расходы. Возможно, это связано со сбором метрик и это стоит учитывать.
Третий вывод: правила авторизации, как и любые другие дополнительные настройки Istio, влияют на latency. Но это тоже не должно смущать, т. к. в реальной жизни вряд ли кому-то понадобится тысяча дополнительных правил.
Четвертый вывод: рост задержки при росте размера файла. Здесь задержка растет так же предсказуемо, и все пропорции по сравнению с «голой» инсталляцией сохраняются. То есть можно считать, что объем данных, которые мы прогоняем через Istio, на задержку не влияет.
2. Сценарий с шифрованием Mutual TLS
Результаты оказались везде более-менее одинаковыми, поэтому я покажу их на примере одной инсталляции, когда просто включен Istio:
Да, влияние есть. Но, опять же, это «копейки», которые можно не учитывать. Так что шифрование можно смело включать.
3. Любопытные наблюдения
Они не связаны напрямую с Istio, но я подумал, что ими не помешает поделиться.
Я решил: а что, если включить keepalive на клиентах? Не выиграю ли я в задержке? Потому что, во-первых, handshakes. Во-вторых, sidecar’ам надо будет поменьше думать.
Результаты покажу на примере того же сетапа с Istio:
Выигрыш хоть и небольшой, но есть. Мелочь, а приятно!
А что, если еще включить многопоточность. И вот тут я расстроился: при тестах на «средневесе» и «тяжеловесе» задержка выросла.
Разбираясь с этой проблемой, я нашел убедительное объяснение в статье блога Cloudflare, почему keepalive — это не всегда хорошо.
Итак, два главных вывода о влиянии Istio на приложение:
перехват трафика не бесплатен — можно смело закладывать накладные расходы по задержке порядка 2,5 мс;
если ваша естественная задержка измеряется в десятках и сотнях мс, лишние 2,5 мс вы не заметите.
Как Istio влияет на кластер
У нашего Istio, как и у supermeshd, который мы ранее реализовали сами, тоже есть Control Plane. Называется он istiod, и это отдельный контроллер, который работает в кластере.
Также у Istio есть свой декларативный язык, с помощью которого можно строить паттерны по управлению сетью. Технически язык представляет собой набор ресурсов Kubernetes, за которыми istiod пристально следит. Еще он следит за набором системных ресурсов: Namespace’ами, Pod’ами, узлами и т. д. В этом смысле контроллер Istio ничем не отличается от любого другого контроллера или оператора. И на практике никаких проблем с ним мы не выявили. Можем идти дальше.
Observability
У Istio есть интеграция со сторонними инструментами для мониторинга. Например, с дашбордом Kiali, с помощью которого можно визуализировать приложение в виде графа, рассмотреть отдельные связи компонентов, как они устроены, что у них «болит» и т. д.
Есть хороший набор дашбордов для графиков Grafana.
Istio также предоставляет базовые возможности для внедрения трассировки на основе Jaeger.
К сожалению, ничего из этого у вас не заработает «из коробки». Для Kiali и Grafana нужно будет установить Prometheus (и саму Grafana). В случае с трассировкой придется, как минимум, установить где-то Jaeger, а как максимум — научить ваше приложение грамотно обрабатывать HTTP-заголовки со служебными данными от трассировки.
Безопасность (Mutual TLS)
Напомню, протокол Mutual TLS (mTLS) — это взаимная, или двусторонняя, аутентификация клиента и сервера. Он нужен, когда мы хотим, чтобы наши клиент и сервер достоверно знали друг друга, не общались со всякими «незнакомцами». Также mTLS нужен, когда мы хотим зашифровать трафик между нашими приложениями. Технически это достигается с помощью обычных SSL-сертификатов.
В мире Istio у каждого Pod’а есть сертификат, который подтверждает подлинность этого Pod’а, а именно — подлинность его идентификатора (ID). В терминологии Istio идентификатор — это principal. Он состоит из трех частей:
ID кластера (Trust Domain),
ServiceAccount’а, под которым работает Pod.
За выпуск сертификатов отвечает Control Plane, на основе собственного корневого сертификата. У каждой инсталляции Istio есть собственный корневой сертификат — root CA (его не стоит путать с корневым сертификатом самого кластера). На основе корневого сертификата выпускаются индивидуальные.
Давайте разберём весь жизненный цикл индивидуального сертификата.
Envoy в Istio не общается напрямую с Control Plane: он это делает через посредника, который называется Istio agent — утилита, написанная на Go, которая живет в одном контейнере с Envoy. Этот агент отвечает за ротацию сертификатов.
Istio-agent генерирует CSR с заявкой на ID (principal), который полагается нашему Pod’у. Но сначала этот ID нужно как-то сгенерировать. С Trust Domain всё просто: агент знает, в каком кластере работает Pod (переменная в ENV). С Namespace’ом и ServiceAccount’ом чуть сложнее…
Но вообще: что такое ServiceAccount? Остановимся на этом чуть подробнее.
ServiceAccount
У Kubernetes есть свой API, который подразумевает, что с ним никто анонимно общаться не будет. С другой стороны, API подразумевает, что к нему будут общаться Pod’ы. Рядовым приложениям это, как правило, не нужно: обычно с API Kubernetes общаются контроллеры, операторы и т. п. И чтобы решить этот вопрос, придумали специальные учетные записи, которые называются ServiceAccount.
ServiceAccount — это примитивный ресурс в K8s, который в момент создания никакой информации не несет. Он просто существует в каком-то Namespace’е, с каким-то именем. Как только такой ресурс появляется в кластере, Kubernetes на это реагирует и выпускает JWT-токен. Этот токен целиком описывает появившийся ServiceAccount. То есть Kubernetes подтверждает: «в моем кластере, в таком-то Namespace’е, с таким-то именем есть ServiceAccount» — и выдает специальный токен, который складывает в Secret ( mysa-token-123 ).
Secret привязывается к ServiceAccount’у, который тем самым становится чем-то вроде ресурса со свидетельством о рождении. Данный ServiceAccount можно «привязать» к любому Pod’у, после чего, в ФС Pod’а будет автоматически примонтирован Secret с токеном, причем по заранее известному пути ( /run/secret/kubernetes.io/serviceaccount/token ). Собственно, данный токен и пригодится нам для того, чтобы узнать Namespace, в котором мы работаем, и ServiceAccount, от имени которого мы работаем.
Мы сгенерировали principal — теперь можем положить его в CSR.
Полученный CSR мы можем отправить в istiod. Но чтобы istiod поверил, что мы — это мы, в дополнение отправляем токен. Далее istiod проверяет этот токен через API K8s ( TokenReview ). Если K8s говорит, что всё хорошо, istiod подписывает CSR и возвращает в sidecar.
Для каждого Pod’а ротация сертификатов происходит раз в сутки.
Чтобы что-то взломать в этой системе, можно:
украсть сертификат (он будет действовать не более суток);
украсть токен ServiceAccount’а Pod’а, чтобы заказывать сертификаты от имени этого Pod’а;
взломать API Kubernetes — тогда можно заказать сколько угодно токенов, от имени какого угодно Pod’а;
украсть корневой сертификат Control Plane — самый примитивный и опасный взлом.
Всё это на самом деле довольно сложно реализовать. Поэтому можем считать, что mTLS в Istio безопасен.
Другие вопросы к Istio
К сожалению, осталось еще много вопросов, не раскрытых в докладе. И по каждому из этих вопросов достаточно много нюансов. Порой эти нюансы либо очень плохо описаны в документации, либо не описаны вовсе — приходится ковыряться в коде и обращаться к разработчикам Istio.
Множество из этих нюансов мы учли в модуле Istio для нашей платформы Deckhouse. С ее помощью вы можете за 8 минут развернуть готовый к использованию Kubernetes-кластер на любой инфраструктуре: в облаке, на «голом железе», в OpenStack и т. д.
Подробнее познакомиться с функциями, которые решает Istio в рамках Deckhouse, можно в нашей документации.