Модуль устанавливает Istio.

Задачи, которые решает Istio

  • Mutual TLS:
    • Трафик между сервисами шифруется прозрачно для приложений при помощи SSL.
    • Сервисы автоматически аутентифицируют друг друга при помощи индивидуальных клиентских и серверных сертификатов.
    • Каждый сервис получает собственный идентификатор в формате /ns//sa/, где TrustDomain в нашем случае — это домен кластера. Каждому сервису можно выделять собственный ServiceAccount или использовать общий “default”. Полученный идентификатор сервиса можно использовать как в правилах авторизации, так и в прикладных целях.
  • Авторизация доступа между сервисами:
    • Для написания правил авторизации можно использовать аргументы:
      • идентификаторы сервисов (/ns//sa/),
      • namespace,
      • диапазоны IP,
      • HTTP-заголовки,
      • JWT-токены из прикладных запросов.
  • Circuit Breaker:
    • Выявление зависших запросов и обрывание их с кодом ошибки.
    • Исключение сервиса из балансировки если превышен лимит ошибок.
    • Настройка лимитов на количество TCP-соединений и запросов на endpoint сервиса.
  • Sticky Sessions:
    • Привязка запросов от конечных пользователей к endpoint сервиса.
  • Маршрутизация запросов:
    • Canary-deployment — отправить часть запросов на новую версию приложения.
    • Доступные аргументы для принятия решения о маршрутизации:
      • Host и любые другие заголовки,
      • uri,
      • Method (GET, POST и пр.).
  • Observability:
    • Сбор и визуализация данных для трассировки прикладных запросов с помощью Jaeger.
    • Сбор метрик о трафике между сервисами в Prometheus и визуализация их в Grafana.
    • Визуализация состояния связей между сервисами и состояния служебных компонентов Istio с помощью Kiali.

Архитектура прикладного сервиса с включенным Istio

Особенности

  • Каждый под сервиса получает дополнительный контейнер — sidecar-proxy. Технически этот контейнер содержит два приложения:
    • Envoy — проксирует прикладной трафик и реализует весь функционал, который предоставляет Istio, включая маршрутизацию, аутентификацию, авторизацию и пр.
    • pilot-agent — часть Istio, отвечает за поддержание конфигурации Envoy в актуальном состоянии, а также содержит в себе кеширующий DNS-сервер.
  • В каждом поде настраивается DNAT входящих и исходящих прикладных запросов в sidecar-proxy. Делается это с помощью дополнительного init-контейнера. Таким образом, трафик будет перехватываться прозрачно для приложений.
  • Так как входящий прикладной трафик перенаправляется в sidecar-proxy, то readiness/liveness-трафика это тоже касается. Подсистема Kubernetes, которая за это отвечает, не рассчитана на формирование проб в формате Mutual TLS. Для адаптации, все существующие пробы автоматически перенастраиваются на специальный порт в sidecar-proxy, который перенаправляет трафик на приложение в неизменном виде.
  • Для приёма запросов от пользователей или сторонних сервисов извне кластера, необходимо использовать подготовленный ingress-контроллер:
    • Поды контроллера аналогично имеют дополнительный контейнер sidecar-proxy.
    • В отличие от подов приложения, sidecar-proxy ingress-контроллера перехватывает только исходящий трафик от контроллера к сервисам. Входящий трафик от пользователей обрабатывает непосредственно сам контроллер.
  • Ресурсы типа Ingress требуют минимальной доработки в виде добавления аннотаций:
    • nginx.ingress.kubernetes.io/service-upstream: “true” — контроллер ingress-nginx в качестве upstream будет использовать ClusterIP сервиса вместо адресов подов. Балансировкой трафика между подами теперь занимается sidecar-proxy. Используйте эту опцию только если у вашего сервиса есть ClusterIP.
    • nginx.ingress.kubernetes.io/upstream-vhost: “myservice.myns.svc.cluster-dns-suffix” — sidecar-proxy ingress-контроллера принимает решения о маршрутизации на основе заголовка Host. Без данной аннотации, контроллер оставит заголовок с адресом сайта, например Host: example.com.
  • Ресурсы типа Service не требуют адаптации и продолжают выполнять свою функцию. Приложениям всё так же доступны адреса сервисов вида servicename, servicename.myns.svc и пр.
  • DNS-запросы изнутри подов прозрачно перенаправляются на обработку в sidecar-proxy:
    • Требуется для разыменования DNS-имён сервисов из соседних кластеров.

Жизненный цикл пользовательского запроса

Архитектура демонстрационного сервиса:

  • Namespace — myns.
  • Под foo:
    • Принимает запросы от пользователей и инициирует вторичные запросы на под bar.
    • Имеет соответствующий Service foo.
  • Под bar:
    • Принимает вторичные запросы от foo и обрабатывает их.
    • Имеет соответствующий Service bar.
  • Ingress настроен на публикацию сервиса foo через домен example.com.

resources

  1. Пользователь делает запрос на example.com и тот попадает напрямую в контейнер с ingress-контроллером. Действия контроллера:

    • по своей логике определяет, что запрос следует отправить на сервис foo из ns myns,
    • заменяет заголовок Host: на foo.myns.svc.cluster.local,
    • разыменовывает ClusterIP,
    • отправляет на него запрос.
  2. DNAT в поде перенаправляет запрос на sidecar-proxy.
  3. Sidecar-proxy:

    • благодаря исчерпывающему заголовку Host определяет расположение сервиса foo,
    • принимает решение о маршрутизации запроса на один из подов, которые обслуживают сервис,
    • принимает решение об авторизации запроса,
    • инициирует TLS-сессию с одним из них (в нашем случае под один) для того, чтобы в дальнейшем отправить через неё запрос.
  4. На входе в под, запрос перенаправляется в sidecar-proxy, который, в свою очередь, согласовывает TLS-сессию и принимает через неё запрос.
  5. Запрос достигает приложения foo.
  6. Приложение по своей логике инициирует вторичный запрос в сервис bar с неполным заголовком Host: bar. Для этого оно разыменовывает Service, подключается к полученному адресу, но запрос перенаправляется в sidecar-proxy.
  7. Sidecar-proxy:

    • Получает новый запрос и изучает заголовок Host дабы направить запрос по назначению. В данном случае Host — не fqdn, но прикладной sidecar-proxy в отличие от прокси ingress-контроллера, имеет возможность вычислить fqdn, добавив в него локальный namespace, что он и делает.
    • Принимает решение о маршрутизации на один из подов, которые обслуживают сервис bar.
    • Принимает решение об авторизации запроса.
    • Инициирует TLS-сессию с адресатом (под bar).
  8. На входе в под, запрос перенаправляется в sidecar-proxy, который:

    • согласовывает TLS-сессию и принимает через неё запрос,
    • принимает решение об авторизации запроса,
    • передаёт запрос в приложение.
  9. Запрос достигает приложения bar.

Как активировать Istio для приложения

Основная цель активации — добавить sidecar-контейнер “istio-proxy” на основе envoy к подам приложения, после чего через него начнет маршрутизироваться трафик и Istio сможет им управлять.

Рекомендованный способ добавления sidecar-ов — использовать sidecar-injector. Istio умеет “подселять” к вашим подам sidecar-контейнер с помощью механизма Admission Webhook. Настраивается с помощью лейблов и аннотаций:

  • Лейбл к namespace — обращает внимание sidecar-injector-а на ваш namespace, после установки лейбла, к новым подам будут подселяться sidecar-ы:
    • istio-injection=enabled — использовать самую свежую установленную версию Istio.
    • istio.io/rev=v1x8x1 — использовать конкретную версию Istio для данного namespace.
  • Аннотация к подуsidecar.istio.io/inject ("true" или "false"), позволяет локально переопределить политику sidecarInjectorPolicy. Эти аннотации работают только в namespace, обозначенных лейблами из списка выше.

Важно знать! Istio-proxy, который работает в качестве sidecar-контейнера тоже потребляет ресурсы и добавляет оверхед:

  • Каждый запрос DNAT-ится в envoy, который обрабатывает реквест и создаёт ещё один. На принимающей стороне аналогично.
  • Каждый envoy хранит информацию обо всех сервисах в кластере, что требует памяти. Больше кластер — больше памяти потребляет envoy. Решение — CustomResource Sidecar.

Архитектура кластера с включенным Istio

Компоненты кластера делятся на две категории:

  • control plane — управляющие и обслуживающие сервисы,
  • data plane — прикладная часть Istio, представляет собой контейнеры sidecar-proxy.

resources

Все сервисы из data plane группируются в mesh. Его характеристики:

  • Общее пространство имён для генерации идентификатора сервиса в формате /ns//sa/. Каждый mesh имеет идентификатор TrustDomain, который в нашем случае совпадает с доменом кластера. Например: mycluster.local/ns/myns/sa/myapp.
  • Сервисы в рамках одного mesh имеют возможность аутентифицировать друг друга с помощью доверенных корневых сертификатов.

Элементы control plane:

  • istiod — ключевой сервис, его задачи:
    • непрерывная связь с API Kubernetes и сбор информации о прикладных сервисах,
    • обработка и валидация с помощью механизма Kubernetes Validating Webhook всех Custom Resources, которые связаны с Istio,
    • компоновка конфигурации для каждого sidecar-proxy индивидуально:
      • генерация правил авторизации, маршрутизации, балансировки и пр.,
      • распространение информации о других прикладных сервисах в кластере,
      • выпуск индивидуальных клиентских сертификатов для организации схемы Mutual TLS. Эти сертификаты не связаны с сертификатами, которые использует и контролирует сам Kubernetes для своих служебных нужд.
    • автоматическая подстройка манифестов, определяющих прикладные поды через механизм Kubernetes Mutating Webhook:
      • внедрение дополнительного служебного контейнера sidecar-proxy,
      • внедрение дополнительного init-контейнера для адаптации сетевой подсистемы (настройка DNAT для перехвата прикладного трафика),
      • перенаправление readiness и leaveness-проб через sidecar-proxy.
  • operator — компонент, отвечающий за установку всех ресурсов, необходимых для работы control plane определённой версии.
  • kiali — панель управления и наблюдения за ресурсами Istio и пользовательскими сервисами под управлением Istio. Позволяет:
    • Визуализировать связи между сервисами.
    • Диагностировать проблемные связи между сервисами.
    • Диагностировать состояние control plane. Для приёма пользовательского трафика требуется доработка ingress-контроллера:
  • К подам контроллера добавляется sidecar-proxy, который обслуживает только исходящий трафик в сторону прикладных сервисов.
  • Если прикладной сервис находится под управлением Istio, sidecar-proxy устанавливает с ним Mutual TLS соединение.
  • Если прикладной сервис не находится под управлением Istio, соединение с ним устанавливается в незашифрованном виде. Контроллер istiod и каждый контейнер sidecar-proxy экспортируют собственные метрики, которые собирает кластерный Prometheus.

Mutual TLS

Данный механизм — это главный метод взаимной аутентификации сервисов. Принцип основывается на том, что при всех исходящих запросах проверяется серверный сертификат, а при входящих — клиентский. После проверок, sidecar-proxy получают возможность идентифицировать удалённый узел и использовать эти данные для авторизации, либо в прикладных целях. Каждый кластер имеет глобальную настройку Mutual TLS, предусмотрено несколько режимов работы:

  • Off — Mutual TLS выключен совсем.
  • MutualPermissive — входящие соединения принимаются как в шифрованном виде, так и в классическом. Исходящие соединения сервисов под управлением Istio устанавливаются в шифрованном виде.
  • Mutual — как входящие, так и исходящие соединения принимаются и устанавливаются только в шифрованном виде. Данные настройки можно переопределить на уровне Namespace.

Авторизация и алгоритм принятия решения

Управление авторизацией осуществляется с помощью ресурса AuthorizationPolicy. В момент, когда для сервиса создаётся этот ресурс, начинает работать следующий алгоритм принятия решения о судьбе запроса:

  • Если под запрос есть правило DENY — запретить.
  • Если для данного сервиса нет политик ALLOW — разрешить запрос.
  • Если запрос попадает под политику ALLOW — разрешить запрос.
  • Все остальные запросы — запретить. Иными словами, если явно что-то запретить, то работает только запрет. Если же что-то явно разрешить, то теперь разрешены только явно одобренные запросы (запреты, при этом, имеют приоритет). Для написания правил авторизации можно использовать аргументы:
  • идентификаторы сервисов и wildcard на их основе (mycluster.local/ns/myns/sa/myapp или mycluster.local/*),
  • namespace,
  • диапазоны IP,
  • HTTP-заголовки,
  • JWT-токены из прикладных запросов.

Федерация и мультикластер

Поддерживается две схемы межкластерного взаимодействия:

  • федерация
  • мультикластер

Принципиальные отличия:

  • Федерация объединяет суверенные кластеры:
    • у каждого кластера собственное пространство имён (для Namespace, Service и пр.),
    • у каждого кластера собственная сетевая инфраструктура и произвольные адресные диапазоны (podSubnetCIDR и serviceSubnetCIDR),
    • доступ к отдельным сервисам между кластерами явно обозначен.
  • Мультикластер объединяет созависимые кластеры:
    • сетевая связность между кластерами плоская — поды разных кластеров имеют взаимный прямой доступ,
    • пространство имён у кластеров общее — каждый сервис доступен для соседних кластеров так, словно он работает на локальном кластере (если это не запрещают правила авторизации).

Федерация

Общие принципы

resources

  • Федерация требует установления взаимного доверия между кластерами. Соответственно, для установления федерации, нужно в кластере A сделать кластер Б доверенным, и в кластере Б сделать кластер А доверенным. Технически это достигается взаимным обменом корневыми сертификатами.
  • Для прикладной эксплуатации федерации необходимо также обменяться информацией о публичных сервисах. Чтобы опубликовать сервис bar из кластера Б в кластере А, необходимо в кластере А создать ресурс ServiceEntry, который определяет публичный адрес ingress-gateway кластера Б.

Включение федерации

При включении федерации (параметр модуля istio.federation.enabled = true) происходит следующее:

  • В кластер добавляется сервис ingressgateway, чья задача проксировать mTLS-трафик извне кластера на прикладные сервисы.
  • В кластер добавляется сервис, который экспортит метаданные кластера наружу:
    • корневой сертификат Istio (доступен без аутентификации),
    • список публичных сервисов в кластере (доступен только для аутентифицированных запросов из соседних кластеров),
    • список публичных адресов сервиса ingressgateway (доступен только для аутентифицированных запросов из соседних кластеров).

Управление федерацией

resources

Для автоматизации процесса федерации, в рамках deckhouse реализован специальный контроллер. Алгоритм установления доверия с следующий:

  • Доверяемый кластер (cluster-b):
    • Местный контроллер собирает мета-информацию о кластере и (1) публикует её через стандартный Ingress:
      • (1a) публичная часть корневого сертификата,
      • (1b) список публичных сервисов в кластере (публичный сервис обозначается специальным лейблом federation.istio.deckhouse.io/public-service=),
      • (1c) публичные адреса ingress-gateway.
  • Доверяющий кластер (cluster-a):
    • Контроллер доверяющего кластера необходимо проинструктировать о доверяемом кластере с помощью специального ресурса IstioFederation (2), который описывает:
      • (2a) доменный префикс удалённого кластера,
      • (2b) URL, где доступна вся метаинформация об удалённом кластере (описание метаданных выше).
    • Контроллер забирает (3) метаданные по URL и настраивает локальный Istio:
      • (3a) добавляет удалённый публичный корневой сертификат в доверенные,
      • (3b) для каждого публичного сервиса из удалённого кластера он создаёт соответствующий ресурс ServiceEntry, который содержит исчерпывающую информацию о координатах сервиса:
        • hostname сервиса, который состоит из комбинации имени и namespace сервиса в удалённом кластере (3с), а также из доменного суффикса кластера (3d),
        • (3e) публичный IP удалённого ingress-gateway.

Для установления взаимного доверия, данный алгоритм необходимо реализовать в обе стороны. Соответственно, для построения полной федерации, необходимо:

  • В каждом кластере создать набор ресурсов IstioFederation, которые описывают все остальные кластеры.
  • Каждый ресурс, который считается публичным, необходимо пометить лейблом federation.istio.deckhouse.io/public-service=.

Мультикластер

Общие принципы

resources

  • Мультикластер требует установления взаимного доверия между кластерами. Соответственно, для построения мультикластера, нужно в кластере A сделать кластер Б доверенным, и в кластере Б сделать кластер А доверенным. Технически это достигается взаимным обменом корневыми сертификатами.
  • Для сбора информации о соседних сервисах, Istio подключается напрямую к apiserver соседнего кластера. Данный модуль берёт на себя организацию соответствующего канала связи.

Включение мультикластера

При включении мультикластера (параметр модуля istio.multicluster.enabled = true) происходит следующее:

  • В кластер добавляется прокси для публикации доступа к apiserver посредством стандартного Ingress:
    • Доступ через данный публичный адрес ограничен корневыми сертификатами Istio доверенных кластеров. Клиентский сертфикат должен содержать Subject: CN=deckhouse.
    • Непосредственно прокси имеет доступ на чтение к ограниченному набору ресурсов.
  • В кластер добавляется сервис, который экспортит метаданные кластера наружу:
    • Корневой сертификат Istio (доступен без аутентификации),
    • Публичный адрес, через который доступен apiserver (доступен только для аутентифицированных запросов из соседних кластеров).

Управление мультикластером

resources

Для автоматизации процесса сбора мультикластера, в рамках deckhouse реализован специальный контроллер. Алгоритм установления доверия с следующий:

  • Доверяемый кластер (cluster-b):
    • Местный контроллер собирает мета-информацию о кластере и (1) публикует её через стандартный Ingress:
      • (1a) публичная часть корневого сертификата,
      • (1b) публичный адрес, через который доступен apiserver (доступ ограничен правами на чтение ограниченного набора ресурсов и открыт только для клиентов с сертификатом, который подписан корневым сертификатом Istio и с CN=deckhouse),
  • Доверяющий кластер (cluster-a):
    • Контроллер доверяющего кластера необходимо проинструктировать о доверяемом кластере с помощью специального ресурса IstioMulticluster (2), который описывает:
      • (2a) URL, где доступна вся метаинформация об удалённом кластере (описание метаданных выше).
    • Контроллер забирает (3) метаданные по URL и настраивает локальный Istio:
      • (3a) добавляет удалённый публичный корневой сертификат в доверенные,
      • (3b) создаёт kubeconfig для подключения к удалённому кластеру через публичный aдрес.
  • После чего, доверяющий istiod знает, как достучаться до api соседнего кластера (4). Но доступ он получит только после того, как симметричный ресурс IstioMulticluster будет создан на стороне доверяемого кластера (5).
  • При взаимном доверии, сервисы общаются друг с другом напрямую (6). Для установления взаимного доверия, данный алгоритм необходимо реализовать в обе стороны. Соответственно, для сборки мультикластера, необходимо:
  • В каждом кластере создать набор ресурсов IstioMulticluster, которые описывают все остальные кластеры.