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

Сценарный вопрос уровня middle+: интервьюер хочет услышать не список стратегий, а обоснованный выбор под конкретные условия.

Вопрос: «Какие стратегии деплоя вы знаете? Какую выберете для платёжного сервиса, а какую — для внутренней админки, и почему?»

Что на самом деле проверяет интервьюер

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

Развёрнутый ответ: четыре стратегии

Recreate

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

Rolling update

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

Blue-green

Поднимается полная копия окружения с новой версией (green) рядом с текущей (blue). После прогона проверок трафик переключается целиком — на балансировщике или сменой селектора Service. Старое окружение остаётся нетронутым.

# версия зашита в метку пода, Service выбирает нужный цвет
kubectl apply -f deploy-green.yaml        # поднять новую версию рядом
kubectl port-forward deploy/api-green 8080:8080   # проверить вживую

# переключение всего трафика одной командой
kubectl patch svc api -p '{"spec":{"selector":{"app":"api","version":"green"}}}'

# откат — обратный patch, секунды
kubectl patch svc api -p '{"spec":{"selector":{"app":"api","version":"blue"}}}'

Плюсы: мгновенный откат и возможность полноценно проверить новую версию до подачи трафика. Минус: на время выкатки нужен двойной запас мощности, а это деньги. Отдельный нюанс — долгоживущие соединения и сессии: при резком переключении их нужно куда-то деть, поэтому состояние сессий выносят в общий Redis.

Canary

Новая версия получает маленькую долю реального трафика — 1%, 5%, 25% — и на каждом шаге сравниваются метрики ошибок и задержек с контрольной группой. Если всё в порядке, доля растёт; если метрики поплыли, трафик возвращается на старую версию.

Это лучший вариант для платёжного сервиса: радиус поражения минимален. Ошибку, которую не поймали тесты, увидит 1% пользователей, а не все. Цена — сложность: нужны маршрутизация по весам (Ingress с весами, service mesh, Argo Rollouts или Flagger) и, главное, метрики, по которым автоматика принимает решение. Canary без автоматического анализа метрик — это просто «выкатили и смотрим в графики руками».

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: api
spec:
  replicas: 10
  strategy:
    canary:
      analysis:
        templates:
          - templateName: error-rate      # автооткат по метрике ошибок
      steps:
        - setWeight: 5
        - pause: { duration: 5m }
        - setWeight: 25
        - pause: { duration: 10m }
        - setWeight: 50
        - pause: { duration: 10m }

Сравнительная таблица

СтратегияПростойДоп. мощностиСкорость откатаРадиус поражения
Recreateестьнетмедленно100%
Rollingнетнебольшиесреднерастёт постепенно
Blue-greenнет×2секунды100% после переключения
Canaryнетнебольшиебыстроминимальный

Отдельно: feature flags и миграции БД

Сильный ход в ответе — разделить деплой (код доехал до сервера) и релиз (функциональность стала доступна пользователям). Feature flag позволяет выкатить код выключенным и включить его отдельным действием, мгновенно погасив при проблемах — без всякой пересборки.

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

Типичные ошибки кандидатов

  • Называют стратегии списком, но не могут объяснить, чем платят за каждую.
  • Путают blue-green и canary: в первом трафик переключается целиком, во втором — долями.
  • Забывают про удвоение ресурсов при blue-green.
  • Предлагают canary там, где нет метрик и наблюдаемости — стратегия превращается в имитацию.
  • Не упоминают миграции базы данных, хотя именно они чаще всего срывают откат.
  • Не различают деплой и релиз, из-за чего любая правка требует полного цикла выкатки.

Как ответить кратко (20–30 секунд)

«Recreate — с простоем, для внутренних сервисов, где это допустимо. Rolling update — стандарт в Kubernetes: без простоя, но какое-то время работают две версии сразу и откат небыстрый. Blue-green — полная копия окружения и мгновенное переключение трафика, откат за секунды, но нужен двойной запас мощности. Canary — новая версия получает 1–5% трафика, метрики сравниваются автоматически, и при ухудшении идёт автооткат; для платёжного сервиса выберу именно её, потому что радиус поражения минимален. Для админки хватит rolling или recreate. И в любой стратегии помню, что откат кода не откатывает миграции — схему меняю по принципу expand-contract.»

Проверьте себя
1. Чем canary отличается от blue-green?
ACanary требует вдвое больше мощностей, blue-green — нет
BПри canary новая версия получает небольшую долю трафика с постепенным увеличением, при blue-green трафик переключается целиком
CBlue-green работает только в Kubernetes
DCanary не позволяет откатиться на предыдущую версию
2. Главная плата за стратегию blue-green — это:
AНеизбежный простой при переключении
BНеобходимость держать двойной запас мощностей на время выкатки
CНевозможность проверить новую версию до подачи трафика
DМедленный откат
3. Почему миграция, удаляющая колонку в базе, опасна при rolling update?
AПотому что миграции нельзя выполнять из контейнера
BПотому что во время выкатки одновременно работают старая и новая версии, и старая перестанет работать без этой колонки, а откат кода схему не вернёт
CПотому что Kubernetes блокирует запись в базу во время деплоя
DПотому что удаление колонки всегда требует простоя базы