Что записывать при каждом деплое, чтобы откат занимал минуты
Через полчаса после деплоя в проде что-то пошло не так, и первый вопрос дежурного — «а что вообще выкатили и на что откатываться» — часто остаётся без быстрого ответа. Приходится поднимать историю чата, вспоминать, кто последний трогал сервер, сравнивать даты изменения файлов на диске. Это решаемо не героизмом в моменте, а тем, что фиксируется на каждом деплое заранее — коротким, но обязательным набором фактов, который превращает «непонятно, что откатывать» в одну команду.
Содержание
- Почему «просто задеплоили» — это яма, а не экономия времени
- Что именно фиксировать на каждом деплое
- Формат записи: где и как это хранить
- Автоматизация: запись как часть деплоя, а не отдельный шаг
- Как это выглядит на практике: чек-лист перед первым автодеплоем
- Почему это сокращает именно время решения, а не только время исполнения
Почему «просто задеплоили» — это яма, а не экономия времени
Деплой без записи выглядит быстрее в моменте: одна команда, git pull или docker compose up -d, и сервис снова работает. Экономия — секунды. Цена этой экономии предъявляется не сразу, а в момент, когда деплой оказывается причиной проблемы, и решать нужно быстро, под давлением падающих метрик.
Без записи о деплое дежурный инженер восстанавливает картину раскопками: смотрит git log, пытается понять, какой коммит реально стоит на проде (а не просто последний в ветке — между ними мог быть незадеплоенный коммит), ищет в переписке, кто и когда катил, гадает, какая версия была «последней хорошей». На каждый из этих вопросов раскопки уходит несколько минут, а вопросов обычно четыре-пять — набегает 15-20 минут только на то, чтобы понять, куда откатываться, ещё до самого отката.
С записью на каждый деплой ответ на все эти вопросы — это cat одного файла или один запрос к API деплой-инструмента. Время принятия решения падает с «нужно расследование» до «читаю факт». Это не абстрактная гигиена — это конкретная экономия времени именно в тот момент, когда время стоит дороже всего. Насколько дороже стоит время инцидента по сравнению с временем на подготовку, разобрано в статье про цену ручного деплоя в часах и инцидентах.
Что именно фиксировать на каждом деплое
Минимальный набор — пять пунктов. Меньше — уже недостаточно для быстрого решения, больше — обычно избыточная бюрократия, которую в реальности перестают заполнять после третьего деплоя.
- Версия или коммит, который выкатывается. Не «последний коммит в main» (это плавающая цель), а конкретный SHA или тег:
a3f9c21илиv2.14.3. Именно это должно превращаться в команду отката без домысливания. - Время начала деплоя. Точный timestamp, а не «около трёх». Нужен, чтобы сопоставить с графиками мониторинга и логами — «проблема началась в 14:32, деплой стартовал в 14:28» это либо подтверждает связь, либо снимает подозрение с деплоя за секунды.
- Кто выкатывает. Не для поиска виноватого, а чтобы было к кому обратиться с вопросом «а ты ничего руками не менял помимо стандартного деплоя» — иногда именно ручное отклонение от процесса и есть причина.
- Краткое описание изменений. Одна-две строки: «добавили кэш для /api/search», «обновили зависимость pg до 16», «миграция: новая колонка в orders». Без этого решение «чинить вперёд или откатывать» принимается вслепую — вы не знаете, тривиальное ли это изменение или рискованное.
- Ссылка на предыдущую рабочую версию. Не «предыдущий коммит в git log» (это может быть коммит, который никогда не деплоился), а именно тот SHA/тег/образ, который реально стоял в проде и был подтверждён рабочим прямо перед текущим деплоем.
Отдельно стоит миграция базы данных, если она была частью деплоя — её нужно фиксировать так же явно: какая миграция накатилась, обратима ли она штатно (down-скрипт) или откат кода без отдельного плана отката схемы опасен. Это отдельная и более тяжёлая тема, разобранная в статье про план отката миграции.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверФормат записи: где и как это хранить
Формат важнее, чем кажется — запись, которую неудобно читать в момент инцидента, эквивалентна отсутствию записи. Три рабочих варианта, от простого к сложному.
Файл deploy.log на сервере или в общем хранилище. Простейший вариант, но рабочий для небольших команд — построчный append, один деплой — одна строка.
2026-08-29T14:28:03Z app=api version=a3f9c21 prev=7b1e004 by=telim desc="кэш для /api/search"
2026-08-29T16:02:41Z app=api version=b02f88a prev=a3f9c21 by=telim desc="фикс таймаута в /health"
Найти предыдущую версию для отката — одна команда:
tail -2 /var/log/deploy/api.log | head -1 | grep -oP 'version=\K\S+'
Git-теги на каждый деплой — вариант, если код и так живёт в git и CI/CD уже настроен через него. Тег deploy-prod-2026-08-29-1428 создаётся автоматически в пайплайне и указывает ровно на задеплоенный коммит, а не на голову ветки.
git tag -a deploy-prod-2026-08-29-1428 -m "prev=deploy-prod-2026-08-29-1602 by=telim desc: кэш для /api/search"
git push origin deploy-prod-2026-08-29-1428
# найти предыдущий деплой-тег
git tag -l 'deploy-prod-*' --sort=-creatordate | sed -n 2p
Структурированная запись через инструмент деплоя (Argo CD, GitLab CI, собственный скрипт) — самый надёжный вариант, потому что запись становится частью системы, а не отдельного файла, который можно забыть. Если у вас уже GitOps-подход, история ревизий Application в Argo CD фактически и есть эта запись — подробнее в статье про основы Argo CD и GitOps.
Автоматизация: запись как часть деплоя, а не отдельный шаг
Ключевая практическая мысль этой статьи: любая запись, которую нужно делать руками отдельной командой после деплоя, рано или поздно перестаёт делаться. Не потому что команда ленивая, а потому что в моменте деплой уже «прошёл успешно», внимание переключилось на следующую задачу, и ручной шаг «не забыть записать» проигрывает конкуренцию за внимание. Решение — встроить запись в сам скрипт или пайплайн деплоя так, чтобы деплой без записи был физически невозможен, а не «настоятельно рекомендован».
Пример простого скрипта деплоя, где запись — обязательный первый и последний шаг, а не постскриптум:
#!/usr/bin/env bash
set -euo pipefail
APP=api
NEW_VERSION="$1"
DESC="${2:-без описания}"
LOG=/var/log/deploy/${APP}.log
DEPLOYER=$(whoami)
# 1. Узнаём текущую версию ДО деплоя — это и есть будущая "ссылка на откат"
PREV_VERSION=$(docker inspect --format='{{index .Config.Image}}' ${APP} 2>/dev/null | grep -oP ':\K.*' || echo "unknown")
TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
# 2. Пишем в лог ДО того, как что-то менять на сервере
echo "${TS} app=${APP} version=${NEW_VERSION} prev=${PREV_VERSION} by=${DEPLOYER} desc=\"${DESC}\" status=started" >> "$LOG"
# 3. Сам деплой
docker compose pull ${APP}
docker compose up -d --force-recreate ${APP}
# 4. Проверка здоровья и финальная отметка статуса
if curl -sf http://127.0.0.1:8080/healthz > /dev/null; then
echo "${TS} app=${APP} version=${NEW_VERSION} status=ok" >> "$LOG"
else
echo "${TS} app=${APP} version=${NEW_VERSION} status=FAILED_HEALTHCHECK" >> "$LOG"
exit 1
fi
Смысл в порядке действий: запись о том, что деплой начался, и о том, какая версия была раньше, пишется до того, как что-то меняется на сервере. Если скрипт упадёт на середине — в логе всё равно останется факт «деплой такой-то версии стартовал, предыдущая была вот эта», и для отката этого достаточно, даже если финальная отметка об успехе так и не появилась.
Если деплой идёт через CI/CD (GitLab CI, Gitea Actions, Drone), тот же принцип реализуется через встроенные переменные пайплайна — CI_COMMIT_SHA, имя запустившего, время старта джобы уже есть в системе, нужно только явно писать их в артефакт или комментарий к деплою, а не полагаться, что «CI и так всё помнит» — через полгода найти нужный запуск среди тысяч логов пайплайна сложнее, чем прочитать одну строку выделенного deploy-лога. Частые грабли при настройке автодеплоя из git, включая ситуации, когда пайплайн деплоит не то, что ожидалось, разобраны в статье про автодеплой из git на сервере.
Как это выглядит на практике: чек-лист перед первым автодеплоем
Прежде чем встраивать запись в существующий процесс деплоя, стоит проверить пять вещей — без них автоматизация записи выглядит готовой, но на практике не даёт быстрого отката.
| Проверка | Зачем | Как проверить | |
|---|---|---|---|
| Предыдущий образ/пакет реально доступен локально | Иначе откат — это скачивание заново, минуты превращаются в 15-30 | `docker images \ | grep myapp`, проверить наличие тега prev-версии |
Версии зафиксированы, а не :latest | :latest на проде — вы не знаете, что там реально стоит | docker inspect на реальный дайджест, apt-mark showhold | |
| Лог деплоя пишется атомарно | Параллельные деплои не должны портить друг другу записи | Использовать flock вокруг записи в общий лог-файл | |
| Ссылка на предыдущую версию проверяема командой | Не текстовое описание «была версия попроще», а точный SHA/тег | Ручной прогон команды отката на staging перед боевым использованием | |
| Миграции БД зафиксированы отдельно от кода | Откат кода и откат схемы — разные операции с разным риском | Отдельная строка/поле migration= в записи деплоя |
Правило flock особенно легко пропустить, если деплоев в день немного — а потом два параллельных деплоя (ручной хотфикс и автодеплой из CI) одновременно пишут в файл и портят последнюю строку ровно в момент, когда она нужна для отката.
(
flock -x 200
echo "${TS} app=${APP} version=${NEW_VERSION} ..." >> "$LOG"
) 200>"${LOG}.lock"
Почему это сокращает именно время решения, а не только время исполнения
Разница между «откатом за 4 минуты» и «инцидентом на час» лежит не столько в скорости команды отката (docker compose up выполняется секунды в обоих случаях), сколько во времени, которое уходит на принятие решения — что откатывать, на какую версию, есть ли риск в самом откате из-за незавершённой миграции. Подробный разбор механики самого отката и того, из чего складывается его время, есть в статье про то, что обновление положило прод и как откатиться.
Запись при деплое напрямую сокращает именно фазу решения:
- Вопрос «что вообще выкатили» закрывается чтением одной строки, а не поиском по чатам и
git log. - Вопрос «на что откатываться» закрывается полем
prev=— не нужно решать в моменте, какая версия была последней стабильной. - Вопрос «связан ли деплой с проблемой» закрывается сопоставлением timestamp деплоя с началом деградации на графике мониторинга — за секунды, а не через «кажется, где-то около обеда катили».
- Вопрос «это рискованно откатывать» закрывается кратким описанием изменений и отметкой о миграции — сразу видно, что откат кода без отдельного разбора схемы БД в этот раз небезопасен.
В стрессовой ситуации, когда дежурный один и решение нужно за минуты, а не через созвон с командой, каждый из этих вопросов, закрытый чтением факта вместо расследования, — это минуты, которые не набегают в общий счёт простоя. Первые минуты инцидента вообще устроены так, что заранее подготовленные ответы решают больше, чем квалификация конкретного инженера на смене — этот принцип подробно разобран в статье про первые 15 минут инцидента.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Нужно ли фиксировать деплой, если он совсем мелкий — например, только конфиг nginx?
Да, но запись может быть короче: версия конфига (коммит в git, если конфиги версионируются) и timestamp достаточно, полноценное описание не обязательно для тривиальных правок.
Что делать, если деплоев в день немного и кажется, что можно запомнить и так?
Память подводит именно в момент инцидента, когда деплоев за последнюю неделю было пять и непонятно, какой из них последний перед проблемой — а «немного деплоев» означает, что вести лог дешевле, чем при частых релизах, аргумент работает в обратную сторону.
Достаточно ли просто git-истории коммитов вместо отдельной записи о деплое?
Нет — git-лог показывает, что закоммичено, а не что реально задеплоено и когда. Между коммитом и деплоем на прод может быть разрыв в днях, и не каждый коммит в main вообще выкатывался.
Как долго хранить историю деплоев?
Для оперативного отката достаточно последних 10-20 записей на сервис; для разбора долгосрочных трендов (участились ли откаты, кто чаще катит рискованные изменения) стоит хранить дольше — это уже вопрос политики хранения логов, а не техническое ограничение.
Что, если инструмент деплоя (например, Argo CD) уже хранит историю ревизий сам?
Тогда отдельный лог может быть избыточен — достаточно убедиться, что история доступна быстро (не нужно копаться в UI пять минут) и включает то же самое: версию, время, автора и что менялось.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →