MAATRIX / Блог / Цена ручного деплоя: считаем в часах и в инцидентах

Цена ручного деплоя: считаем в часах и в инцидентах

MAATRIX

«У нас маленький проект, зачем городить CI/CD ради пяти команд в терминале» — знакомая фраза, и в ней есть логика: пять команд действительно занимают немного времени. Но цена ручного деплоя не сводится к этим пяти командам. У неё есть вторая часть, которая обычно не считается вообще — стоимость инцидентов, которые рано или поздно случаются из-за человеческого фактора. Разберём обе составляющие честно, без пугалок и без придуманных цифр.

Из чего вообще складывается ручной деплой

Прежде чем считать цену, стоит явно перечислить, что входит в «ручной деплой» — иначе легко недооценить объём работы, потому что в голове держится только последний шаг git pull && systemctl restart.

Типичный ручной релиз бэкенд-приложения на VPS выглядит примерно так:

  1. Проверить, что все нужные коммиты смержены в ветку релиза.
  2. Подключиться по SSH к серверу (иногда — к нескольким: staging, потом prod).
  3. Сделать бэкап базы данных или снапшот перед изменениями.
  4. Остановить сервис или перевести его в режим обслуживания.
  5. Забрать код: git pull, git checkout <tag> или скопировать архив.
  6. Установить/обновить зависимости (composer install, npm ci, pip install -r requirements.txt).
  7. Применить миграции базы данных.
  8. Пересобрать статику или фронтенд-бандл.
  9. Перезапустить сервис и проверить, что он поднялся.
  10. Проверить логи на ошибки, открыть сайт руками, прогнать пару сценариев.
  11. Если что-то пошло не так — откатиться вручную.

На небольшом проекте это 10-20 минут при удачном стечении обстоятельств. На проекте с несколькими сервисами, очередями и внешними интеграциями — 40-60 минут и больше, особенно если релиз идёт не каждый день и часть шагов приходится вспоминать заново. Это уже первая, видимая часть цены. Но не главная.

Составляющая первая: прямые часы разработчика

Посчитать прямые часы проще всего, и именно поэтому про них обычно и вспоминают, когда спорят о пользе автоматизации. Логика простая: время, потраченное на ручные операции деплоя, — это время, не потраченное на разработку. Оно не создаёт продукт, оно его перекладывает с одного сервера на другой.

Если взять для примера гипотетическую команду, где релиз происходит 3-4 раза в неделю и занимает по 30-40 минут ручной работы, за месяц набегает несколько часов чистого «перекладывания», которые ложатся на самого опытного человека в команде — потому что деплой доверяют не джуну. Это не измеренное число, а иллюстрация логики: у вас частота релизов, состав шагов и стоимость часа разработчика будут свои, но принцип счёта один и тот же — умножить время одного релиза на число релизов и на ставку человека, который это делает.

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

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Составляющая вторая: скрытая цена ошибок

Вот здесь начинается то, что в спорах про автоматизацию обычно недооценивают. Ручной процесс отличается от автоматизированного не только скоростью — он отличается вероятностью ошибки. Человек, который тридцатый раз выполняет один и тот же чек-лист, рано или поздно:

  • пропустит шаг (забудет применить миграцию или пересобрать статику);
  • опечатается в команде (перепутает окружение, накатит staging-конфиг на prod);
  • выполнит шаги не в том порядке (перезапустит сервис до применения миграции);
  • забудет сделать бэкап «потому что вроде и так всё нормально»;
  • задеплоит не ту ветку или не тот тег, потому что переключался между несколькими терминалами.

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

Ключевая мысль, которую стоит проговорить прямо: ошибка при ручном деплое не эквивалентна потерянным пяти минутам на исправление. Она эквивалентна инциденту, а инцидент — это отдельная, куда более дорогая история.

Как ошибка деплоя превращается в инцидент

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

  1. Приложение падает с ошибкой column does not exist на первом же запросе, использующем новую колонку.
  2. Часть запросов уже могла уйти к пользователям — они видят 500-ю ошибку или белый экран.
  3. Кто-то замечает проблему — по алертам, если мониторинг настроен, или по жалобам пользователей, если нет.
  4. Начинается диагностика: смотрят логи приложения, логи веб-сервера, состояние процесса, пробуют понять, что именно сломалось — это не пять минут, особенно если человек не сразу вспоминает, что сам только что деплоил.
  5. Причина найдена, миграция накатывается вручную на живой базе — что само по себе рискованно, если под нагрузкой.
  6. Сервис перезапускается, проверяется, что всё поднялось.
  7. После инцидента (если в команде принято разбирать такие случаи) кто-то пишет постмортем и придумывает, как избежать повтора.

Даже без единого придуманного числа видно: диагностика и исправление инцидента почти всегда занимают ощутимо больше времени, чем сам шаг, который был пропущен. Плюс к времени добавляется цена, которую сложно измерить в часах напрямую — недоступность сервиса для пользователей в этот момент, и если сервис публичный и платный, это уже репутационные и прямые финансовые потери, не говоря о нервах команды.

Похожий паттерн разбирается в статье про откат после неудачного обновления — там показано, что происходит, когда план восстановления не продуман заранее, и во сколько это обходится по факту.

Почему эта составляющая систематически недооценивается

Если спросить команду напрямую: «сколько стоит ручной деплой», почти всегда прозвучит ответ про прямые часы — «ну, минут по 20-30 на релиз». Про инциденты вспоминают только тогда, когда их прямо спрашивают об этом, и часто отвечают: «да у нас редко такое бывает».

Проблема в том, что «редко» не значит «дёшево». Если инцидент из-за человеческой ошибки случается раз в два-три месяца, а на диагностику и исправление уходит несколько часов работы одного-двух человек плюс простой сервиса — за год набирается сумма, сопоставимая с прямыми часами на сам деплой, а часто и превышающая их. Просто эта сумма размазана по редким, но дорогим событиям, и её сложнее увидеть в ежедневной рутине, чем стабильные 30 минут по средам и пятницам.

Есть и вторая причина недооценки: инциденты от ручного деплоя редко попадают в общую статистику инцидентов как отдельная категория. Их списывают на «баг в коде», «проблему с базой», «странности хостинга» — хотя корневая причина в том, что процесс выката изменений не был воспроизводимым. Разбор причин таких случаев — тема статьи про то, как писать разбор инцидента: если в постмортемах регулярно всплывает «забыли шаг» или «перепутали окружение», это прямой сигнал, что проблема не в конкретном человеке, а в самом процессе.

Что меняет автоматизация деплоя

CI/CD-пайплайн не убирает шаги деплоя — он убирает человека из цикла их выполнения. Тот же список из первого раздела (проверить ветку, забрать код, поставить зависимости, применить миграции, пересобрать статику, перезапустить сервис, проверить здоровье) остаётся, но выполняется скриптом, который делает его одинаково каждый раз — и в понедельник утром, и в пятницу вечером после тяжёлого дня.

Это даёт экономию по обеим составляющим сразу:

По времени. Разработчик тратит на релиз не 30-40 минут ручной работы, а пару минут на git push и наблюдение за пайплайном — причём наблюдать не обязательно постоянно, можно вернуться к задаче и проверить результат позже.

По инцидентам. Пайплайн не забывает шаги и не опечатывается. Он либо выполняет весь сценарий целиком, либо падает на конкретном шаге с понятным логом — а не выкатывает код наполовину примененного изменения. Это не значит, что инциденты исчезают полностью: баг в самом коде CI/CD не отменяет, ошибку в логике миграции — тоже. Но именно тот класс инцидентов, который вызван человеческой невнимательностью при повторяющихся ручных операциях, автоматизация убирает почти целиком.

Минимальный рабочий пример для проекта на VPS с GitLab CI — стадия деплоя по SSH после успешных тестов:

deploy_prod:
  stage: deploy
  image: alpine:3.20
  before_script:
    - apk add --no-cache openssh-client
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - ssh-keyscan -H $PROD_HOST >> ~/.ssh/known_hosts
  script:
    - ssh deploy@$PROD_HOST "
        cd /var/www/app &&
        git fetch --tags &&
        git checkout $CI_COMMIT_TAG &&
        composer install --no-dev --optimize-autoloader &&
        php artisan migrate --force &&
        php artisan config:cache &&
        sudo systemctl restart app.service &&
        curl -f http://127.0.0.1:8080/health || exit 1
      "
  rules:
    - if: '$CI_COMMIT_TAG'

Обратите внимание на последнюю строку скрипта — проверку /health с exit 1 при неудаче. Это ключевой элемент: пайплайн не просто выполняет шаги, он проверяет результат и явно падает, если сервис не поднялся, вместо того чтобы молча считать релиз успешным. Именно эта проверка в ручном процессе чаще всего пропускается «на автомате» — человек видит, что команда отработала без ошибок, и не всегда идёт дергать /health руками.

Если раннера для такого пайплайна пока нет, установка описана в статье про настройку GitLab CI/CD на VPS — там же пример полного .gitlab-ci.yml со стадиями build/test/deploy.

С чего начать, если деплой пока полностью ручной

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

  1. Задокументировать текущий ручной чек-лист как есть. Это уже снижает часть риска — человек, который делает деплой не каждый день, не полагается на память.
  2. Обернуть шаги в один shell-скрипт на сервере. Даже без CI-системы это убирает риск опечатки и пропущенного шага при ручном запуске — ./deploy.sh вместо десятка отдельных команд.
  3. Добавить проверку здоровья сервиса в конец скрипта. Именно она чаще всего отсутствует в ручных процессах и именно её отсутствие превращает пропущенный шаг в незамеченный инцидент.
  4. Подключить CI-раннер и перенести туда запуск скрипта по тегу или пушу в ветку. Это тот момент, когда время релиза перестаёт зависеть от того, кто сегодня на месте и не устал ли он.
  5. Добавить откат как часть пайплайна, а не как отдельную ручную процедуру «на всякий случай». План отката, продуманный заранее, экономит часы именно в тот момент, когда нервничать и вспоминать команды меньше всего хочется — это разбирается в статье про план отката миграции.

Каждый из этих шагов можно сделать отдельно и получить пользу сразу, не дожидаясь полного enterprise-пайплайна с несколькими окружениями и approval-гейтами. Для старта хватит одного VPS с раннером и простого shell-скрипта с проверкой на выходе.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

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

Стоит ли автоматизировать деплой, если релизы происходят раз в месяц или реже?

Экономия по прямым часам будет небольшой — раз в месяц 30-40 минут не критичны. Но именно редкие релизы особенно уязвимы к человеческой ошибке: за месяц забывается, как именно проходил прошлый деплой, и чек-лист выполняется по памяти. В этом случае вторая составляющая цены — риск инцидента — часто важнее первой.

Можно ли получить пользу CI/CD без полноценного пайплайна с тестами?

Да. Даже автоматизация только самого деплоя (без автотестов) убирает основной источник ошибок — ручное выполнение повторяющихся шагов. Автотесты добавляют защиту на уровне логики кода, но это отдельный слой, не обязательный для первого шага.

Что делать, если часть шагов деплоя всё равно требует ручного вмешательства (например, редкие миграции с ручной проверкой данных)?

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

Нужен ли для CI/CD отдельный сервер, или можно поставить раннер на тот же VPS, где крутится приложение?

Для небольших проектов раннер на том же сервере — рабочий вариант, особенно на старте. По мере роста нагрузки стоит вынести раннер на отдельный VPS, чтобы сборка и тесты не конкурировали за ресурсы с продакшен-сервисом в момент пиковой нагрузки.

Как оценить, окупится ли переход на CI/CD именно у нас?

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

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →