Стратегии деплоя: rolling, canary, blue-green

Обзорно разбираем способы выкатывать новую версию, снижая риск простоя и сбоев.

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

Почему «просто заменить» рискованно

Самый простой деплой — остановить старую версию и запустить новую (recreate). Но это даёт простой и нулевую страховку: если новая версия сломана, пользователи увидят ошибку немедленно. Зрелые команды используют стратегии, размазывающие риск.

Корень проблемы в том, что у recreate есть момент, когда старой версии уже нет, а новая ещё не готова принимать трафик — окно недоступности. Для внутреннего инструмента полминуты простоя ночью никто не заметит; для платёжного шлюза в час пик те же полминуты — потерянные транзакции и алерты. Вторая беда — бинарность: recreate переключает 100% пользователей разом, поэтому любая ошибка мгновенно становится массовым инцидентом. Стратегии деплоя бьют в обе точки: убирают окно простоя и/или ограничивают долю пользователей, на которых может «прилететь» поломка.

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

Rolling update

При rolling старые экземпляры заменяются новыми постепенно, по одному или небольшими порциями. В любой момент часть трафика идёт на новую версию, часть — на старую, простоя нет. Если что-то ломается, обновление можно остановить, не затронув все экземпляры. Это дефолт в Kubernetes Deployment.

Старт:  [v1][v1][v1][v1]
Шаг 1:  [v2][v1][v1][v1]
Шаг 2:  [v2][v2][v1][v1]
Готово: [v2][v2][v2][v2]

У rolling есть тонкость, о которой часто забывают: в середине раскатки две версии работают одновременно. Это безобидно, если версии совместимы между собой и с общей базой, но становится ловушкой при несовместимых изменениях схемы БД или API. Если v2 переименовала колонку, которой ещё пользуется v1, часть запросов будет падать. Отсюда практика «expand and contract»: сначала выкатывают изменение, совместимое со старой версией (добавили колонку, но не удалили старую), и лишь после полной замены, отдельным деплоем, убирают лишнее.

Canary

Canary («канарейка в шахте») выкатывает новую версию сначала на маленькую долю трафика — скажем, 5%. Если метрики (ошибки, задержки) в норме, долю увеличивают; если нет — мгновенно откатывают, и проблему увидели лишь 5% пользователей. Это самый осторожный способ выкатывать рискованные изменения.

95% трафика -> [v1][v1][v1]
 5% трафика -> [v2]   (наблюдаем метрики)
   ок? наращиваем долю v2; плохо? убираем v2

Сердце canary — не сам выкат на 5%, а наблюдение между шагами. Без него canary вырождается в медленный rolling: вы всё равно дойдёте до 100%, просто дольше. Смысл появляется, когда после каждого шага вы смотрите на метрики именно canary-группы — долю ошибок, задержки, бизнес-сигналы — и сравниваете их со «здоровой» старой версией. Если canary хуже, вы откатываете до того, как доля пострадавших станет заметной. Зрелые команды автоматизируют это (automated canary analysis): инструмент сам решает, наращивать долю или откатывать, по заданным порогам. Платой за такую осторожность становится время: полный выкат может растянуться на часы, что неудобно для срочных хотфиксов.

Blue-green

Blue-green держит два полных окружения: «синее» (текущее, на проде) и «зелёное» (новая версия). Новую версию полностью разворачивают и проверяют в «зелёном», а затем мгновенно переключают весь трафик с синего на зелёное. Откат — обратное переключение, тоже мгновенное. Минус — нужно держать двойные ресурсы.

До:    трафик -> [BLUE v1 (live)]      [GREEN v2 (прогрев)]
Switch: трафик -> [GREEN v2 (live)]      [BLUE v1 (резерв)]
Откат:  трафик -> [BLUE v1 (live)]   (мгновенно вернули)

Сильная сторона blue-green — предсказуемость и скорость отката: переключение трафика атомарно, и если что-то пошло не так, вы возвращаете стрелку обратно за секунды, причём старое окружение всё это время стояло прогретым и готовым. Слабая сторона — не только двойные ресурсы, но и состояние, которое нельзя «переключить». Пока трафик шёл в зелёное, в общую базу могли записаться данные; при откате на синее эти данные останутся, и версия v1 должна уметь их переварить. Поэтому blue-green особенно требователен к совместимости схемы БД, как и rolling, — просто проблема всплывает в момент отката, а не во время раскатки.

СтратегияРискРесурсыОткат
rollingсреднийобычныеостановить раскатку
canaryнизкий+небольшойубрать canary
blue-greenнизкийдвойныепереключить обратно

Как выбрать стратегию под задачу

Простое правило: чем выше цена ошибки и чем неувереннее вы в изменении, тем осторожнее стратегия. Рутинные, хорошо протестированные правки отлично катятся rolling — дёшево и без простоя. Рискованные изменения с трудно предсказуемым эффектом просятся в canary, где плохой исход затронет малую долю пользователей. Когда нужен мгновенный, заранее отрепетированный откат, берут blue-green, соглашаясь платить за второй комплект ресурсов. Многие комбинируют: основной поток идёт rolling, а особо рискованные релизы прогоняют через canary.

Как это связано с пайплайном

GitLab сам по себе не реализует эти стратегии — он лишь запускает команды. Стратегию обеспечивает целевая платформа: Kubernetes делает rolling штатно, canary и blue-green достигаются манипуляцией с числом подов и маршрутизацией трафика (через ingress/service mesh). Задача джобы — вызвать нужный kubectl/helm с правильными параметрами и, для canary, наблюдать метрики между шагами (тут пригодится when: delayed).

Это разделение ответственности стоит усвоить накрепко, потому что на нём спотыкаются новички: они ищут в .gitlab-ci.yml «настройку canary» и не находят, ведь её там и не должно быть. GitLab отвечает на вопрос «когда и в каком порядке запустить шаги выката»: он умеет разбить деплой на стадии, поставить ручные гейты между ними (when: manual), отложить следующий шаг на время наблюдения (when: delayed с start_in) и связать всё с окружениями. А как именно перелить 5% трафика или переключить blue на green — это уже работа платформы: ingress, service mesh, число реплик. Иными словами, пайплайн — это дирижёр, а оркестр — Kubernetes и сеть.

В GitHub Actions картина та же: CI-система оркеструет шаги, а стратегию реализует платформа деплоя. Разница в нюансах — у GitLab сильнее завязка на окружения и встроенные ручные/отложенные гейты, в GitHub чаще опираются на сторонние actions и инструменты прогрессивной доставки вроде Argo Rollouts или Flagger. Но принцип неизменен: ни одна CI-система не «делает» canary за вас.

Частые ошибки

  • Считать, что выбор стратегии — настройка GitLab; на деле это поведение платформы деплоя.
  • Делать canary без наблюдения метрик между шагами — тогда теряется весь смысл осторожности.
  • Запускать blue-green без плана по двойным ресурсам и переключению трафика.
  • Катить несовместимое изменение схемы БД через rolling или blue-green, забыв про две версии или про откат с уже записанными данными.
  • Выбирать blue-green ради «безопасности» там, где хватило бы дешёвого rolling, и переплачивать за резервное окружение.

Итоги

  • Rolling — постепенная замена без простоя, дефолт Kubernetes.
  • Canary — выкат на малую долю трафика с наблюдением и быстрым откатом.
  • Blue-green — два окружения и мгновенное переключение ценой двойных ресурсов.
  • Выбор стратегии — торг между риском, стоимостью и скоростью отката; под рутину rolling, под риск canary, под мгновенный откат blue-green.
  • CI-система (GitLab или GitHub) оркеструет шаги; саму стратегию реализует платформа деплоя.
Проверьте себя
1. В чём суть canary-деплоя?
AЗаменить все экземпляры разом
BВыкатить новую версию сначала на малую долю трафика, наблюдать метрики и при проблеме быстро откатить
CДержать два полных окружения
DДеплоить только ночью
2. Кто реализует стратегию деплоя при использовании GitLab CI?
AСам GitLab автоматически
BЦелевая платформа деплоя (например, Kubernetes), а джоба лишь вызывает нужные команды
CGitLab Runner на уровне executor
DContainer Registry