/posts/rollback-before-deploy

$ cat post.md

automation

Почему rollback для меня важнее, чем красивый deploy

Спокойный прод начинается не с эффектной выкладки, а с понятного ответа на вопрос: что я сделаю через три минуты, если новая версия окажется ошибкой.

Раньше я думал о деплое как о движении вперёд. Главное, чтобы новая версия собралась, контейнеры стартовали, миграции прошли и после релиза всё открылось без явной ошибки. В такой картине мира rollback выглядел запасной кнопкой на случай очень плохого дня.

Практика быстро поставила всё на место. В проде проблема редко выглядит как красивое падение с понятным stack trace. Гораздо чаще это странная деградация: что-то стало медленнее, часть запросов ведёт себя нестабильно, фоновые задачи перестали успевать, а внешне сервис ещё “жив”. Именно в такие моменты становится видно, насколько деплой зрелый на самом деле.

С тех пор я смотрю на релиз не через вопрос “как его выкатить”, а через вопрос “как я верну систему в предыдущее рабочее состояние, если через три минуты пойму, что ошибся”. Если ответ расплывчатый, значит deploy ещё не готов.

Что у меня раньше считалось хорошим deploy

Долгое время я считал хорошей выкладкой ту, которая выглядит аккуратно снаружи:

  • build зелёный;
  • контейнеры собрались без ошибок;
  • compose поднялся;
  • health endpoint отвечает;
  • новая версия визуально открывается в браузере.

Такой подход работает до первого неприятного релиза. Проблема в том, что он слишком сосредоточен на счастливом пути. Он отвечает на вопрос “как попасть в новую версию”, но почти ничего не говорит о том, как быстро и без паники выйти из неё назад.

Снаружи этот deploy может выглядеть очень современно: красивые шаги в CI, переменные окружения, автоматическая выкладка, несколько проверок после релиза. Но если в этот момент никто не может уверенно сказать, какую команду запускать для отката и какое состояние считать предыдущим рабочим, то система всё ещё хрупкая.

Где это ломалось в реальности

Самые неприятные выкладки у меня редко были связаны с явной катастрофой. Обычно сценарий выглядел тише:

  • релиз формально успешный;
  • сервис не лежит;
  • но часть поведения уже “поползла”;
  • и через несколько минут становится ясно, что оставаться на новой версии опаснее, чем откатиться.

Именно здесь всплывают плохие вопросы:

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

Когда на эти вопросы нет короткого ответа, команда начинает импровизировать. А импровизация в проде почти всегда дороже нескольких минут подготовки до релиза.

Что я теперь проверяю до релиза

Перед деплоем мне важно понимать несколько простых вещей.

Какая версия считается предыдущей рабочей

Это звучит очевидно, но именно здесь начинается путаница. “Предыдущая” не всегда значит “предыдущий commit”. Иногда между ними были миграции, ручные правки конфига или частично докатившийся релиз.

Для себя я стараюсь держать одно правило: предыдущая рабочая версия должна быть зафиксирована так, чтобы её можно было назвать без обсуждения. Tag, commit hash, image tag, release ref — не так важно, если ответ однозначен.

Каким действием я возвращаю её в трафик

Rollback не должен требовать археологии по shell history. Если возвращение предыдущей версии зависит от пяти ручных действий, которые нужно вспомнить в правильном порядке, это слишком дорогая схема для реального инцидента.

Мне нравится, когда rollback можно объяснить в одну-две команды и потом проверить таким же коротким smoke test.

Нужно ли трогать данные

Это самый неприятный слой. Откат контейнера прост сам по себе, но если релиз уже изменил схему, формат или ожидания приложения относительно данных, rollback приложения может оказаться только половиной решения.

Именно поэтому я стараюсь разделять:

  • rollback приложения;
  • rollback схемы;
  • ручные recovery-действия.

Если это не разделено заранее, в момент проблемы очень легко сделать ситуацию хуже.

Кто понимает, что rollback действительно помог

Возврат старой версии сам по себе ещё не завершает инцидент. Нужно знать, что именно проверять после отката:

  • время ответа;
  • ошибки в логах;
  • поведение фоновых задач;
  • ключевые пользовательские сценарии.

Без этого rollback превращается в ритуал: что-то откатили, но не до конца ясно, стало ли лучше.

Что изменилось в моём подходе к deploy flow

Со временем мои deploy-сценарии стали проще не потому, что я полюбил минимализм как идею, а потому что устал от лишней неопределённости.

Деплой должен быть скучным

Если в день релиза мне хочется “заодно” поправить конфиг, перестроить структуру каталогов, обновить образ базы и чуть улучшить nginx, это уже тревожный сигнал. Чем больше переменных едет вместе с релизом, тем хуже читается причина проблем.

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

Rollback должен быть частью обычного сценария

Я больше не воспринимаю откат как редкую аварию. Это обычная ветка того же процесса. Если новый релиз не оправдал ожиданий, я должен иметь право быстро и спокойно вернуться назад, а не героически “дочинять” его уже на проде.

Ручная магия должна исчезать первой

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

Минимум, без которого я не считаю deploy готовым

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

  • понятно, что считается предыдущей рабочей версией;
  • есть короткая команда или сценарий для отката;
  • релиз не смешан с “небольшими” инфраструктурными улучшениями;
  • после выкладки есть минимальный smoke test;
  • после rollback тоже есть проверка, что система действительно вернулась в норму.

Это не выглядит впечатляюще в презентации. Но именно такой набор чаще всего и спасает от длинной ночи на сервере.

Почему этот подход оказался полезнее “красивого” CI

Я люблю аккуратные пайплайны и понятную автоматизацию, но со временем понял, что зрелость инфраструктуры ощущается не там, где всё идеально на happy path, а там, где система спокойно переживает ошибку.

В этом смысле rollback оказался для меня гораздо честнее любого красивого deploy dashboard. Он сразу показывает, насколько хорошо вы понимаете собственный релизный процесс, насколько у вас прозрачен прод и насколько мало в нём скрытой магии.

Главный вывод

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

Красивый deploy может впечатлять. Понятный rollback почти всегда полезнее.

$ cat /etc/motd

infraTales

Личный блог о DevOps, инфраструктуре, инструментах и инженерной практике.