В 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