В 3:12 ночи скрипт, который выгружает заказы из 1С:УТ 11 в отчёт для собственника, упал на неожиданном формате даты. Никто этого не заметил. В 9:00 собственник открыл отчёт со вчерашними цифрами, а в 11:00 главбух спросила, почему не видно утренних оплат. Ещё два часа ушло на то, чтобы найти, что сломалось.

У любой автоматизации учёта есть невидимая часть: маленькие скрипты на сервере, которые по расписанию собирают отчёты, проверяют новые заявки в amoCRM, отправляют сводку в Telegram. Пока они работают, о них не вспоминают. Когда падают, узнают об этом от людей. Ниже схема, которую мы используем для таких задач на обычном VPS: без Docker и внешнего мониторинга, только на том, что уже есть в Linux.

Почему запуск «в фоне» не работает

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

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

Раскладка файлов

  • /opt/myapp/ для кода и виртуального окружения Python;
  • /opt/myapp/data/ как единственная папка, куда скрипт имеет право писать;
  • /etc/myapp.env для секретов: токен бота, пароль к базе, ключ API amoCRM. Права 600, владелец root.

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

Сервис с автоперезапуском

Файл /etc/systemd/system/myapp.service описывает, как запускать задачу. Ключевые параметры:

  • Restart=on-failure и RestartSec=5: если скрипт завершился с ошибкой, systemd поднимет его через 5 секунд;
  • StartLimitIntervalSec=300 и StartLimitBurst=5: если скрипт падает 5 раз за 5 минут, systemd перестаёт его поднимать и переводит в состояние failed. Без этого скрипт с ошибкой в конфиге будет бесконечно перезапускаться и забивать журнал;
  • EnvironmentFile=/etc/myapp.env: секреты подтягиваются из файла, в коде и в репозитории их нет;
  • Environment=PYTHONUNBUFFERED=1: вывод попадает в журнал сразу, без задержки до завершения процесса;
  • ProtectSystem=strict, ReadWritePaths=/opt/myapp/data, NoNewPrivileges=true: файловая система для процесса доступна только на чтение, кроме папки с данными.

Запуск: systemctl daemon-reload, затем systemctl enable --now myapp, проверка через systemctl status myapp. Чтобы убедиться, что перезапуск работает, принудительно убейте процесс командой systemctl kill -s KILL myapp. Через 5 секунд статус снова будет active.

Задачи по расписанию: таймеры вместо cron

Если скрипт должен запускаться раз в час или раз в сутки, удобнее systemd-таймер. Рядом с сервисом кладётся файл myapp.timer с параметром OnCalendar=hourly или OnCalendar=*-*-* 06:00. Параметр Persistent=true запускает пропущенную задачу, если сервер был выключен в момент планового запуска.

Преимущество таймера перед cron в том, что запуски, ошибки и логи видны в одном месте, через те же команды systemctl и journalctl. Список ближайших запусков показывает systemctl list-timers.

Логи: просто пишите в stdout

Отдельные лог-файлы с ротацией не нужны. Всё, что скрипт пишет в stdout и stderr, попадает в journald вместе с трейсбэками и временем. Пригодятся две команды:

  • journalctl -u myapp -f показывает логи в реальном времени;
  • journalctl -u myapp --since "1 hour ago" показывает логи за последний час.

Чтобы журнал не съел диск на маленьком VPS, ограничьте его размер параметром SystemMaxUse в /etc/systemd/journald.conf, например 500 МБ.

Алерты в Telegram

Самая важная часть. О падении скрипта вы должны узнать от бота, и раньше, чем бухгалтер. В systemd для этого есть директива OnFailure: при переходе основного сервиса в failed она запускает другой сервис.

Делаем шаблон tg-notify@.service, который принимает имя упавшего юнита, берёт последние 20 строк его журнала и отправляет их через curl в Telegram Bot API в рабочий чат. В каждом сервисе достаточно прописать OnFailure=tg-notify@%n.service. Один шаблон обслуживает все скрипты на сервере.

Когда скрипт жив, но не работает

Бывает хуже, чем падение: процесс работает, но ничего не делает. Например, amoCRM сменила формат ответа, скрипт ловит исключение, пишет его в лог и продолжает крутиться. Статус active, алерта нет, данные не обновляются.

От этого спасает проверка результата. Отдельный таймер раз в 30 минут смотрит, когда последний раз обновлялись данные: время последней записи в таблице, дата файла выгрузки. Если отчёт для собственника не обновлялся больше 2 часов в рабочее время, приходит сообщение в тот же чат.

Что это даёт бизнесу

Речь не о DevOps ради DevOps. Если на скрипт уже рассчитывают главбух, РОП или собственник, он должен:

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

Настройка по этой схеме занимает 1–2 часа на скрипт. Если у вас уже есть такие задачи на nohup или в screen, начните с самой важной: обычно это выгрузка из 1С или синхронизация заявок с CRM. Если скриптов пока нет, но выгрузки и сверки делаются руками каждый день, это первые кандидаты на автоматизацию, и запускать их стоит сразу по этой схеме.

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

Если хотите примерить это на свой процесс, можно написать в Telegram @automationdev24: опишите, как всё устроено сейчас, и мы скажем, хватит ли доработки текущей системы или нужна своя.

Узнали свою ситуацию?

Опишите в Telegram, как у вас сейчас идут заявки и учёт. Скажем, хватит ли доработки вашей CRM или лучше собрать свою систему. Первая консультация бесплатная.

Написать в Telegram