← назад к разделу

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

Раньше деплоили «всё или ничего»

Исторически деплой означал: остановить сервер, залить новую версию, запустить. Пока идёт замена — сервис недоступен. Если что-то пошло не так — откат занимает столько же времени, сколько сам деплой. Все пользователи видят проблему одновременно.

Современные стратегии решают это по-разному. Разберём каждую с нуля.

Rolling update — менять по одной реплике

Проблема: при полной замене сервис на время недоступен, а на большом парке серверов это неприемлемо.

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

Kubernetes делает это из коробки: он не пустит трафик на новую реплику, пока та не пройдёт readiness-проверку. Если новая реплика стартует с ошибками — rolling останавливается сам.

Что rolling не умеет:

  • мгновенный откат (откат — это такой же rolling в обратную сторону, занимает минуты);
  • защита от ошибок, которые видны только под полным трафиком;
  • контроль «кто именно увидит новую версию».

Важное условие: во время rolling update работают две версии одновременно. Это значит, что схема базы данных и формат сообщений должны понимать обе версии. Если новая версия добавила колонку, а старая её не знает — это не проблема; если старая версия сломается при виде новых данных — это проблема.

Rolling update — разумный выбор по умолчанию для большинства изменений.

Blue-green — мгновенный переключатель

Проблема: откат занимает минуты, а некоторые ошибки критичны и каждая секунда на счету.

Решение: держать два полных окружения. Одно (green) несёт боевой трафик, в другом (blue) разворачивается новая версия. Когда blue готово и проверено — трафик переключается целиком. Откат: переключить трафик обратно на green. Занимает секунды.

Что blue-green даёт:

  • новую версию можно проверить в продакшн-окружении до того, как её увидят пользователи;
  • откат — переключение, а не новый деплой.

За что платите:

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

Blue-green оправдан, когда цена ошибки высока, а релизы редкие: платёжные системы, критичные API, системы с SLA.

Canary — выкатка на процент

Проблема: даже хорошо протестированный код может вести себя иначе под реальным трафиком и реальными данными.

Решение: новая версия сначала получает маленькую долю трафика — например, 1%. Если метрики (ошибки, время ответа, бизнес-показатели) не ухудшаются, доля растёт: 1% → 5% → 25% → 100%. При деградации — автоматический откат. Проблему увидела сотая часть пользователей, а не все.

Название — от шахтёрской практики брать канарейку в шахту: если птица гибнет, шахтёры уходят. Новая версия — канарейка; если она «гибнет» на малом проценте, остальные пользователи в безопасности.

Что нужно для canary:

  • инфраструктура для маршрутизации с весами (service mesh, Argo Rollouts, Flagger);
  • надёжные метрики — без них canary превращается в rolling с лишними шагами; продвижение по ступеням должно быть по формальным критериям, не «посмотрим глазами»;
  • время — выкатка растягивается на часы.

Canary оправдан при большом трафике (на 100 запросов в секунду 1% — это один запрос; статистики нет) и зрелой наблюдаемости.

Feature flags — деплой и релиз как разные события

Проблема: иногда код готов, но включать его для всех страшно. Или нужно проверить фичу на 10% пользователей без изменений в инфраструктуре.

Решение: code travels separately from its activation. Код едет в продакшн выключенным, а включается позже — без деплоя. Флаг можно включить для всех, для процента, для конкретного сегмента (например, только бета-пользователи).

Логика одна во всех языках: взять идентификатор (пользователя, заказа), спросить сервис флагов, выбрать путь:

// Java — Unleash
UnleashContext ctx = UnleashContext.builder()
        .userId(customerId)
        .build();

if (unleash.isEnabled("new-pricing", ctx)) {
    return newPricingPolicy.calculate(order);
}
return currentPricingPolicy.calculate(order);
// Go — Unleash
ctx := unleash.Context{UserId: customerId}

if unleash.IsEnabled("new-pricing", unleash.WithContext(ctx)) {
    return newPricingPolicy.Calculate(order)
}
return currentPricingPolicy.Calculate(order)
// Node/TypeScript — Unleash
const enabled = isEnabled("new-pricing", { userId: customerId });

if (enabled) {
  return newPricingPolicy.calculate(order);
}
return currentPricingPolicy.calculate(order);
# Python — Unleash
if client.is_enabled("new-pricing", {"userId": customer_id}):
    return new_pricing_policy.calculate(order)
return current_pricing_policy.calculate(order)

Что флаги меняют в процессе:

  • деплой становится скучным — выключенный код не несёт риска;
  • включение обратимо за секунды;
  • постепенная раскатка по пользователям без инфраструктуры весов.

Главная ловушка — вечные флаги. Флаг без даты удаления через год становится частью архитектуры, которую никто не понимает. Заведите правило: у каждого флага есть владелец и дата, когда он должен исчезнуть. После полной раскатки — удалить.

Для начала не нужен специальный сервис: таблица в PostgreSQL с кешем вполне достаточна. Unleash, LaunchDarkly и другие решения нужны, когда таблицы начинает не хватать.

Сравнение стратегий

RollingBlue-greenCanary+ Feature flags
Радиус ошибкиВсе, постепенноВсе, мгновенноМалый процентУправляемый сегмент
Скорость откатаМинутыСекундыАвтоматикаСекунды
Дополнительные затратыНетДвойная ёмкостьМаршрутизация + метрикиДисциплина флагов
Когда применятьПо умолчаниюДорогая ошибкаБольшой трафик, зрелые метрикиВсегда полезны

Практичная связка для большинства команд: rolling + feature flags. Canary — когда трафик и метрики дозрели. Blue-green — точечно для самых критичных систем.

Типичные ошибки

Canary без формальных критериев. «Посмотрим в Grafana» — это не canary, это надежда. Если нет порогов для автоматического продвижения или отката, canary не работает как защита.

Blue-green с несовместимой миграцией схемы. Переключили на blue, мигрировали схему — старый green больше не работает с новой схемой. Откат за секунды превращается в восстановление из резервной копии.

Вечные флаги. Сотня флагов, переплетённых условиями, сложнее любого монолита. Флаги — временный инструмент.

Воркеры под canary. Потребитель очереди в двух версиях, по-разному обрабатывающих одни данные — рассинхрон. Для фоновых обработчиков используют Recreate или переключение, не распределение по процентам.

Коротко

  • Стратегии релиза отличаются радиусом ошибки и скоростью отката.
  • Rolling — реплики меняются по одной; откат занимает минуты; выбор по умолчанию.
  • Blue-green — два окружения, трафик переключается целиком; откат за секунды; цена — двойная ёмкость.
  • Canary — новая версия получает малый процент трафика и растёт по метрикам; требует маршрутизации и надёжных метрик.
  • Feature flags — деплой и включение функции разделены; флаги включают/выключают за секунды без деплоя.
  • При rolling и canary две версии работают одновременно — схема БД и контракты должны понимать обе.
  • Флаги — временный инструмент: заводить с датой удаления и убирать после раскатки.

Что почитать дальше

  • Деплой в Kubernetes — механика rolling update и совместимые миграции.
  • Принципы конвейера — деплой и релиз как разные события.
  • Ветки и релизный цикл — как код доезжает до этих стратегий.