Legan Studio
Все статьи
~ 6 мин чтения

Canary и blue-green деплой

Стратегии безопасного релиза: blue-green, canary, rolling, instant rollback. Реализация на Kubernetes, Docker Compose, nginx и без оркестратора.

  • веб
  • devops
  • архитектура

«Релиз в пятницу» — фраза, после которой опытный девопс начинает нервно искать билет на Бали. Но если ваш процесс деплоя устроен правильно, релиз в любой день недели — это рутина, а не риск. Разбираем стратегии деплоя, которые делают релиз безопасным.

Зачем что-то менять

Простой деплой:

  1. Останавливаем старую версию.
  2. Поднимаем новую.
  3. Если новая сломана — откатываемся, теряем время и пользователей.

Между шагами 1 и 2 — даунтайм. На шаге 3 — паника. Хотим: ноль даунтайма, мгновенный откат, проверка на части трафика перед полным переключением.

Стратегии

СтратегияДаунтаймОткатСложностьКогда
Recreate (обычный)дадолгийминимумdev, staging
Rollingминимальныйсреднийнизкаястандарт K8s
Blue-Greenнетмгновенныйсредняякритичные сервисы
Canaryнетмгновенныйвысокаябольшие проекты
A/B (на трафике)нетмгновенныйочень высокаяпродуктовые гипотезы

Rolling deploy

Стандарт Kubernetes. Поды старой версии заменяются по одному.

spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # сколько новых сверх лимита
      maxUnavailable: 0  # сколько одновременно недоступно

В каждый момент: 4 рабочих пода, постепенно меняются. Если новый под не проходит readiness probe — выкатка останавливается.

Минус: во время деплоя в проде одновременно работают и старая, и новая версии. Для несовместимых API/схем БД — проблема.

Blue-Green

Две идентичные среды. Blue — текущая в проде. Green — новая версия. Балансировщик переключает 100% трафика мгновенно.

Пользователи → LoadBalancer → Blue (v1.42)  ← активный
                              Green (v1.43) ← новый, прогревается

После прогрева и smoke-тестов — переключаем балансер: всё на Green. Blue остаётся работать ещё час (на случай отката).

Откат — два клика, обратно на Blue.

upstream backend {
  server blue:3000;       # активный
  # server green:3000;    # закомментирован
}

Reload nginx → переключение мгновенное. Без сброса соединений (graceful reload).

Минусы:

  • Ресурсов нужно в 2 раза больше (две среды).
  • Долгие соединения (WebSocket) обрываются при переключении.
  • Миграции БД сложнее: новая версия может ожидать новой схемы.

Canary

Постепенный rollout: 1% → 10% → 50% → 100%. Если на 1% видим проблему (рост ошибок, латенция, бизнес-метрики) — откатываемся, основной массе ничего не показано.

LoadBalancer:
  - 99% → v1.42 (стабильная)
  - 1% → v1.43 (canary)

Через 30 минут наблюдения за метриками — увеличиваем процент. Цикл может длиться часы или дни.

В Kubernetes — через Argo Rollouts или Flagger:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
spec:
  strategy:
    canary:
      steps:
      - setWeight: 5
      - pause: { duration: 5m }
      - setWeight: 25
      - pause: { duration: 10m }
      - setWeight: 50
      - pause: { duration: 30m }
      - setWeight: 100
      analysis:
        templates:
        - templateName: success-rate
        args:
        - name: service-name
          value: my-app

Если success-rate < 99% на каком-то шаге — автоматический rollback.

Без Kubernetes

На VPS с Docker Compose тоже можно делать blue-green, через два compose-проекта и переключение nginx upstream.

# скрипт deploy.sh
docker compose -p site-green pull
docker compose -p site-green up -d
sleep 10
curl -f http://green:3000/health || exit 1

# переключаем nginx
sed -i 's/blue:3000/green:3000/' /etc/nginx/conf.d/site.conf
nginx -s reload

# даём 5 минут на observation
sleep 300
docker compose -p site-blue down

Не идеально, но работает на маленьких проектах без K8s-головной боли.

Миграции БД

Главная сложность blue-green / canary: схема БД одна, версий приложения две.

Правило expand-migrate-contract:

  1. Expand: добавить новую колонку как nullable, новый код может с ней работать, старый игнорирует.
  2. Migrate: задеплоить новый код, который пишет в обе колонки.
  3. Contract: после полного перехода на новую — удалить старую колонку.

Это растягивает фичу на 3 деплоя, но избавляет от downtime.

Health checks и readiness

Без них любая стратегия превращается в обычный recreate. Балансировщик должен знать, готов ли под принимать трафик:

readinessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 5
livenessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 30
  periodSeconds: 10

/health должен реально проверять — не просто return 200, а пинг к БД, Redis, очереди. Лучше отдельные /ready (можно принимать трафик) и /live (приложение живо вообще).

Метрики для решения

Что смотреть во время canary:

  • HTTP 5xx rate (если выше базового — стоп).
  • Latency p95/p99 (если выросла — стоп).
  • Бизнес-метрика: конверсия checkout, успешность регистрации, добавления в корзину.
  • Логи ошибок (Sentry — алерт на новую ошибку в release X).

Автоматизация: Flagger/Argo сами читают Prometheus и решают. Без них — оператор смотрит дашборд и вручную нажимает «promote» или «rollback».

Откат — самое важное

Стратегия имеет смысл только если откат реально работает. Что нужно:

  • Образ предыдущей версии всегда доступен (не удалён из registry).
  • Конфиг и переменные окружения версионированы (не редактируются вручную).
  • Скрипт ./rollback.sh отлажен и регулярно тестируется.
  • Команда знает, кто и когда инициирует откат, нет долгих обсуждений.
  • Миграции БД совместимы назад минимум на 1-2 версии.

«Откатимся» без дисциплины миграций — гарантированный downtime, потому что новая версия сделала ALTER TABLE, которого старая не понимает.

Когда какую стратегию

Размер проектаРекомендация
MVP, 1 серверRecreate с быстрой пересборкой образа
Малый прод, 1-2 пользователя в секундуRolling в Docker Compose
Средний прод, важно не падатьBlue-Green
Большой прод, много фич/неделюCanary с автоматическим rollback
Финтех, медицина, госсекторCanary + extensive smoke + manual approve

Итого

Сложность стратегии должна соответствовать рискам. Не стройте Argo Rollouts на трёх пользователях — оверинжиниринг. Но и не запускайте recreate без даунтайма для приложения с 5к посещений в час — потеряете деньги. Blue-Green — отличный middle ground, доступен почти всем.

Частые вопросы

Можно ли делать blue-green на одном VPS?

Можно, два набора контейнеров на разных портах + nginx переключает. Памяти нужно в 2 раза больше (на время деплоя), CPU — почти столько же. На 4 ГБ RAM поднимется только маленькое приложение в двух копиях, для нормального — 8-16 ГБ. Альтернатива — арендовать второй VPS только на время деплоя.

Canary на маленьком проекте — оверкилл?

Да. Canary имеет смысл от ~10 деплоев в неделю и заметного трафика (тысячи RPS), где вы статистически быстро увидите ухудшение метрик на 1% трафика. Для проекта с 1 деплоем в неделю и 10 RPS на canary метрики не отличить от шума. Хватит blue-green с smoke-тестами.

Как делать rollback миграций БД?

Никак не делать «обратные миграции» автоматически — они почти всегда теряют данные. Правило: миграции совместимы назад. Если новая колонка nullable — старый код её игнорирует, можно откатываться. Если переименовали поле — сначала expand (новое поле, оба заполняются), потом deploy, потом contract — даже если откатились между шагами, всё работает.

Что делать с долгими WebSocket-соединениями при blue-green?

Drainage: новые соединения идут на green, старые остаются на blue до закрытия. Через 5-15 минут (или по достижении ~0 активных) гасим blue. Если простой висит часами (стрим, чат) — отправьте клиенту команду reconnect, тогда он переподключится на актуальную версию. WS-серверы (LiveKit, Socket.IO) обычно поддерживают graceful drain.

Argo Rollouts или Flagger?

Argo Rollouts — более гибкая декларативная модель, лучше интегрируется с Argo CD. Flagger — проще, тесная интеграция с Istio/Linkerd/NGINX. Если уже на Argo CD — Argo Rollouts. Если service mesh — Flagger. Для не-K8s — обе не подходят, смотрите Spinnaker или самописные скрипты.

Какой минимум health-check должен быть?

/health — приложение запущено и принимает HTTP. Этого мало. Лучше /ready — может ли реально обслуживать (есть коннект к БД, инициализированы кеши, доступны критичные внешние API). Правило: если probe вернул 200, но первый же запрос даёт 500 — это сломанный probe, переписывайте.

Сколько времени занимает blue-green деплой?

На небольшом приложении (Node.js + Postgres) — 30-60 секунд от push до переключения трафика, плюс 5-15 минут наблюдения за метриками green-окружения перед декомиссионированием blue. На больших образах (200-500 МБ) — pull занимает дольше, плюс прогрев JIT и кешей. Цельтесь в полный цикл деплоя «push → fully promoted» 10-30 минут.