Как бороться с критическими изменениями в Service Mesh

Я создаю пример приложения для микросервисов с Kubernetes, чтобы узнать о лучших практиках и шаблонах для будущих проектов. Я использую Istio в качестве Service Mesh для обработки трафика с востока на запад, и у меня есть базовое понимание концепций (VirtualServices, DestinationRules, ...). Сервисная сетка позволяет мне легко выпускать новые версии микросервиса и перенаправлять трафик на новый экземпляр (например, с помощью взвешенного распределения). Имея в виду семантическое управление версиями, это действительно хорошо работает для Patch и Minorобновления, потому что они, теоретически, не изменяли существующий контракт и, следовательно, могут быть заменой существующей службы. Теперь мне интересно, как правильно бороться с критическими изменениями службы, поэтому Major обновление версии.

Для этого сложно найти информацию, но с той ограниченной информацией, которую я получил, я теперь думаю о двух подходах:

  1. Каждая основная версия службы (например, user-service) получает свое VirtualService чтобы клиенты могли обращаться к нему правильно (по другому имени службы, например user-service-v1). Затем Istio используется для правильной маршрутизации трафика для основной версии (например, 1.*) к различным доступным службам (например, user-service v1.3.1 и user-service v1.4.0).

  2. Я использую один общий VirtualService для конкретного микросервиса (например, user-service). Этот VirtualService содержит множество определений маршрутизации для использования, например, заголовок, отправленный клиентом (например, x-major-version=1) для сопоставления запроса с местом назначения.

В целом между обоими методами нет особой разницы. Очевидно, что клиенту необходимо указать, с какой основной версией он хочет разговаривать, установив заголовок или разрешив другое имя службы. Существуют ли какие-либо ограничения описанных методов, которые делают один лучше другого? Или есть другие варианты, которые мне совершенно не хватает? Любая помощь и указатели приветствуются!

1 ответ

Решение

TL; DR

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

Подход 1

Как указано в документации

В ситуациях, когда неудобно определять полный набор правил или политик маршрутизации для конкретного хоста в одном ресурсе Virtual Service или DestinationRule, может быть предпочтительнее постепенно указывать конфигурацию для хоста в нескольких ресурсах. Pilot объединит такие правила назначения и объединит такие виртуальные службы, если они привязаны к шлюзу.

Итак, теоретически вы можете использовать подход номер 1, но я бы сказал, что здесь слишком много конфигурации, и есть лучшая идея сделать это.

Допустим, у вас есть старое приложение с именем v1.3.1 и новое приложение с именем v1.4.0, поэтому соответствующая виртуальная служба будет выглядеть следующим образом.

       apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: vs-vervice1
spec:
  hosts:
  - '*'
  http:
  - name: "v1.3.1"
    route:
    - destination:
        host: service1.namespace.svc.cluster.local

---

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: vs-service2
spec:
  hosts:
  - '*'
  http:
  - name: "v1.4.0"
    route:
    - destination:
        host: service2.namespace.svc.cluster.local

Подход 2

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

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

Допустим, вы развернули новое приложение, вы не хотите отправлять сюда 1% входящего трафика, кроме того, вы можете использовать зеркало, поэтому каждый запрос, который отправляется на старую службу, будет отражаться на новой службе в целях тестирования.

       apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: vs-vervice
spec:
  hosts:
  - '*'
  http:
  - name: "old"
    route:
    - destination:
        host: service.namespace.svc.cluster.local
        subset: v1
      weight: 99
    mirror:
      host: service.namespace.svc.cluster.local
      subset: v2
    mirror_percent: 100
  - name: "new"
    route:
    - destination:
        host: service.namespace.svc.cluster.local
        subset: v2
      weight: 1

---


apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews-destination
spec:
  host: service.namespace.svc.cluster.local
  subsets:
  - name: v1
    labels:
      version: v1  <--- label on old pod
  - name: v2
    labels:
      version: v2  <--- label on new pod

Тестирование нового приложения

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

На самом деле это зависит от конфигурации, если вы используете вышеуказанный вариант с new и oldверсии, то для этого и используется канареечное развертывание, например, взвешенное распределение. Вы можете указать процент трафика, который должен быть отправлен в новую версию вашего приложения. Конечно, вы можете указать заголовки или префиксы в своей виртуальной службе, чтобы пользователи могли использовать старую или новую версию вашего приложения.

Канарское развертывание

Как упоминалось здесь

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

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

Истио

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

Правила маршрутизации Istio также предоставляют другие важные преимущества; вы можете легко управлять точными процентами трафика (например, направлять 1% трафика, не требуя 100 модулей), и вы можете управлять трафиком, используя другие критерии (например, маршрутизировать трафик для определенных пользователей в канареечную версию). Чтобы проиллюстрировать это, давайте посмотрим на развертывание службы helloworld и посмотрим, насколько простой становится проблема.

Вот пример.

Зеркальное отображение

Второе, что часто используется для тестирования новой версии приложения, - это зеркалирование трафика.

Как упоминалось здесь

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

Если вы ищете лучшие практики, я бы рекомендовал начать с этого урока на среднем уровне, потому что здесь он очень хорошо объяснен.

Как работает зеркалирование трафика

Зеркалирование трафика работает следующим образом:

  • Вы развертываете новую версию приложения и включаете зеркалирование трафика.

  • Старая версия отвечает на запросы, как и раньше, но также отправляет асинхронную копию новой версии.

  • Новая версия обрабатывает трафик, но не отвечает пользователю.

  • Операционная группа следит за новой версией и сообщает о любых проблемах группе разработчиков.

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

Кроме того, есть пример с nginx, который отлично показывает, как он должен работать.

Стоит упомянуть, что если вы используете API-интерфейсы записи, такие как заказ или оплата, то зеркальный трафик будет означать, что API-интерфейсы записи, такие как заказ, несколько раз. Эта тема подробно описана здесь Кристианом Постой.


Дайте мне знать, если вы хотите еще что-то обсудить.

Другие вопросы по тегам