Короткий ответ
Нужно проектировать шаги так, чтобы для каждого из них было известно, как его откатить или безопасно повторить (идемпотентно), использовать trap для гарантированной очистки/отката при любом сбое, и там, где это возможно, делать потенциально опасные шаги (например, миграции схемы БД) обратимыми и отделёнными от шагов, которые сложно откатить.
Как это работает подробнее
trap 'cleanup' ERR EXIT в bash позволяет выполнить функцию очистки при любом выходе из скрипта, в том числе аварийном, что даёт шанс откатить уже выполненные шаги или хотя бы оповестить об их состоянии. Для шагов, которые физически нельзя откатить мгновенно (необратимая миграция схемы), стратегия обычно сдвигается в сторону предварительной валидации и поэтапного раскатывания (сначала на части трафика), а не "чистого" отката.
Пример кода
#!/usr/bin/env bash
set -euo pipefail
STATE_FILE="/var/run/deploy.state"
cleanup() {
if [ $? -ne 0 ]; then
echo "Деплой упал на шаге: $(cat "$STATE_FILE" 2>/dev/null || echo unknown)"
echo "Проверьте состояние вручную перед повторным запуском"
fi
}
trap cleanup EXIT
echo "migration" > "$STATE_FILE"
run_db_migration
echo "config" > "$STATE_FILE"
update_service_config
echo "restart" > "$STATE_FILE"
restart_service
rm -f "$STATE_FILE"
echo "Деплой успешно завершён"Что использовать на практике
- Рекомендация. хороший подход — логировать, какой шаг выполняется и завершён успешно (например, писать маркеры в файл состояния), чтобы при повторном запуске после сбоя скрипт мог продолжить с места сбоя, а не повторять уже выполненные необратимые шаги; это требует, чтобы каждый шаг был либо идемпотентным, либо явно пропускался при повторном запуске, если уже выполнен.
Подводные камни
- Ошибка. писать скрипт деплоя как линейную последовательность команд без обработки ошибок между ними, из-за чего падение на шаге 3 оставляет систему с новой конфигурацией, но со старым кодом сервиса (или наоборот); пытаться "откатить" уже применённую необратимую миграцию схемы базы данных автоматически без участия человека, что может привести к потере данных.