Прогрессивная доставка: канарейка, blue-green и флаги
Задеплоить код — не то же, что зарелизить. Раздели их: выкатывай тёмным, открывай постепенно — канарейка 1-5% с авто-откатом по метрикам, blue-green для мгновенного флипа назад, фича-флаг как kill switch без редеплоя. Ограничь радиус поражения, а не ставь на staging.
Тег нарезан, пайплайн зелёный, артефакт опубликован. Команда сделала всё, чему учили прошлые два урока — чистый SemVer, отревьюенный тег, подписанный релиз — а затем выкатила новую версию на 100% прода одним махом, потому что так деплой-скрипт делал всегда. За пятнадцать секунд error rate ушёл вертикально. Баг сериализации, который срабатывает только при реальных конкурентных записях — невидимый в однопоточном smoke-тесте staging — теперь падал для каждого пользователя. У них не было ни гейта, ни среза, чтобы откатить, только один рычаг, и он уже был опущен до упора. Откат был лихорадочным редеплоем предыдущей версии под крик инцидент-канала: минуты полного простоя, 100% пользователей задеты. Баг не был настоящим провалом. Провалом было выкатить его на всех сразу.
Деплой — это не релиз
Вот рамка, на которой держится весь урок. Деплой — это размещение нового кода на серверах: бинарь работает, контейнер поднят. Релиз — это направление пользовательского трафика к этому коду: решение, кто на самом деле на него попадёт. Прошлые уроки довели тебя до опубликованного артефакта; они не сказали, что хоть один пользователь уже должен его увидеть. Сеньорский ход — считать это двумя отдельными переключателями. Выкатывай код тёмным — задеплоенным, но без трафика — и затем крути ручку трафика вверх постепенно, следя за сигналами, готовый повернуть её обратно в тот момент, когда они деградируют. Всё ниже — это разные механизмы одной идеи: отдели «код здесь» от «пользователи на нём» и навесь ограничитель радиуса поражения на второй переключатель.
Четыре стратегии выката
# big-bang (recreate): останови всё, запусти новую версию — простой + 100% радиуса поражения
strategy:
type: Recreate # старые поды умирают, новые стартуют: зазор, где никто не обслуживает
# rolling: заменяй инстансы партиями — без простоя, но две версии работают одновременно
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 0 # почти дефолт k8s: инкрементально, но БЕЗ явного гейта трафика- Big-bang (recreate / всё-сразу): останови старую версию, запусти новую. Есть зазор простоя, и в момент, когда она поднялась, радиус поражения — 100%: все пользователи разом на коде, непроверенном в проде. Это провал из Hook. Приемлемо только для крошечных или внутренних приложений, где сбой не страшен.
- Rolling: заменяй инстансы партия за партией (дефолт Kubernetes). Без простоя — поэтому кажется безопасным — но две версии работают одновременно, так что они обязаны быть обратно/прямо совместимы, а плохая версия всё равно доходит до пользователей инкрементально без явного гейта, следящего, здорова ли она. Rolling управляет доступностью, а не риском.
- Blue-green: подними два полных окружения. Задеплой в GREEN (простаивающее), прогони smoke-тест в изоляции, затем атомарно переключи балансировщик / роутер с BLUE на GREEN. Переключение мгновенно, и откат тоже мгновенный — переключи роутер обратно на BLUE. Цена — примерно
2xинфраструктуры на время переключения, и трудная часть — общее состояние: обе версии обычно ходят в одну базу, так что изменение схемы должно быть совместимо и с BLUE, и с GREEN через весь флип. - Canary (канарейка): направь маленький процент реального трафика (1-5%) на новую версию, следи за её error rate, латентностью и бизнес-метриками против старой, затем поднимай
5 → 25 → 50 → 100%, если остаётся здоровой, или прерывай, если нет. Канарейка ловит проблемы на реальном продовом трафике при крошечном радиусе поражения — ровно тот класс багов, что staging пропустил.
▸Почему это работает
Зачем разделять деплой и релиз флагами и канарейками, вместо того чтобы просто тестировать жёстче в staging? Потому что staging структурно не может воспроизвести реальный микс трафика, конкуренцию, форму данных и масштаб прода — а именно там живут дорогие баги. Гонки, contention по горячему ключу, сериализатор, который ломается только при одновременных записях, единственный кривой payload, существующий в реальной таблице пользователей — ни один из них не проявится против восьми засеянных строк и одного тестового потока. Прогрессивная доставка принимает это и считает прод финальным тестом, но с привинченным ограничителем: открой изменение тонкому срезу, измерь его против baseline, и если оно плохое — ты сдержал ущерб этим срезом, а не поставил всю свою пользовательскую базу на то, что staging был репрезентативен. Тестировать жёстче — хорошо; просто это заканчивается раньше, чем баги, которые тебя кладут.
Фича-флаги: переключатель релиза, которому не нужен редеплой
Канарейка двигает трафик на уровне инфраструктуры. Фича-флаг переносит решение о релизе на уровень приложения: оберни новое поведение в условие, задеплой с флагом OFF, и код едет тёмным, пока пользователи продолжают получать старый путь. Затем ты переключаешь флаг — для 1% пользователей, потом 10%, потом всех — независимо от любого деплоя. Новый код уже был на каждом сервере; ты просто меняешь значение конфига.
// новый код задеплоен везде, но тёмный, пока флаг его не откроет
if (flags.isEnabled("new-checkout-pipeline", { userId })) {
return runNewCheckout(order); // открыт на 1% -> 10% -> 100% через конфиг, без редеплоя
}
return runLegacyCheckout(order); // все остальные, без измененийДва свойства делают это любимым инструментом сеньора. Первое — это мгновенный kill switch: если new-checkout-pipeline ведёт себя плохо, ты переключаешь флаг обратно в OFF, и каждый пользователь на старом пути за секунды — без прогона пайплайна, без пересборки образа, без редеплоя. Второе — таргетинг по пользователю / проценту, что и питает A/B-тесты и dark launch (гоняй новый путь, отбрасывай его результат, просто измеряй его нагрузку). Если у тебя есть флаг, который «временно включили» полгода назад — ты уже в колонке затрат. Цена реальна: долг флагов. Каждый флаг — это ветка в коде и строка в конфиге; устаревшие флаги, которые так и не убрали, превращаются в комбинаторный бардак, который никто не смеет удалить. Флаги — это рычаг релиза, а не постоянный дом: открой их, узнай что нужно, потом удали.
Автоматический откат: решает гейт, а не человек
Всё выше просто безопаснее, если человек смотрит. Сеньорская версия — автоматическая. Контроллер прогрессивной доставки — Argo Rollouts, Flagger или логика в твоём деплой-пайплайне — поднимает канарейку по расписанию и на каждом шаге прогоняет аналитический запрос к твоим метрикам: error rate, p99 латентность, скорость прожига SLO. Если запрос пробивает порог, контроллер авто-прерывает и откатывает — сдвигает трафик обратно на стабильную версию — без человека в петле. Вот ключевое число, которое надо усвоить: решение об откате завязано на метрики, а не на ощущения и не на то, что кто-то случайно пялится в дашборд в 16:00.
# Канарейка Argo Rollouts с шагом анализа по метрике (набросок)
strategy:
canary:
steps:
- setWeight: 5 # 5% трафика на новую версию
- analysis: # запрос к Prometheus; авто-прерывание при провале
templates: [{ templateName: error-rate }]
- setWeight: 25
- analysis: { templates: [{ templateName: error-rate }] }
- setWeight: 50
- setWeight: 100 # полное продвижение только если каждый гейт прошёлПрогони инцидент из Hook через это. Плохая версия едет на 5% трафика. Баг сериализации срабатывает под реальной конкуренцией, и аналитический запрос видит, как error rate на канарейке скачет за порог за секунды. Контроллер прерывает на 5% и сдвигает трафик обратно на стабильную — автоматически. Радиус поражения: 5% пользователей на несколько секунд вместо 100% пользователей на минуты. Баг всё равно случился; прогрессивная доставка просто сделала его не-событием.
| Стратегия | Простой | Радиус поражения плохой версии | Откат | Цена / подвох |
|---|---|---|---|---|
| Big-bang | Да (зазор) | 100% мгновенно | Редеплой старой (медленно) | Нет гейта; ок лишь для крошечных/внутренних |
| Rolling | Нет | Растёт инкрементально, без гейта | Откати партии | Две версии живы; должны быть совместимы |
| Blue-green | Нет | 100% на флипе (но сначала smoke-тест) | Мгновенно: флипни роутер назад | 2x инфры; общая БД — трудная часть |
| Canary | Нет | 1-5%, сдержан гейтом | Авто-прерывание при пробое метрики | Нужны хорошие метрики + автоматизация |
Тебе нужно выкатить рискованное изменение на высоконагруженный сервис с минимально возможным радиусом поражения и мгновенным откатом. Как ты его выкатишь?
В прогрессивной доставке в чём разница между деплоем и релизом и что разделяет фича-флаг?
Баг сериализации проявляется только при реальных конкурентных записях; его big-bang деплой на 100% положил весь сайт. Какой выкат сдержал бы его и по какому сигналу?
- 01Объясни рамку «деплой против релиза» и сравни четыре стратегии выката (big-bang, rolling, blue-green, canary) по простою, радиусу поражения и откату.
- 02Что разделяет фича-флаг и какова его цена, и как автоматический откат по метрикам превращает инцидент из Hook в не-событие?
Задеплоить код — не то же самое, что зарелизить: деплой кладёт бинарь на серверы, релиз направляет к нему пользовательский трафик, и сеньорский ход — считать их двумя переключателями: выкатывай тёмным, потом открывай постепенно, следя за сигналами, готовый быстро откатить. Четыре стратегии выката торгуют простоем против радиуса поражения: big-bang останавливает старую и запускает новую с зазором простоя и 100% радиуса (ок лишь для крошечных/внутренних приложений); rolling заменяет инстансы партия за партией без простоя, но с двумя совместимыми версиями и без явного гейта, управляя доступностью, а не риском; blue-green поднимает два полных окружения и атомарно флипает роутер с BLUE на GREEN для мгновенного переключения и мгновенного флипа назад, ценой ~2x инфры и трудной проблемы общей базы; canary направляет 1-5% реального трафика на новую версию, следит за error rate / p99 / бизнес-метриками против baseline и поднимает 5/25/50/100%, если здорова, или прерывает, если нет, ловя баги, которые staging не может воспроизвести, при крошечном радиусе поражения. Фича-флаги отделяют релиз от деплоя на уровне приложения — деплой с флагом OFF, потом переключай по пользователю или проценту без редеплоя — давая мгновенный kill switch и таргетинг A/B/dark-launch, ценой долга флагов, который надо чистить. Сеньорская версия автоматическая: контроллер поднимает канарейку и прогоняет аналитический запрос по error rate / p99 / прожигу SLO, авто-прерывая и откатывая на стабильную при пробое метрики без человека в петле — решение завязано на метрики, а не на ощущения. Канарейка на 5% с этим гейтом поймала бы баг сериализации из Hook при 5% радиуса поражения на секунды, вместо того чтобы big-bang на 100% положил весь сайт на минуты. Теперь, когда увидишь деплой-скрипт, выкатывающий сразу на 100%, — будешь знать, какого рычага не хватает и чего стоило бы его иметь.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.