UncategorizedгаджетыНовостиразработчиктехнология

Внедрение Microservicilites с Istio

Ключевые выводы

  • При разработке архитектуры микросервисов необходимо решить ряд новых задач, таких как масштабируемость, безопасность и наблюдаемость.
  • Микросервисы предоставляют список сквозных проблем для правильной реализации микросервисов.
  • Kubernetes – хорошее начало для реализации этих микросервисов, но есть некоторые пробелы.
  • Сервисная сеть – это выделенный уровень инфраструктуры, обеспечивающий безопасную, быструю и надежную связь между сервисами.
  • Istio – это сервисная сетка, реализующая некоторые из требуемых микросервисов неинвазивным способом.

В архитектуре микросервисов приложение состоит из нескольких взаимосвязанных сервисов, где все они работают вместе для создания необходимых бизнес-функций. Итак, типичная архитектура корпоративных микросервисов выглядит так:

Вначале может показаться, что легко реализовать приложение с использованием микросервисной архитектуры. Но сделать это должным образом – непростая задача, поскольку есть некоторые проблемы, которых не было в монолитной архитектуре. Некоторые из них – это отказоустойчивость, обнаружение служб, масштабирование, ведение журнала или трассировка, и это лишь некоторые из них.

Чтобы решить эти проблемы, каждый микросервис должен реализовывать то, что мы в Red Hat назвали «микросервисами». Термин относится к списку сквозных проблем, которые служба должна реализовать помимо бизнес-логики для решения этих проблем.

Эти проблемы суммированы на следующей диаграмме:

Бизнес-логика может быть реализована на любом языке (Java, Go, JavaScript) или в любом фреймворке (Spring Boot, Quarkus), но вокруг бизнес-логики также должны быть реализованы следующие проблемы:

API: Служба доступна через определенный набор операций API. Например, в случае RESTful Web API в качестве протокола используется HTTP. Более того, API можно задокументировать с помощью таких инструментов, как Swagger.

Открытие: Сервисам нужно открыть для себя другие сервисы.

Призыв: После обнаружения службы ее необходимо вызвать с набором параметров и, при необходимости, вернуть ответ.

Эластичность: Одна из важнейших особенностей микросервисной архитектуры заключается в том, что каждая из служб является эластичной, то есть ее можно увеличивать и / или уменьшать независимо в зависимости от некоторых параметров, таких как критичность системы, или в зависимости от текущей рабочей нагрузки.

Устойчивость: В микросервисной архитектуре мы должны разрабатывать с учетом сбоев, особенно при взаимодействии с другими сервисами. В монолитном приложении приложение в целом работает или не работает. Но когда это приложение разбито на микросервисную архитектуру, оно состоит из нескольких сервисов. Все они связаны сетью, что означает, что некоторые части приложения могут работать, а другие выходить из строя. Важно ограничить сбой, чтобы избежать распространения ошибки через другие службы. Отказоустойчивость (или отказоустойчивость приложения) – это способность приложения / службы реагировать на проблемы и при этом обеспечивать наилучший результат.

Трубопровод: Службу следует развертывать независимо, без какой-либо оркестровки развертывания. По этой причине у каждой службы должен быть свой конвейер развертывания.

Аутентификация: Одним из важнейших аспектов архитектуры микросервисов является аутентификация / авторизация вызовов между внутренними службами. Веб-токены (и токены в целом) являются предпочтительным способом безопасного представления требований между внутренними службами.

логирование: Регистрация в монолитных приложениях проста, поскольку все компоненты работают в одном узле. Компоненты теперь распределены по нескольким узлам в виде сервисов; следовательно, требуется единая система регистрации / сборщик данных, чтобы иметь полное представление о трассировках регистрации.

Мониторинг: Метод для измерения производительности вашей системы, понимания общего состояния приложения или предупреждения, когда что-то не так, для обеспечения правильной работы приложения на основе микросервисов. Мониторинг – ключевой аспект управления приложением.

Отслеживание: Трассировка используется для визуализации хода выполнения программы и обработки данных. Это особенно полезно, когда мы, как разработчик / оператор, должны проверять, как пользователь проходит через все приложение.

Kubernetes становится де-факто инструментом для развертывания микросервисов. Это система с открытым исходным кодом для автоматизации, оркестровки, масштабирования и управления контейнерами.

Однако при использовании Kubernetes покрываются только три из десяти микросервисов.

Открытие реализован с концепцией Kubernetes Service. Он предоставляет способ группировки модулей Kubernetes (действующих как единое целое) со стабильным виртуальным IP-адресом и DNS-именем. Обнаружение службы – это просто вопрос выполнения запросов с использованием имени службы Kubernetes в качестве имени хоста.

Призыв сервисов легко с Kubernetes, поскольку платформа предоставляет сеть, необходимую для вызова любых сервисов.

Эластичность (или масштабирование) – это то, что Kubernetes имел в виду с самого начала. Например, выполнение команды kubectl scale deployment myservice --replicas=5, развертывание myservice масштабируется до пяти реплик или экземпляров. Платформа Kubernetes заботится о поиске нужных узлов, развертывании службы и постоянном поддержании желаемого количества реплик в рабочем состоянии.

А как насчет остальных микросервисов? Kubernetes охватывает только три из них, так как же реализовать остальные?

В первой части этой серии статей я рассмотрел реализацию этих проблем путем встраивания их в службу с использованием Java.

Сервис с сквозными проблемами, реализованными внутри одного и того же кода, выглядит как следующая диаграмма:

Этот подход работает и имеет несколько преимуществ, как показано в предыдущей статье, но у него есть некоторые недостатки. Назовем основные из них:

  • Базовый код сервиса представляет собой сочетание бизнес-логики (что дает компании ценность) и кода инфраструктуры (необходимого из-за микросервисов).
  • Сервисы в архитектуре микросервисов могут разрабатываться на разных языках, например, сервис A в Java и сервис B в Go. Сложность использования полиглотовых сервисов состоит в том, чтобы научиться реализовывать эти микросервисы для каждого языка. Например, какую библиотеку можно использовать для реализации отказоустойчивости в Java, Go и т. Д.
  • В случае Java мы можем добавить новые библиотеки (со всеми его транзитивными зависимостями) для каждой из «микросервисов», таких как Resiliency4J для устойчивости, Jaeger для трассировки или Micrometer для мониторинга. Хотя в этом нет ничего плохого, мы увеличиваем шансы возникновения конфликтов пути к классам при добавлении различных типов библиотек в путь к классам. Кроме того, увеличивается потребление памяти и время загрузки. И последнее, но не менее важное: существует проблема поддержания версий библиотек, согласованных для всех служб Java, поэтому все они работают с одной и той же версией.

Итак, подойдя к этому моменту, мы можем задаться вопросом, зачем нам нужно реализовывать все эти микросервисы?

В архитектуре микросервисов приложение состоит из нескольких взаимосвязанных сервисов, где все они работают вместе для создания необходимых бизнес-функций. Все эти сервисы связаны между собой с помощью сети, поэтому мы эффективно реализуем модель распределенных вычислений. А поскольку он распределен, наблюдаемость (мониторинг, отслеживание, ведение журнала) становится немного сложнее, поскольку все данные распределяются по нескольким службам. Поскольку сеть ненадежна или задержка не равна нулю, сервисы должны быть устойчивыми к этим сбоям.

Итак, предположим, что микросервисы необходимы из-за решений, принимаемых на уровне инфраструктуры (распределенные службы, взаимодействующие с использованием сети, в отличие от монолита). Почему нам нужно реализовывать эти микросервисы на уровне приложений, а не на уровне инфраструктуры? В этом и заключается проблема. Это справедливый вопрос, на который легко ответить: Сервисная сетка.

Что такое Service Mesh и Istio?

Service Mesh – это выделенный уровень инфраструктуры для безопасной, быстрой и надежной передачи данных между сервисами.

Сервисная сетка обычно реализуется как облегченный сетевой прокси-сервер, развернутый вместе с сервисным кодом, который прозрачно перехватывает весь входящий / исходящий сетевой трафик из сервиса.

Istio – это реализация сервисной сети с открытым исходным кодом для Kubernetes. Стратегия, используемая Istio для интеграции прокси-сервера сетевого трафика в Kubernetes Pod, реализуется с помощью контейнера sidecar. Это контейнер, работающий вместе с сервисным контейнером в том же Pod. Поскольку они работают в одном поде, оба контейнера имеют общий IP-адрес, жизненный цикл, ресурсы, сеть и хранилище.

Istio использует Envoy Proxy в качестве сетевого прокси внутри контейнера sidecar и настраивает Pod для отправки всего входящего / исходящего трафика через прокси Envoy (контейнер sidecar).

При использовании Istio связь между сервисами не прямая. Тем не менее, через дополнительный контейнер (прокси-сервер Envoy), когда служба A запрашивает службу B, запрос отправляется в прокси-контейнер службы A с использованием ее DNS-имени. Затем прокси-контейнер службы A отправляет запрос в прокси-контейнер службы B, который, наконец, вызывает реальную службу B. Для ответа используется обратный путь.

Контейнер сопутствующего прокси-сервера Envoy реализует следующие функции:

  • Интеллектуальная маршрутизация и балансировка нагрузки между сервисами.
  • Внедрение неисправностей.
  • Устойчивость: повторные попытки и автоматический выключатель.
  • Наблюдаемость и телеметрия: метрики и трассировка.
  • Безопасность: шифрование и авторизация.
  • Обеспечение соблюдения политики в масштабах всего парка.

Как вы можете видеть на следующей диаграмме, функции, реализуемые контейнером sidecar, идеально соответствуют пяти микрослужбам: обнаружение, отказоустойчивость, аутентификация, мониторинг и отслеживание.

Логика микрослужб в контейнере дает несколько преимуществ:

  • Бизнес-код полностью изолирован от микросервисов.
  • Все службы используют точную реализацию, поскольку используют один и тот же контейнер.
  • Это независимый код. Сервис может быть реализован на любом языке, но эти сквозные проблемы всегда одинаковы.
  • Процесс настройки и параметры одинаковы для всех сервисов.

Но как Istio работает внутри и зачем нам Istio, а не только прокси Envoy?

Архитектура

Прокси-сервер Envoy – это легкий сетевой прокси-сервер, который можно использовать автономно, но когда развернуты десятки служб, необходимо настроить десятки прокси-серверов Envoy. Все может стать немного сложным и громоздким. Istio упрощает этот процесс.

С архитектурной точки зрения, сервисная сетка Istio состоит из плоскости данных и плоскости управления.

В плоскость данных состоит из прокси-сервера Envoy, развернутого как вспомогательный компонент. Этот прокси-сервер перехватывает всю сетевую связь между службами. Он также собирает и сообщает телеметрию по всему трафику ячеистой сети.

В плоскость управления управляет и настраивает прокси Envoy.

На следующей диаграмме показаны оба компонента:

Установка Istio

Для установки Istio нам понадобится кластер Kubernetes. В этой статье мы используем Minikube, но подойдет любой другой кластер Kubernetes.

Выполните следующую команду, чтобы запустить кластер:

minikube start -p istio --kubernetes-version='v1.19.0' --vm-driver='virtualbox' --memory=4096

  [istio] minikube v1.17.1 on Darwin 11.3
  Kubernetes 1.20.2 is now available. If you would like to upgrade, specify: --kubernetes-version=v1.20.2
  minikube 1.19.0 is available! Download it: https://github.com/kubernetes/minikube/releases/tag/v1.19.0
  To disable this notice, run: 'minikube config set WantUpdateNotification false'

✨ Использование драйвера виртуального бокса на основе существующего профиля

❗ Вы не можете изменить размер памяти для существующего кластера minikube. Пожалуйста, сначала удалите кластер.

  Starting control plane node istio in cluster istio
  Restarting existing virtualbox VM for "istio" ...
  Preparing Kubernetes v1.19.0 on Docker 19.03.12 ...
  Verifying Kubernetes components...
  Enabled addons: storage-provisioner, default-storageclass
  Done! kubectl is now configured to use "istio" cluster and "" namespace by default

Когда кластер Kubernetes запущен и работает, загрузите istioctl Инструмент командной строки для установки Istio внутри кластера. В этом случае мы загружаем Istio 1.9.4 со страницы релиза.

С istioctl установлен, мы можем приступить к развертыванию Istio в кластере. Istio поставляется с разными профилями, но для начала работы с Istio идеально подойдет демонстрационный профиль.

istioctl install --set profile=demo -y

Detected that your cluster does not support third party JWT authentication. Falling back to less secure first party JWT. See https://istio.io/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for details.

✔ Ядро Istio установлено

✔ Установлен Istiod

✔ Установлены исходящие шлюзы

✔ Установлены шлюзы Ingress

✔ Установлены дополнения

✔ Установка завершена

Подождите, пока все модули в istio-system пространство имен находится в рабочем состоянии.

kubectl get pods -n istio-system

NAME                                   READY   STATUS    RESTARTS   AGE
grafana-b54bb57b9-fj6qk                1/1     Running   2          171d
istio-egressgateway-68587b7b8b-m5b58   1/1     Running   2          171d
istio-ingressgateway-55bdff67f-jrhpk   1/1     Running   2          171d
istio-tracing-9dd6c4f7c-9gcx9          1/1     Running   3          171d
istiod-76bf8475c-xphgd                 1/1     Running   2          171d
kiali-d45468dc4-4nbl4                  1/1     Running   2          171d
prometheus-74d44d84db-86hdr            2/2     Running   4          171d

Чтобы воспользоваться всеми возможностями Istio, на модулях в сети должен быть запущен дополнительный прокси-сервер Istio.

Есть два способа внедрить sidecar Istio в Pod: вручную с помощью istioctl или автоматически при развертывании Pod в настроенном для этой цели пространстве имен.

Для простоты автоматический впрыск коляски настроен в default пространство имен, выполнив следующую команду:

kubectl label namespace default istio-injection=enabled

namespace/default labeled

Теперь Istio установлен в кластере Kubernetes и готов к использованию в default пространство имен.

В следующем разделе мы увидим обзор приложения для «istioize», и мы его развернем.

Приложение

Приложение состоит из двух служб: службы бронирования и службы оценки. Книжный сервис возвращает информацию о книге вместе с ее рейтингами. Сервис рейтинга возвращает рейтинги данной книги. Есть две версии в случае рейтингового сервиса: v1 возвращает фиксированный рейтинг для любой книги (1), а v2 возвращает случайный рейтинг.

Развертывание

Поскольку автоматическое внедрение дополнительных компонентов включено, нам не нужно ничего менять в файлах развертывания Kubernetes. Давайте развернем эти три службы в «istioized» пространстве имен.

Например, файл развертывания Kubernetes книжный сервис является:

---
apiVersion: v1
kind: Service
metadata:
 labels:
   app.kubernetes.io/name: book-service
   app.kubernetes.io/version: v1.0.0
 name: book-service
spec:
 ports:
 - name: http
   port: 8080
   targetPort: 8080
 selector:
   app.kubernetes.io/name: book-service
   app.kubernetes.io/version: v1.0.0
 type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
 labels:
   app.kubernetes.io/name: book-service
   app.kubernetes.io/version: v1.0.0
 name: book-service
spec:
 replicas: 1
 selector:
   matchLabels:
     app.kubernetes.io/name: book-service
     app.kubernetes.io/version: v1.0.0
 template:
   metadata:
     labels:
       app.kubernetes.io/name: book-service
       app.kubernetes.io/version: v1.0.0
   spec:
     containers:
     - env:
       - name: KUBERNETES_NAMESPACE
         valueFrom:
           fieldRef:
             fieldPath: metadata.namespace
       image: quay.io/lordofthejars/book-service:v1.0.0
       imagePullPolicy: Always
       name: book-service
       ports:
       - containerPort: 8080
         name: http
         protocol: TCP

Как мы видим, в файле нет ничего специфического ни для Istio, ни для контейнера sidecar. По умолчанию внедрение возможностей Istio происходит автоматически.

Развернем приложение в кластере Kubernetes:

kubectl apply -f rating-service/src/main/kubernetes/service.yml -n default
kubectl apply -f rating-service/src/main/kubernetes/deployment-v1.yml -n default
kubectl apply -f rating-service/src/main/kubernetes/deployment-v2.yml -n default
kubectl apply -f book-service/src/main/kubernetes/deployment.yml -n default

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

kubectl get pods -n default

NAME                                READY   STATUS    RESTARTS   AGE
book-service-5cc59cdcfd-5qhb2       2/2     Running   0          79m
rating-service-v1-64b67cd8d-5bfpf   2/2     Running   0          63m
rating-service-v2-66b55746d-f4hpl   2/2     Running   0          63m

Обратите внимание, что каждый модуль Pod содержит два запущенных контейнера, один из которых является самой службой, а другой – прокси-сервером Istio.

Если мы опишем Pod, мы заметим, что:

kubectl describe pod rating-service-v2-66b55746d-f4hpl

Name:         rating-service-v2-66b55746d-f4hpl
Namespace:    default
…
Containers:
  rating-service:
    Container ID:   docker://cda8d72194ee37e146df7bf0a6b23a184b5bfdb36fed00d2cc105daf6f0d6e85
    Image:          quay.io/lordofthejars/rating-service:v2.0.0
…
  istio-proxy:
    Container ID:  docker://7f4a9c1f425ea3a06ccba58c74b2c9c3c72e58f1d805f86aace3d914781e0372
    Image:         docker.io/istio/proxyv2:1.6.13

Поскольку мы используем Minikube, а служба Kubernetes относится к типу LoadBalancer, для доступа к приложению требуются IP-адрес Minikube и служебный порт. Чтобы найти их, выполните следующую команду:

minikube IP -p istio
192.168.99.116

kubectl get services -n default
NAME           TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
book-service   LoadBalancer   10.106.237.42         8080:31304/TCP   111m
kubernetes     ClusterIP      10.96.0.1                443/TCP          132m
rating         LoadBalancer   10.109.106.128        8080:31216/TCP   95m

И давайте выполним несколько команд curl для службы:

curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":1}
curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":3}
curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":1}
curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":3}

Из выходных данных видно, что значение рейтинга изменяется с 1 до 3 для того же идентификатора книги. По умолчанию Istio распределяет вызовы, используя циклический подход между сервисами. В этом примере запросы сбалансированы между rating:v1 (фиксированный рейтинг – 1) и rating:v2 (случайная оценка, рассчитанная при запуске; в данном случае 3 для книги с идентификатором 1).

Приложение развернуто и «Istioized», но микрослужбы еще не включены. Давайте приступим к созданию ресурсов Istio для включения и настройки микрослужб в контейнерах прокси Istio.

Istio Microservicities

Открытие

Сервис Kubernetes реализует концепцию открытия. Это дает возможность сгруппировать Модули Kubernetes (действуя как единое целое) со стабильным виртуальным IP-адресом и DNS-именем. Модули получают доступ к другим модулям с помощью Сервис Kubernetes name как имя хоста. Однако это позволяет нам реализовать только базовые стратегии обнаружения, но когда требуются более продвинутые стратегии обнаружения / развертывания, такие как канареечные выпуски, темные запуски или теневой трафик, сервисов Kubernetes недостаточно.

Istio позволяет легко контролировать поток трафика между сервисами, используя две концепции: DestinationRule а также VirtualService.

А DestinationRule определяет политики для обслуживания трафика после выполнения маршрутизации. Вот некоторые из вещей, которые мы настраиваем в правиле назначения:

  • Политика дорожного движения.
  • Политика балансировки нагрузки.
  • Настройки пула подключений.
  • mTLS.
  • Устойчивость.
  • Укажите подмножества услуги с помощью меток. Эти подмножества используются в VirtualService.

Давайте создадим файл с именем destination-rule-v1-v2.yml зарегистрировать два подмножества: один для рейтинговая служба v1 и еще один для рейтинговая служба v2:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
 name: rating
spec:
 host: rating
 subsets:
 - labels:
     app.kubernetes.io/version: v1.0.0
   name: version-v1
 - labels:
     app.kubernetes.io/version: v2.0.0
   name: version-v2

Мы устанавливаем host для оценки, так как это DNS-имя, указанное в Сервис Kubernetes. Тогда в subsets раздел, мы определяем подмножества, используя labels установить в качестве ресурсов Kubernetes и сгруппировать их под «виртуальным» name. Например, в предыдущем случае создаются две группы: одна группа для версии 1 и другая группа для рейтинговых сервисов версии 2.

kubectl apply -f src/main/kubernetes/destination-rule-v1-v2.yml -n default
destinationrule.networking.istio.io/rating created

А VirtualService позволяет настроить маршрутизацию запросов к сервису в сервисной сети Istio. Благодаря виртуальным сервисам легко реализовать такие стратегии, как A / B-тестирование, развертывание синих / зеленых, канареечных выпусков или темных запусков.

Давайте создадим файл с именем virtual-service-v1.yml направить весь трафик в рейтинг v1:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: rating
spec:
 hosts:
 - rating
 http:
 - route:
   - destination:
       host: rating
       subset: version-v1
     weight: 100

В предыдущем файле мы настраиваем любой запрос на доступ к хосту оценки, который должен быть отправлен в модули оценки, принадлежащие подмножеству version-v1. Помните, что DestinationRule файл создал это подмножество.

kubectl apply -f src/main/kubernetes/virtual-service-v1.yml -n default
virtualservice.networking.istio.io/rating created

Теперь выполните несколько curl команды против службы снова, но теперь наиболее существенным отличием является вывод, поскольку все запросы отправляются в рейтинг v1.

curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":1}
curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":1}
curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":1}

Очевидно, мы можем вместо этого создать другой файл виртуальной службы, указывающий на рейтинг v2:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: rating
spec:
 hosts:
 - rating
 http:
 - route:
   - destination:
       host: rating
       subset: version-v2
     weight: 100
kubectl apply -f src/main/kubernetes/virtual-service-v2.yml -n default
virtualservice.networking.istio.io/rating configured

И трафик отправляется в рейтинг v2:

curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":3}
curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":3}

В rating поле больше не установлено в 1, так как запрос был обработан версией 2.

Канареечный релиз выполняется путем изменения weight поле в виртуальном сервисе.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: rating
spec:
 hosts:
 - rating
 http:
 - route:
   - destination:
       host: rating
       subset: version-v1
     weight: 75
   - destination:
       host: rating
       subset: version-v2
     weight: 25
kubectl apply -f src/main/kubernetes/virtual-service-v1-v2-75-25.yml -n default
virtualservice.networking.istio.io/rating configured

Теперь выполните несколько curl команды против приложения:

curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":1}
curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":1}
curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":1}
curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":3}

К рейтингу v1 обращаются чаще, чем к рейтингу v2, в соответствии с пропорцией, установленной в weight поле.

Давайте удалим ресурс виртуальной службы, чтобы вернуться к поведению по умолчанию (стратегия циклического перебора):

kubectl delete -f src/main/kubernetes/virtual-service-v1-v2-75-25.yml -n default
virtualservice.networking.istio.io "rating" deleted

Устойчивость

В микросервисной архитектуре мы должны разрабатывать с учетом сбоев, особенно при взаимодействии с другими сервисами. В монолитном приложении ваше приложение в целом работает или не работает, но в архитектуре микросервисов это не так, поскольку некоторые из них могут работать, а другие – отключаться. Отказоустойчивость (или отказоустойчивость приложения) – это способность приложения / службы реагировать на проблемы и при этом обеспечивать наилучший результат.

Давайте посмотрим, как Istio помогает нам в реализации стратегий отказоустойчивости и как их настраивать.

Неудачи

Служба оценки реализует конкретную конечную точку, которая заставляет службу начинать возвращать код ошибки 503 HTTP при обращении к ней.

Выполните следующую команду (изменив имя Pod на свое), чтобы рейтинг службы v2 начал давать сбой при обращении к нему:

kubectl get pods -n default

NAME                                READY   STATUS    RESTARTS   AGE
book-service-5cc59cdcfd-5qhb2       2/2     Running   4          47h
rating-service-v1-64b67cd8d-5bfpf   2/2     Running   4          47h
rating-service-v2-66b55746d-f4hpl   2/2     Running   4          47h

kubectl exec -ti rating-service-v2-66b55746d-f4hpl -c rating-service -n default curl localhost:8080/rate/misbehave

Ratings endpoint returns 503 error.

Повторные попытки

В настоящее время Istio настроено без виртуального сервиса, то есть балансирует запросы между обеими версиями.

Давайте сделаем несколько запросов и убедимся, что рейтинг v2 не работает:

curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":1}

curl 192.168.99.116:31304/book/1

curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":1}

Один запрос не дает ответа, потому что рейтинг v2 возвращает не действительный ответ, а ошибку.

Повторные попытки поддерживаются Istio и настраиваются в VirtualService ресурс. Создайте новый файл с именем virutal-service-retry.yml со следующим содержанием:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: rating
spec:
 hosts:
 - rating
 http:
 - route:
   - destination:
       host: rating
   retries:
     attempts: 2
     perTryTimeout: 5s
     retryOn: 5xx

Мы настроены на выполнение двух автоматических повторных попыток, если рейтинговая служба (любая версия) возвращает 5XX Код ошибки HTTP.

kubectl apply -f src/main/kubernetes/virtua-service-retry.yml -n default
virtualservice.networking.istio.io/rating created

Давайте сделаем несколько запросов и просмотрим результат:

curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":1}

curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":1}

curl 192.168.99.116:31304/book/1
{"bookId":1,"name":"Book 1","rating":1}

Теперь мы видим, что все запросы соответствуют рейтингу v1. Причина проста. Когда запросы к рейтинговому сервису отправляются на v1, предоставляется действительный ответ. Но когда запросы отправляются в v2, возникает ошибка и выполняется автоматическая повторная попытка.

Поскольку вызовы между обеими службами балансируются по нагрузке, запрос на повторение отправляется на v1, что дает действительный ответ.

По этой причине каждый предыдущий запрос возвращает ответ от v1.

Автоматический выключатель

Автоматические повторные попытки – отличный способ справиться с сетевыми сбоями или спорадическими ошибками, но что происходит, когда несколько одновременных пользователей отправляют запросы в неисправную систему с автоматическими повторными попытками?

Давайте смоделируем этот сценарий с помощью Siege, утилиты для проверки нагрузки HTTP, но сначала давайте проверим журналы рейтинга v2 с помощью команды kubectl:

kubectl get pods -n default

NAME                                READY   STATUS    RESTARTS   AGE
book-service-5cc59cdcfd-5qhb2       2/2     Running   4          47h
rating-service-v1-64b67cd8d-5bfpf   2/2     Running   4          47h
rating-service-v2-66b55746d-f4hpl   2/2     Running   4          47h

kubectl logs rating-service-v2-66b55746d-f4hpl -c rating-service -n default

…
Request 31
Request 32
Request 33
Request 34

Эти строки журнала показывают количество запросов, обработанных этой службой. На данный момент сервис обработал 34 запроса.

Чтобы смоделировать четырех одновременных пользователей, отправляя каждому по десять запросов в приложение, выполните следующую команду siege:

siege -r 10 -c 4 -v -d 1 192.168.99.116:31304/book/1

HTTP/1.1 200     0.04 secs:      39 bytes ==> GET  /book/1
HTTP/1.1 200     0.03 secs:      39 bytes ==> GET  /book/1

Transactions:		          40 hits
Availability:		      100.00 %
Elapsed time:		        0.51 secs
Data transferred:	        0.00 MB
Response time:		        0.05 secs
Transaction rate:	       78.43 trans/sec
Throughput:		        0.00 MB/sec
Concurrency:		        3.80
Successful transactions:          40
Failed transactions:	           0
Longest transaction:	        0.13
Shortest transaction:	        0.01

Конечно, вызывающему абоненту не отправляются сообщения об ошибках, так как повторные попытки выполняются автоматически, но давайте еще раз проверим журналы оценки v2:

kubectl logs rating-service-v2-66b55746d-f4hpl -c rating-service -n default

…
Request 56
Request 57
Request 58
Request 59

Несмотря на то, что рейтинг v2 не смог сгенерировать действительный ответ, к сервису обращались 25 раз, что оказало значительное влияние на приложение, потому что:

  1. Если служба перегружена, отправка большего количества запросов кажется плохой идеей, чтобы позволить ей восстановиться. Наверное, лучше всего будет поместить экземпляр сервиса в карантин.
  2. Если служба просто перестает работать из-за ошибки, повторная попытка не улучшит ситуацию.
  3. Для каждой повторной попытки создается сокет, выделяются некоторые файловые дескрипторы или некоторые пакеты отправляются по сети, что приводит к сбою. Этот процесс влияет на другие службы, работающие на том же узле (ЦП, память, файловые дескрипторы и т. Д.) Или использующие сеть (увеличивая бесполезный трафик, задержку и т. Д.).

Чтобы решить эту проблему, нам нужно найти способ автоматически работать с ошибкой, когда выполнение постоянно терпит неудачу. Образец проектирования выключателя и образцы переборок являются решениями этой проблемы. Первый обеспечивает безотказную стратегию при возникновении одновременных ошибок, а второй ограничивает количество одновременных выполнений.

Теперь создайте новый файл с именем destination-rule-circuit-breaker.yml со следующим содержанием:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
 name: rating
spec:
 host: rating
 subsets:
 - labels:
     version: v1
   name: version-v1
 - labels:
     version: v2
   name: version-v2
 trafficPolicy:
   connectionPool:
     http:
       http1MaxPendingRequests: 3
       maxRequestsPerConnection: 3
     tcp:
       maxConnections: 3
   outlierDetection:
     baseEjectionTime: 3m
     consecutive5xxErrors: 1
     interval: 1s
     maxEjectionPercent: 100

Первое, что вы должны заметить, это то, что DestinationRule настраивает автоматический выключатель. Помимо настройки параметров автоматического выключателя, необходимо указать подмножества. Ограничение одновременных подключений задается в connectionPool поле.

Для настройки автоматического выключателя используйте outlierDetection раздел. В этом примере цепь будет разомкнута, если в течение одной секунды произойдет ошибка, отключив службу на три минуты. По истечении этого времени схема будет полуоткрыта, что означает выполнение реальной логики. Если он снова выходит из строя, цепь остается разомкнутой; если нет, то он закрыт.

kubectl apply -f src/main/kubernetes/destination-rule-circuit-breaker.yml
destinationrule.networking.istio.io/rating configured

Мы настроили схему выключателя в Istio; давай выполним siege снова и просмотрите журналы рейтинг v2.

siege -r 10 -c 4 -v -d 1 192.168.99.116:31304/book/1

HTTP/1.1 200     0.04 secs:      39 bytes ==> GET  /book/1
HTTP/1.1 200     0.03 secs:      39 bytes ==> GET  /book/1

Transactions:		          40 hits
Availability:		      100.00 %

Еще раз осмотрите журналы. Помните, что в предыдущем прогоне мы остановились на запросе 59.

kubectl журналы рейтинг-сервис-v2-66b55746d-f4hpl -c рейтинг-сервис -n по умолчанию

kubectl logs rating-service-v2-66b55746d-f4hpl -c rating-service -n default

…
Request 56
Request 57
Request 58
Request 59
Request 60

Рейтинг v2 получает только один запрос, потому что первый обработанный запрос возвратил ошибку, цепь была открыта и больше не было отправлено запросов для рейтинга v2.

Теперь мы убедились, что с Istio работает отказоустойчивость. Вместо того, чтобы реализовывать эту логику в сервисе вместе с бизнес-логикой, контейнер реализует ее.

Наконец, выполните следующую команду, чтобы вернуть рейтинг v2 в предыдущее состояние.

kubectl exec -ti rating-service-v2-66b55746d-f4hpl -c rating-service curl localhost:8080/rate/behave
Back to normal

Аутентификация

Одна из проблем, которые мы можем обнаружить при реализации архитектуры микросервисов, заключается в том, как защитить связь между внутренними службами. Стоит ли использовать mTLS? Должны ли мы аутентифицировать запросы? И мы должны их разрешить? Ответ на эти вопросы – ДА !. Постепенно мы посмотрим, как Istio может нам в этом помочь.

Аутентификация

Istio автоматически обновляет весь трафик между прокси-серверами и рабочими нагрузками до mTLS, ничего не меняя в коде службы. Между тем, как разработчики, мы реализуем сервисы по протоколу HTTP. Когда служба «istioized», связь между службами происходит по протоколу HTTPS. Istio отвечает за управление сертификатами, центрами сертификации или отзыв / продление сертификатов.

Чтобы убедиться, что mTLS включен, мы можем использовать инструмент istioctl, выполнив следующую команду:

istioctl experimental authz check book-service-5cc59cdcfd-5qhb2 -a

LISTENER[FilterChain]     HTTP ROUTE          ALPN        mTLS (MODE)          AuthZ (RULES)

...
virtualInbound[5] inbound|8080|http|book-service.default.svc.cluster.local             istio,istio-http/1.0,istio-http/1.1,istio-h2 noneSDS: default     yes (PERMISSIVE)     no (none)

…

Хост-сервис Book-service на порту 8080 имеет mTLS, настроенный с разрешающей стратегией.

Авторизация

Давайте посмотрим, как включить аутентификацию конечных пользователей с Istio с использованием формата JSON Web Token (JWT).

Первое, что нужно сделать, это применить RequestAuthentication ресурс. Эта политика гарантирует, что если Authorization заголовок содержит токен JWT, он должен быть действительным, не просроченным, выпущенным правильным эмитентом и не подвергающимся манипуляциям.

apiVersion: "security.istio.io/v1beta1"
kind: "RequestAuthentication"
metadata:
 name: "bookjwt"
 namespace: default
spec:
 selector:
   matchLabels:
     app.kubernetes.io/name: book-service
 jwtRules:
 - issuer: "testing@secure.istio.io"
   jwksUri: "https://gist.githubusercontent.com/lordofthejars/7dad589384612d7a6e18398ac0f10065/raw/ea0f8e7b729fb1df25d4dc60bf17dee409aad204/jwks.json"

Основные поля:

  • issuer: Действительный эмитент токена. Если предоставленный токен не указывает этого эмитента в iss JWT, значит, токен недействителен.
  • jwksUri: URL-адрес jwks файл, в котором зарегистрированы открытые ключи для проверки подписи токена.
kubectl apply -f src/main/kubernetes/request-authentication-jwt.yml -n default
requestauthentication.security.istio.io/bookjwt created

Беги сейчас curl снова с недопустимым токеном:

curl 192.168.99.116:31304/book/1 -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUU"

Jwt verification fails

Поскольку токен недействителен, запрос отклоняется кодом HTTP / 1.1 401 Unauthorized.

Повторите предыдущий запрос с действующим токеном:

curl 192.168.99.116:31304/book/1 -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg"

{"bookId":1,"name":"Book 1","rating":3}

Теперь мы видим правильный ответ, поскольку токен правильный.

Пока мы проверяем только запросы (требуется только действительный токен), но Istio также поддерживает авторизацию по модели управления доступом на основе ролей (RBAC). Давайте создадим AuthorizationPolicy чтобы разрешать запросы только с действующим веб-токеном JSON с ролью утверждения, установленной для клиента. Создайте файл с именем authorization-policy-jwt.yml:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
 name: require-jwt
 namespace: default
spec:
 selector:
   matchLabels:
     app.kubernetes.io/name: book-service
 action: ALLOW
 rules:
 - from:
   - source:
      requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"]
   when:
   - key: request.auth.claims[role]
     values: ["customer"]
kubectl apply -f src/main/kubernetes/authorization-policy-jwt.yml
authorizationpolicy.security.istio.io/require-jwt created

Затем выполните ту же самую команду curl, что и раньше:

curl 192.168.99.116:31304/book/1 -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg"

RBAC: access denied

На этот раз ответ немного другой. Хотя токен действителен, в доступе отказано, поскольку для токена не задана заявленная роль клиента.

Давайте использовать следующий токен:

curl 192.168.99.116:31304/book/1 -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjI1NDkwNTY4ODgsImlhdCI6MTU0OTA1Njg4OSwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJyb2xlIjoiY3VzdG9tZXIiLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.VM9VOHD2NwDjQ6k7tszB3helfAn5wcldxe950BveiFVg43pp7x5MWTjMtWQRmQc7iYul19PXsmGnSSOiQQobxdn2UnhHJeKeccCdX5YVgX68tR0R9xv_wxeYQWquH3roxHh2Xr2SU3gdt6s7gxKHrW7Zc4Z9bT-fnz3ijRUiyrs-HQN7DBc356eiZy2wS7O539lx3mr-pjM9PQtcDCDOGsnmwq1YdKw9o2VgbesfiHDDjJQlNv40wnsfpq2q4BgSmdsofAGwSNKWtqUE6kU7K2hvV2FvgwjzcB19bbRYMWxRG0gHyqgFy-uM5tsC6Cib-gPAIWxCdXDmLEiqIdjM3w"

{"bookId":1,"name":"Book 1","rating":3}

Теперь мы видим действительный ответ, поскольку токен правильный и содержит допустимое значение роли.

Наблюдаемость

Istio поставляется с четырьмя установленными компонентами, отвечающими требованиям наблюдаемости:

Получите все капсулы из istio-system пространство имен:

kubectl  get pods -n istio-system
NAME                                   READY   STATUS         RESTARTS   AGE

grafana-b54bb57b9-k5qbm                1/1     Running        0          178m
istio-egressgateway-68587b7b8b-vdr67   1/1     Running        0          178m
istio-ingressgateway-55bdff67f-hlnqw   1/1     Running        0          178m
istio-tracing-9dd6c4f7c-44xhk          1/1     Running        0          178m
istiod-76bf8475c-xphgd                 1/1     Running        7          177d
kiali-d45468dc4-fl8j4                  1/1     Running        0          178m
prometheus-74d44d84db-zmkd7            2/2     Running        0          178m

Мониторинг

Istio интегрируется с Prometheus для отправки всех видов информации, связанной с сетевым трафиком и услугами. Более того, он предоставляет экземпляр Grafana для визуализации всех собранных данных.

Чтобы получить доступ к Grafana, давайте раскроем Pod с помощью команды port-forward:

kubectl port-forward -n istio-system grafana-b54bb57b9-k5qbm 3000:3000
Forwarding from 127.0.0.1:3000 -> 3000
Forwarding from [::1]:3000 -> 3000

Откройте браузер и получите доступ к панели управления Grafana, перейдя в locahost:3000.

Kiali – это еще один инструмент, работающий в Istio, для управления Istio и наблюдения за параметрами сервисной сети, например, как сервисы подключаются, как они работают и какие ресурсы Istio зарегистрированы.

Чтобы получить доступ к Kiali, давайте откроем Pod с помощью команды port-forward:

kubectl port-forward -n istio-system kiali-d45468dc4-fl8j4 20001:20001
Forwarding from 127.0.0.1:20001 -> 20001
Forwarding from [::1]:20001 -> 20001

Откройте браузер, войдите в панель управления Istio, затем перейдите к locahost: 20001.

Отслеживание

Трассировка используется для визуализации выполнения программы и развития данных. Istio перехватывает все запросы / ответы и отправляет их Jaeger.

Вместо использования команды port-forward мы можем использовать istioctl чтобы открыть порт и автоматически открыть страницу:

istioctl dashboard jaeger

Выводы

Разработка и внедрение архитектуры микросервисов немного сложнее, чем разработка монолитного приложения. Мы считаем, что микросервисы могут побудить вас правильно создавать сервисы с точки зрения инфраструктуры приложений.

Istio реализует некоторые из этих микрослужб в дополнительном контейнере, что делает их повторно используемыми во всех службах независимо от языка (языков) программирования, используемых для приложения.

Более того, подход Istio позволяет нам изменять поведение сервисов без необходимости их повторного развертывания.

Если вы планируете разрабатывать микросервисы и развертывать их в Kubernetes, Istio – жизнеспособное решение, поскольку оно легко интегрируется с Kubernetes.

Исходный код, продемонстрированный в этой статье, можно найти в этом репозитории GitHub, а исходный код первой части этой серии можно найти в этом репозитории GitHub.

об авторе

Алекс Сото является директором по опыту разработчиков в Red Hat. Он увлечен миром Java, автоматизацией программного обеспечения и верит в модель программного обеспечения с открытым исходным кодом. Сото – соавтор Manning | Тестирование микросервисов Java и O’Reilly | Quarkus Cookbook и участник нескольких проектов с открытым исходным кодом. Чемпион Java с 2017 года, он также является международным докладчиком и преподавателем в университете Salle URL. Вы можете следить за ним в Твиттере (Алекс Сото ⚛️), чтобы быть в курсе того, что происходит в Kubernetes и мире Java.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button