Как мы делаем канареечный деплой в PaaS

от автора

Всем привет, меня зовут Антон Малафеев, я руководитель группы инженеров IT-инфраструктуры в СберМаркете. Моя команда вот уже больше 3-х лет занимается разработкой деплоя для PaaS и сопутствующих инструментов.

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

Но сначала немного цифр. Ниже на рисунке — наша статистика использования канареечного деплоя. На момент написания статьи у нас в проде 200+ сервисов, 5300 подов PaaS, ежемесячно мы делаем 2 тысячи деплоев в продакшн и 11 тысяч деплоев в стейдж, которые проходят через канарейку.

Возможности канареечного деплоя

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

  1. Постепенное наращивание трафика на новую версию. У нас в данный момент есть несколько стандартных сценариев, отличающихся по времени.

  2. Возможность задавать кастомные сценарии канарейки, если стандартных не достаточно.

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

  1. У разработчиков есть возможность задать свои метрики, если стандартных не достаточно.

  1. Автооткат при отклонении этих метрик. В таком случае произойдет автоматический откат всех изменений до состояния «как было». В таком случае откат произойдет вместе со всеми вспомогательными инструментами (кодом разработчиков, настройками) и вернет сервис в изначальное состояние. Да, мы не откатываем миграции, но канарейка невозможна без совместимых миграций между версиями в принципе. Пример как это работает: если во время деплоя мы обнаружим повышение ошибок на любой из версий (новой или старой), мы все автоматически откатываем. И уже после, в спокойной обстановке, будем производить разбор ситуации.

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

  3. Возможность выкатить сервисы вообще без нагрузки. Обращение так же идет через отдельный хэдер.

  4. Канарейка не только для внешнего трафика, но и для внутреннего. Многие реализации делают канареечный деплой только для входящего внешнего трафика. Наша реализация делает это и для внутреннего, включая http- и gRPC-взаимодействия. 

Как мы перестроили наш пайплайн под канареечный деплой

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

Простейший пайплайн

Простейший пайплайн

Но, если добавить канарейку, да еще с выбором, то у нас появляется несколько вариантов. Это уже нельзя сделать автоматически, т.к. нужно принимать решение, какой именно вариант нужен.

Простейший пайплайн с выбором деплоя

Простейший пайплайн с выбором деплоя

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

Наш пайплайн отличается от общепринятых тем, то что кнопки вынесены налево — в самом заметном месте, где их не надо искать. Разработчик может выбрать нужный вариант сразу после старта пайплайна, без ожидания. Это потребовало от нас своей реализации needs внутри джоб с ожиданием нужных шагов. Деплой запустится автоматически после прохождения всех нужных сборок и тестов.

Наш текущий продовый пайплайн

Наш текущий продовый пайплайн

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

Какие технологии мы используем

Среди технологий, которые применяет наша команда: Gitlab, Kubernetes, Helm, Argo Rollouts, Istio, Istio Ingress (он же Ingress Gateway) и несколько самописных операторов, один из которых GW Operator (про него отдельно расскажу ниже).

С первыми тремя, думаю, все понятно. Что касается Argo Rollouts, то его мы выбрали из-за простоты интеграции с istio и из-за того ,что он отвечает всем нашим потребностям. Да, мы рассматривали и flagger, но это было на этапе зарождения PaaS, и никто уже не помнит, почему не выбрали именно его).

Istio Ingress

Istio Ingress — это по сути реализация Ingress вместо всем привычного NGINX Ingress. Его мы выбрали за полную интеграцию с istio (в отличии от nginx). С ним мы можем описывать правила для внутреннего и внешнего взаимодействия в одних и тех же манифестах, и под капотом он использует тот же envoy proxy с его преимуществами и недостатками.

Когда мы начали использовать Istio Ingress, мы почти сразу столкнулись с довольно большой проблемой: правила istio формируются и обрабатываются не привычным всем nginx-способом — в порядке наибольшего соответствия пути. Здесь просто берется первое совпадение.

К примеру, возьмем 2 совершенно разных сервиса и опишем для них VirtualService с именами virtsvc1 и virtsvc2 для одного и того же хоста.

В virtsvc1 мы открываем /api/, в virtsvc2 открываем /api/v2. В итоговом конфиге envoy они могут быть в любом порядке и мы получаем разное поведение в зависимости от их местоположения. Это обусловлено тем, что /api/ мог оказаться вверху правил и перехватывал вообще все запросы, в том числе /api/v2.

Поэтому мы решили сделать свой костыль оператор. Мы назвали его GW Operator, он написан на operatorframework.io и делает одну простую, но очень нужную нам вещь — объединяет все ручки и прописывает их в нужном порядке в едином манифесте. Для этого мы сделали свою CRD и назвали ее route. В каком-то виде оно заменило для нас virtualservice в части внешнего взаимодействия. После объединения всех путей мы получаем единый virtualservice, в котором будет правильный порядок всех этих правил.

apiVersion: api-gateway.sbermarket.ru/v1alpha1 kind: Route ... spec:   gateway: istio-ingressgateway/api-gw   host: api.app.ru   rules:   - delegate:       name: app-http       namespace: app     match:     - uri:         prefix: /api/v2/ ...
apiVersion: api-gateway.sbermarket.ru/v1alpha1 kind: Route ... spec:   gateway: istio-ingressgateway/api-gw   host: api.app.ru   rules:   - delegate:       name: app-http       namespace: app2     match:     - uri:         prefix: /api/ ... 

Argo rollouts

Argo rollouts оперирует немного отличными сущностями, чем привычный всем deployment. Они называются Rollout — это, по сути, тот же самый deployment, но приправленный дополнительными опциями, которые позволяют делать канарейки. Если посмотреть на отличия, в самом начале мы видим блок canary.strategy.canary. В нем есть блок steps, который отвечает за шаги канареечного деплоя. В этом примере мы делаем сначала вес 1%, ждем 1 минуту… и уже после выкатываем 100%. При этом оператор сам управляет весами и репликами.

apiVersion: argoproj.io/v1alpha1 kind: Rollout ... spec:   strategy:     canary:       analysis:         templates:         - templateName: success-rate-app       steps:       - setWeight: 1       - pause:           duration: 1m       - setWeight: 100 …

canary.analysis.templates указывает, какой AnalysysTemplate нам использовать при деплое. Это еще одна crd от argo rollouts, которая описывает проверки для успешного прохождения канарейки.

apiVersion: argoproj.io/v1alpha1 kind: AnalysisTemplate ... spec:   metrics:   - failureLimit: 1     interval: 5m     name: success-rate-http     provider:       prometheus:         address: http://prometheushost:9090         query: [[метрика на расчет 500]]     successCondition: result[0] >= 0.95   - failureLimit: 1     interval: 1m     name: canary-crashloop     provider:       prometheus:         address: http://prometheushost:9090         query: [[метрика на падение пода]]     successCondition: result[0] < 3  

Во время деплоя на основе AnalysisTemplate формируется AnalisysRun, в котором происходят проверки жизнеспособности и принимается решение о необходимости дальнейших шагов. 

А в примере ниже — пример неудачной канарейки.

apiVersion: argoproj.io/v1alpha1 kind: AnalysisRun ... status: ...   - count: 5     failed: 2     measurements: ...     - finishedAt: "2023-07-04T07:05:28Z"       phase: Successful       startedAt: "2023-07-04T07:05:28Z"       value: '[0]'     - finishedAt: "2023-07-04T07:06:28Z"       phase: Failed       startedAt: "2023-07-04T07:06:28Z"       value: '[3]'     - finishedAt: "2023-07-04T07:07:28Z"       phase: Failed       startedAt: "2023-07-04T07:07:28Z"       value: '[4]'     metadata:       ResolvedPrometheusQuery: |         sum(increase(kube_pod_container_status_restarts_total{namespace=~"app", pod=~"app-6fff94bbc6.*"}[3m]) >= 3) or vector(0)     name: canary-crashloop     phase: Failed     successful: 3 

Второе отличие rollouts от deployments — это описание canary.trafficRouting.destinationRule. Это указатель на DestinationRule от Istio, который описывает, как нам найти приложение. Мы объявляем, какой у нас DestinationRule прошлой версии канарейки stable и новой canary.

apiVersion: argoproj.io/v1alpha1 kind: Rollout ... spec:   …   strategy:     canary: …       trafficRouting:         istio: …           destinationRule:             canarySubsetName: app-canary             name: app-rollout             stableSubsetName: app-stable … 

И описываем в DestinationRule лейблы для поиска самого приложения по rollouts-pod template-hash, который проставляется самим Argo Rollout, когда канарейка деплоится.

apiVersion: networking.istio.io/v1beta1 kind: DestinationRule ... spec:   host: app.namespace.svc.cluster.local   subsets:   - labels:       app.kubernetes.io/name: app       rollouts-pod-template-hash: 5c5657cf77     name: app-stable   - labels:       app.kubernetes.io/name: app       rollouts-pod-template-hash: 1b3195rd23     name: app-canary ... 

Еще одно отличие rollouts от deployments — это описание VirtualServices, в котором как раз Argo Rollout будет управлять трафиком, проставляя веса.

apiVersion: argoproj.io/v1alpha1 kind: Rollout ... spec:   …   strategy:     canary: …       trafficRouting:         istio:           virtualServices:           - name: app-http             routes:             - app-http … 

В блоке ниже видно, что у нас описано два destination: в одном 100% трафика, в другом вес отсутствует — то есть 0%. 

apiVersion: networking.istio.io/v1beta1 kind: VirtualService ... spec:   http:   - name: app-http     route:     - destination:         host: app.namespace.svc.cluster.local         port:           number: 8080         subset: app-stable       weight: 100     - destination:         host: app.namespace.svc.cluster.local         port:           number: 8080         subset: app-canary     timeout: 900s 

В результате получается такая картина: деплоим Rollout, DestinationRule, AnalysisTemplate, VirtualService, Route через helm. После все это обрабатывается в  GW Operator и Argo Rollout Operator, istio, и получается такая схема связей (картинка ниже)

Итоговая схема канарейки

Итоговая схема канарейки

А здесь можно увидеть процесс самого канареечного деплоя. Все выглядит как обычное обновление deployment: также появляются новые поды, выключаются старые, но все это приправлено правилами весов трафика в istio.

Схема процесса канарейки

Схема процесса канарейки

Заключение

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

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

Tech-команда СберМаркета ведет соцсети с новостями и анонсами. Если хочешь узнать, что под капотом высоконагруженного e-commerce, следи за нами в Telegram и на  YouTube. А также слушай подкаст «Для tech и этих» от наших it-менеджеров.


ссылка на оригинал статьи https://habr.com/ru/articles/813029/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *