Ревизия cron-задач: сколько из них ещё нужны, а сколько уже вредят
Сервер, который живёт больше пары лет, обрастает cron-задачами так же, как квартира — вещами: каждая появилась по конкретному поводу, но повод давно забыт, а задача осталась и выполняется каждую ночь. Одна дёргает API проекта, закрытого ещё в прошлом году. Вторая дублирует то, что уже делает другой, более новый скрипт. Третья падает с ошибкой третий месяц подряд, просто никто не читает её вывод. По отдельности каждая незаметна — секунда CPU, лишняя запись в лог. Вместе они превращаются в фоновый шум, который мешает диагностике и хранит риск: часть таких задач держит доступ к боевым данным или ключам, которые давно пора отозвать. Разберём, как провести ревизию cron-задач так, чтобы не сломать ничего рабочего и при этом реально вычистить мусор.
Содержание
Почему cron-задачи умирают тихо, а не громко
У cron нет механизма, который сообщил бы: «эта задача больше никому не нужна». Он честно выполняет расписание, пока строка физически существует в crontab — независимо от того, жив ли проект, который эту задачу когда-то породил, и нужен ли кому-то результат её работы. Задачи выходят из актуальности тремя разными путями, и для каждого своя логика диагностики.
Проект закрылся, а задача осталась. Скрипт синхронизации с внешним API, парсер данных для клиента, который давно ушёл, обработчик очереди для фичи, выпиленной полгода назад из-за низкого спроса. Задача исправно запускается и либо тихо падает на несуществующем эндпоинте, либо — хуже — успешно отрабатывает вхолостую, потребляя ресурсы ради результата, который никто не читает.
Задача задублировалась. Два администратора независимо решали одну и ту же проблему в разное время: один написал скрипт ротации логов, через год другой — не зная о первом — написал второй, чуть иначе. Оба до сих пор в crontab и выполняют одну работу, иногда конфликтуя за одни файлы. Частый случай дублирования разобран в статье про наложение cron-задач при их большом количестве — там же видно, как дубли усиливают пиковую нагрузку в круглые минуты вроде полуночи.
Задача устарела архитектурно, но продолжает работать. Классический пример — самописный скрипт очистки временных файлов, который должен был уступить место штатному logrotate или systemd-tmpfiles, но остался как временное решение, ставшее постоянным. Он не сломан в строгом смысле, но обходит нормальные механизмы системы и делает лишнюю работу, которую система и так умеет делать правильно.
Общая черта всех трёх случаев — задача не подаёт признаков поломки. Она просто выполняется, занимает время в расписании, иногда пишет что-то в лог, который никто не читает, — и этим отличается от сбойной задачи, о которой быстро узнают по алерту. Мусорная cron-задача не кричит о себе, поэтому единственный способ её найти — целенаправленная ревизия.
Полная инвентаризация: где искать задачи, кроме `crontab -l`
Первая и самая частая ошибка ревизии — посмотреть только crontab -l под своим пользователем и решить, что это полный список. На сервере с несколькими сервисными аккаунтами расписание разбросано минимум по пяти разным местам.
Пользовательские crontab — по каждому пользователю отдельно, не только под тем, из-под которого вы зашли:
for u in $(cut -f1 -d: /etc/passwd); do
echo "== $u =="
crontab -l -u "$u" 2>/dev/null
done
Системные файлы, которые crontab -l вообще не показывает, потому что это отдельный механизм:
cat /etc/crontab
ls -la /etc/cron.d/
cat /etc/cron.d/*
Периодические каталоги — задачи, которые выполняются через run-parts, а не через явное расписание в файле:
ls -la /etc/cron.hourly/ /etc/cron.daily/ /etc/cron.weekly/ /etc/cron.monthly/
Сырые файлы crontab на диске — полезно, когда задача числится за пользователем, которого уже удалили из /etc/passwd, но файл остался:
ls -la /var/spool/cron/crontabs/ # Debian/Ubuntu
ls -la /var/spool/cron/ # RHEL/AlmaLinux/CentOS
И слой, о котором забывают чаще всего — systemd timers, которые на современных дистрибутивах всё чаще заменяют cron для системных задач и никак не пересекаются с перечисленными выше файлами:
systemctl list-timers --all
systemctl cat имя.timer
Где ещё может прятаться запланированная задача — /etc/anacrontab, задания at, хуки в /etc/cron.d от установленных пакетов и скрипты, которые сами себя переставляют через crontab из кода — разобрано отдельно в статье про то, где ещё искать чужую задачу кроме crontab.
Результат инвентаризации стоит сразу свести в один файл — таблицу или текстовый список: владелец, расписание, что запускает, откуда взялась строка. Без него вторая часть ревизии превращается в хаос, где легко упустить половину найденного.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверКак проверить историю и следы использования задачи
Прежде чем выносить вердикт «не нужна», стоит подтвердить его данными, а не догадкой — три источника дают почти всегда достаточно фактов.
Логи самой задачи — если она вообще что-то пишет. Проверьте не только факт наличия строк, но и их содержание за приличный период, а не за последний день:
journalctl -u имя.service --since "-30 days" | tail -100
grep "sync_old_project" /var/log/syslog | tail -50
Если скрипт годами падает с ошибкой и об этом никто ни разу не написал в тикет или чат — это почти всегда означает, что результат никому не был нужен настолько, чтобы заметить его отсутствие. Задача, которая молча ломается и молча остаётся сломанной, — верный кандидат на удаление, а не на починку: если бы она была нужна, поломку заметили бы раньше.
Дата последнего изменения самого файла скрипта и файла расписания — грубый, но полезный индикатор давности:
stat /opt/scripts/sync_old_project.sh
stat /etc/cron.d/legacy-tasks
Если файл не трогали три года, а система за это время сменила пару поколений инфраструктуры — вероятность, что задача пережила свой смысл, заметно выше, чем у скрипта, который правили месяц назад.
Если /etc под git через etckeeper, история commit-сообщений часто прямо объясняет, зачем задача появилась и не было ли решения выключить её раньше, которое просто забыли довести до конца:
cd /etc && git log --follow -p cron.d/legacy-tasks | less
Наконец, самая надёжная проверка — прямой вопрос команде. Ревизия cron — не только техническая, но и организационная задача: скрипт может выглядеть мёртвым технически (файл существует, ошибок нет), но обслуживать процесс из другого отдела, о котором вы просто не знаете. Прежде чем отключать что-то сомнительное, но не мёртвое явно (пункт 1 из предыдущего раздела), стоит написать в общий чат: «отключаю такую-то задачу такого-то числа, если она вам для чего-то нужна — скажите за два дня». Это не техническая мера, но именно она чаще всего спасает от поломки чужого процесса.
Безопасное отключение: гасить, а не удалять
Главное правило ревизии — ни одна сомнительная задача не удаляется в тот же день, когда её нашли. Разница между «отключить» и «удалить» — это разница между обратимым и необратимым действием, а на этапе ревизии вы ещё не знаете наверняка, что задача точно не нужна — вы лишь предполагаете это по косвенным признакам.
Для обычной crontab-строки безопасное отключение — закомментировать её, а не стереть, и обязательно оставить комментарий с датой и причиной, чтобы через месяц не пришлось вспоминать, почему строка вообще тронута:
# DISABLED 2026-08-25 (tm): проект closed-project.example.com не работает с марта,
# ждём 30 дней наблюдения перед окончательным удалением
# 0 3 * * * /opt/scripts/sync_closed_project.sh
Для файлов в /etc/cron.d/ — тот же приём с построчным комментированием, файл не удаляется целиком, если в нём есть хотя бы одна ещё нужная строка.
Для systemd timers отключение — это disable и stop, но не mask и не удаление unit-файла: маскирование и удаление куда сложнее откатить, если выяснится, что задача всё-таки была нужна.
systemctl stop имя.timer
systemctl disable имя.timer
# юнит-файл остаётся на диске — включить обратно можно одной командой:
# systemctl enable --now имя.timer
Перед отключением любой задачи, которая пишет результат в файл или таблицу, имеет смысл сделать её резервную копию отдельно от основной системы бэкапов — просто скопировать текущий результат работы в архивный каталог с датой, чтобы при необходимости откатиться было откуда:
mkdir -p /root/cron-archive/2026-08-25
cp /opt/scripts/sync_closed_project.sh /root/cron-archive/2026-08-25/
Дальше — период наблюдения, и его длительность должна соответствовать частоте задачи, а не быть одинаковой для всех. Для часовой задачи недели наблюдения достаточно с запасом. Для месячного отчёта нужно выдержать минимум один полный цикл — иначе последствия отключения просто не успеют проявиться. Разумный минимум — 2-4 недели для частых задач и полный цикл расписания плюс запас для редких.
Если не хочется полагаться только на память «никто не пожаловался», на время наблюдения можно поставить простую внешнюю проверку, которая честно покажет, если что-то, зависящее от отключённой задачи, перестало обновляться — подход через пинг наружу после выполнения задачи разобран в статье про Healthchecks.io и мониторинг cron-задач; здесь он полезен наоборот — для контроля тишины после отключённой задачи: если смежный процесс молчит все 2-4 недели, это подтверждает, что задача была лишней.
Как встроить ревизию в регламент, а не делать разово
Разовая большая чистка снимает накопленный за годы мусор, но без регулярной ревизии проблема появится снова — просто на новом наборе задач, за следующие два-три года. Практичнее встроить лёгкую версию ревизии в существующий регламент обслуживания, а не превращать её в отдельное большое мероприятие.
Реестр задач — простой файл (markdown или таблица), который живёт рядом с конфигурацией сервера и обновляется при каждом изменении расписания, а не восстанавливается по памяти раз в год:
| Задача | Владелец | Расписание | Назначение | Последняя проверка |
|---|---|---|---|---|
| sync_project_a.sh | tm | */15 * * * * | синхронизация с проектом A | 2026-08-25 |
| backup_pg.sh | root, /etc/cron.d | 0 2 * * * | ночной дамп PostgreSQL | 2026-08-25 |
| cleanup_tmp.sh | www-data | 0 4 * * 0 | устарело, заменить на tmpfiles.d | помечено на удаление |
Даже такая простая таблица снимает главную проблему — необходимость каждый раз заново вспоминать, что вообще есть на сервере, начиная инвентаризацию с нуля.
Практический ритм без лишних временных затрат: в еженедельный или ежемесячный регламент обслуживания сервера полезно добавить один пункт — просмотреть реестр и отметить задачи, ждущие финального решения после наблюдения. Полная инвентаризация по всем пяти источникам не нужна каждую неделю — её достаточно повторять раз в 6-12 месяцев, а между разами реестр поддерживается сам собой, если каждую новую задачу сразу заносить в таблицу в момент создания, а не постфактум.
Отдельное правило — не заводить новую cron-задачу без записи в реестре и без владельца, привязанного к конкретному человеку, а не к абстрактному «команде разработки». Именно безымянные задачи оказываются самыми трудными для ревизии через два года — спросить не с кого, контекст приходится восстанавливать по содержимому скрипта.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Сколько времени занимает полная ревизия cron-задач на сервере среднего размера?
Зависит от числа накопленных задач и качества документации, но для сервера, который не чистили несколько лет, первая ревизия обычно занимает несколько часов, растянутых на 2-3 подхода, — часть времени уходит на ожидание ответов от коллег по неочевидным задачам.
Что делать, если задача явно мёртвая (скрипта нет физически), но владельца уже не найти?
Мёртвый файл — самый безопасный случай: раз скрипта нет, отключение расписания ничего не меняет, оно и так не выполняет ничего полезного. Отключить можно сразу, без долгого наблюдения, оставив короткий комментарий с датой.
Стоит ли удалять задачу сразу, если она явно вредит — например, забивает диск логами?
Активный вред — повод сократить период наблюдения, но не пропустить его целиком. Быстрее ограничить последствия (перенаправить вывод в /dev/null, урезать частоту), а полноценно отключить и понаблюдать несколько дней, а не месяц.
Как быть с задачами, унаследованными от предыдущего администратора без документации?
Относитесь к ним как к чёрным ящикам, пока три вопроса из раздела выше не дадут ясности. Не отключайте по подозрению «выглядит странно» — сначала проверьте физическое существование объекта работы и историю логов, и только потом переходите к отключению с наблюдением.
Нужно ли согласовывать отключение каждой сомнительной задачи с командой?
Для задач с однозначно мёртвым объектом работы — нет. Для остального — да, короткое уведомление в общем чате за пару дней стоит меньше, чем разбор инцидента от отключения чужого процесса, о котором вы не знали.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →