Cron: как не спать ночами и не ломать продакшен
Введение: почему cron — это не просто "забыл запустить скрипт"
Cron — это не просто утилита для запуска задач по расписанию. Это часть инфраструктуры, которая может либо спасти проект от ручного администрирования, либо превратить его в кошмар поддержки. В реальных системах cron часто становится узким местом: задачи тормозят сервера, логи забивают диск, а команды начинают ругаться на "непредсказуемый cron".
В этой статье мы разберём, как cron работает в продакшене, какие ошибки чаще всего встречаются и как настроить его так, чтобы он не мешал, а помогал.
Проблема: когда cron становится врагом
Cron полезен, когда нужно автоматизировать повторяющиеся задачи: бэкапы, очистку логов, синхронизацию данных, отправку уведомлений. Но в реальных системах он часто превращается в источник проблем:
- Задачи запускаются слишком часто — например, каждые 5 минут, но скрипт выполняется 2 часа. Результат: сервер загружен на 100%, а команда не понимает, почему.
- Логи и временные файлы не чистятся — диск заполняется, система глючит, а логирование никто не отслеживает.
- Зависимости между задачами игнорируются — одна задача ждёт данные от другой, но cron их запускает вразнобой.
- Ошибки молча глотаются — скрипт падает с ошибкой, но cron продолжает пытаться его запустить, не уведомляя никого.
- Расписание не синхронизировано между серверами — на одном сервере задача запускается в 3:00, на другом — в 3:01, а между ними зависимость.
Эти проблемы решаются не "правильным cron", а правильной архитектурой вокруг него.
Практика: как cron должен работать в продакшене
1. Расписание: не каждые 5 минут, а когда нужно
Cron позволяет задавать расписание в формате * * * * *, но это не значит, что нужно использовать его по умолчанию. Например:
- Частые задачи (менее 1 часа): Используйте
*/5 * * * *(каждые 5 минут) только если действительно нужно. Чаще всего достаточно0 * * * *(каждый час). - Редкие задачи (раз в день/неделю):
0 3 * * *(в 3:00 каждый день) или0 0 * * 0(в 00:00 каждую субботу). - Задачи с зависимостями: Если задача B зависит от задачи A, запускайте их с небольшим интервалом, например:
# Задача A в 2:00 0 2 * * * /путь/к/скрипту_A.sh # Задача B в 2:10 (если A успела завершиться) 10 2 * * * /путь/к/скрипту_B.sh
2. Логирование: чтобы знать, что пошло не так
Любой cron-задача должна логировать:
- Время запуска.
- Результат выполнения (успех/неудача).
- Время выполнения (если задача длительная).
- Вывод скрипта (если что-то пошло не так).
Пример скрипта с логированием:
#!/bin/bash
LOG_FILE="/var/log/myscript.log"
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
echo "[$TIMESTAMP] Запуск скрипта" >> "$LOG_FILE"
/путь/к/вашему/скрипту.sh >> "$LOG_FILE" 2>&1
echo "[$TIMESTAMP] Скрипт завершён с кодом $?" >> "$LOG_FILE"
3. Обработка ошибок: не молчать, а уведомлять
Если скрипт падает, cron должен это заметить и сообщить команде. Для этого используйте mail или сервисы уведомлений (Slack, Telegram, PagerDuty).
Пример с уведомлением по почте:
#!/bin/bash
LOG_FILE="/var/log/myscript.log"
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
RESULT=$(/путь/к/скрипту.sh)
if [ $? -ne 0 ]; then
echo "[$TIMESTAMP] ОШИБКА: Скрипт завершён с кодом $?" | mail -s "Ошибка в cron-задаче" admin@example.com
fi
Для Slack можно использовать curl:
curl -X POST -H 'Content-type: application/json' --data '{"text":"Ошибка в cron-задаче: '$?'"}' 'https://hooks.slack.com/services/...'
4. Изоляция задач: не грузить один сервер всем
Если у вас много cron-задач, не сваливайте их все на один сервер. Распределите нагрузку:
- Легкие задачи (например, очистка логов) — на веб-серверы.
- Тяжёлые задачи (например, бэкапы) — на отдельный сервер или контейнер.
- Критичные задачи (например, синхронизация базы) — с приоритетом и мониторингом.
Примеры: как настроить cron правильно
Пример 1: Бэкап базы данных с проверкой и уведомлением
# /etc/cron.d/daily_backup
# Запускаем в 2:00 каждый день
0 2 * * * root /usr/bin/mysqldump -u user -p'password' db_name | gzip > /backups/db_$(date +\%Y-\%m-\%d).sql.gz
# Проверяем, что бэкап прошёл успешно
0 2 * * * root [ -f /backups/db_$(date -d "yesterday" +\%Y-\%m-\%d).sql.gz ] || /usr/bin/send_alert.sh "Бэкап не создан"
Пример 2: Очистка временных файлов с логированием
# /etc/cron.d/clean_temps
# Каждый час в 15 минуту
15 * * * * root find /tmp -type f -mtime +7 -delete >> /var/log/clean_temps.log 2>&1
Пример 3: Синхронизация данных между сервисами
# Задача A: экспорт данных
0 0 * * * root /usr/bin/export_data.sh >> /var/log/export_data.log 2>&1
# Задача B: импорт данных (запускается через 10 минут после экспорта)
10 0 * * * root /usr/bin/import_data.sh >> /var/log/import_data.log 2>&1
Типичные ошибки и как их избежать
Запуск задач без проверки результата
- Проблема: Скрипт падает, но cron продолжает его запускать.
- Решение: Всегда проверяйте код возврата (
$?) и уведомляйте об ошибках.
Игнорирование времени выполнения
- Проблема: Задача должна выполняться 5 минут, но cron запускает её каждые 5 минут.
- Решение: Используйте
flockдля блокировки или проверяйте статус предыдущего запуска.
Неправильное расписание
- Проблема: Задача запускается в рабочие часы и тормозит систему.
- Решение: Запускайте задачи в ночное время или с низким приоритетом (
nice).
Отсутствие логирования
- Проблема: Нет возможности отследить, когда и почему задача упала.
- Решение: Логируйте всё: время, результат, вывод скрипта.
Зависимости между задачами не учтены
- Проблема: Задача B зависит от задачи A, но cron запускает их одновременно.
- Решение: Используйте задержки или явные проверки статуса.
Использование
crontab -eна продакшене без контроля версий- Проблема: Кто-то меняет cron-задачи вручную, и никто не знает, что изменилось.
- Решение: Храните конфиги cron в репозитории и используйте инструменты типа
cronieилиsystemd timersдля управления.
Вывод: как cron должен работать в вашей инфраструктуре
- Не используйте cron для всего подряд — если задача требует сложной логики или зависимостей, лучше использовать систему задач (Airflow, Dagster, Kubernetes CronJobs).
- Логируйте всё — без логов cron становится чёрным ящиком, и вы не сможете диагностировать проблемы.
- Уведомляйте об ошибках — если задача падает, команда должна знать об этом сразу.
- Распределяйте нагрузку — не сваливайте все задачи на один сервер, особенно если они долгие или ресурсоёмкие.
- Синхронизируйте расписание — если у вас несколько серверов, убедитесь, что задачи запускаются в одном порядке.
- Храните конфиги в репозитории — это позволит откатывать изменения и отслеживать, кто что изменил.
Cron — это не просто утилита, а часть вашей инфраструктуры. Если вы настроите его правильно, он сэкономит вам время и нервы. Если нет — он станет источником проблем. Выбирайте осознанно.