За 10+ лет работы с сайтами я понял простую истину: задачи, которые можно автоматизировать, должны быть автоматизированы. И cron jobs — это основа всей автоматизации на Linux-серверах.
Честно говоря, когда я только начинал работать веб-разработчиком, то делал многие вещи вручную. Обновлял кеш, чистил логи, отправлял отчёты. А потом понял, что трачу на это часы каждую неделю. Cron решил все эти проблемы раз и навсегда.
Что такое cron и зачем он нужен
Cron — это демон (фоновый процесс) в Unix-подобных системах, который выполняет задачи по расписанию. Название происходит от греческого «chronos» — время. На деле это ваш личный помощник, который работает 24/7 и никогда не забывает выполнить задачу.
Я использую cron практически на всех проектах. Вот типичные задачи, которые автоматизирую:
- Создание резервных копий базы данных каждую ночь
- Очистка временных файлов и старых логов
- Обновление курсов валют для интернет-магазинов
- Отправка email-рассылок по расписанию
- Проверка работоспособности сайта каждые 5 минут
- Генерация отчётов для клиентов
- Обновление индексов поиска
У одного клиента был интернет-магазин с 50 000 товаров. Каждый день нужно было обновлять цены из 1С, проверять остатки, пересчитывать скидки. Раньше менеджер делал это вручную каждое утро — полтора часа работы. После настройки cron всё происходит автоматически в 6 утра, до открытия офиса.
Синтаксис crontab и форматы записей
Расписание cron записывается в специальном формате, который многих пугает на первый взгляд. Но на самом деле всё просто. Формат состоит из пяти полей:
* * * * * команда
│ │ │ │ │
│ │ │ │ └─── день недели (0-7, где 0 и 7 = воскресенье)
│ │ │ └───── месяц (1-12)
│ │ └─────── день месяца (1-31)
│ └───────── час (0-23)
└─────────── минута (0-59)
Грубо говоря, читать нужно слева направо: минута, час, день месяца, месяц, день недели. Вот несколько примеров, которые я часто использую:
# Каждую минуту
* * * * * /path/to/script.sh
# Каждый день в 3:30 утра
30 3 * * * /usr/bin/php /var/www/backup.php
# Каждый понедельник в 9:00
0 9 * * 1 /var/www/html/weekly_report.php
# Каждое 1 число месяца в полночь
0 0 1 * * /usr/bin/mysql-dump backup.sql
# Каждые 15 минут в рабочие дни
*/15 * * * 1-5 /var/www/check_queue.php
# Дважды в день: в 8:00 и 20:00
0 8,20 * * * /usr/bin/php /var/www/sync_prices.php
Специальные символы делают cron очень гибким:
*— любое значение,— список значений (1,3,5)-— диапазон (1-5)/— шаг (*/15 означает каждые 15 минут)
Есть также предопределённые макросы, которые я использую для читаемости:
@reboot— при перезагрузке системы@yearlyили@annually— раз в год (0 0 1 1 *)@monthly— раз в месяц (0 0 1 * *)@weekly— раз в неделю (0 0 * * 0)@dailyили@midnight— каждый день (0 0 * * *)@hourly— каждый час (0 * * * *)
Настройка cron jobs на сервере
Управление cron осуществляется через команду crontab. Я всегда начинаю с просмотра текущих задач:
# Показать текущие cron jobs
crontab -l
# Редактировать crontab текущего пользователя
crontab -e
# Удалить все cron jobs текущего пользователя
crontab -r
# Работа с cron другого пользователя (от root)
crontab -u username -l
crontab -u username -e
При первом запуске crontab -e система предложит выбрать редактор. Я рекомендую nano для новичков или vim для опытных пользователей.
Вот как я обычно настраиваю cron для типичного веб-проекта. Создаю файл /var/www/html/cron_tasks.txt с задачами:
# Резервное копирование БД каждую ночь в 2:00
0 2 * * * /usr/bin/mysqldump -u backup_user -pPASSWORD database_name > /backups/db_$(date +\%Y\%m\%d).sql
# Очистка старых логов каждое воскресенье
0 3 * * 0 find /var/log/apache2/ -name "*.log" -mtime +30 -delete
# Обновление курсов валют каждый час в рабочее время
0 9-18 * * 1-5 /usr/bin/php /var/www/html/update_rates.php
# Проверка очереди задач каждые 5 минут
*/5 * * * * /usr/bin/php /var/www/html/process_queue.php
# Генерация sitemap.xml каждую ночь
0 4 * * * /usr/bin/php /var/www/html/generate_sitemap.php
А затем загружаю их в crontab:
crontab /var/www/html/cron_tasks.txt
На практике я предпочитаю создавать отдельный скрипт-обёртку для каждой задачи. Это упрощает отладку и логирование. Вот пример такого скрипта:
#!/bin/bash
# backup_wrapper.sh
LOG_FILE="/var/log/cron_backup.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$DATE] Starting backup process" >> $LOG_FILE
# Выполняем резервное копирование
if /usr/bin/mysqldump -u backup_user -pPASSWORD mysite_db > /backups/db_$(date +%Y%m%d).sql; then
echo "[$DATE] Backup completed successfully" >> $LOG_FILE
# Удаляем бэкапы старше 7 дней
find /backups/ -name "db_*.sql" -mtime +7 -delete
else
echo "[$DATE] Backup failed!" >> $LOG_FILE
# Отправляем уведомление об ошибке
echo "Backup failed on $(hostname)" | mail -s "Backup Error" admin@example.com
fi
И в crontab записываю просто:
0 2 * * * /var/www/scripts/backup_wrapper.sh
Cron для WordPress сайтов
WordPress имеет собственную систему cron — WP-Cron, но она работает только при посещении сайта. Для стабильной работы я всегда отключаю WP-Cron и настраиваю системный cron.
Сначала добавляю в wp-config.php:
// Отключаем встроенный WP-Cron
define('DISABLE_WP_CRON', true);
Затем настраиваю системный cron для запуска wp-cron.php:
# Запуск WordPress cron каждые 15 минут
*/15 * * * * /usr/bin/php /var/www/html/wp-cron.php
# Или через curl (если нужны cookie и сессии)
*/15 * * * * /usr/bin/curl -s https://yoursite.com/wp-cron.php >/dev/null 2>&1
Для WordPress я также часто настраиваю дополнительные задачи:
# Оптимизация базы данных раз в неделю
0 3 * * 0 /usr/bin/wp db optimize --path=/var/www/html
# Обновление плагинов (осторожно, только на dev-окружении!)
0 4 * * 1 /usr/bin/wp plugin update --all --path=/var/www/html
# Генерация превью изображений для новых размеров
0 5 * * * /usr/bin/wp media regenerate --yes --path=/var/www/html
# Очистка кеша объектов
*/30 * * * * /usr/bin/wp cache flush --path=/var/www/html
У меня был клиент с новостным сайтом на WordPress, который публиковал 50+ статей в день. WP-Cron не справлялся с отправкой уведомлений подписчикам — письма отправлялись с задержкой в несколько часов. После перехода на системный cron всё заработало как часы.
Cron для Битрикс проектов
В Битрикс есть свой механизм агентов, но для тяжёлых задач я всё равно использую системный cron. Особенно это важно для интернет-магазинов с большим каталогом.
Стандартная настройка для Битрикс:
# Запуск агентов Битрикс каждые 5 минут
*/5 * * * * /usr/bin/php -f /var/www/html/bitrix/modules/main/tools/cron_events.php
# Или через curl
*/5 * * * * /usr/bin/curl -s "https://yoursite.com/bitrix/tools/cron_events.php" >/dev/null 2>&1
Но на практике я создаю более продвинутую схему. Вот пример скрипта для обработки агентов с логированием:
И в crontab:
*/5 * * * * /usr/bin/php /var/www/html/cron/bitrix_agents.php
Для интернет-магазинов на Битрикс я также настраиваю:
# Обновление остатков из 1С каждые 30 минут в рабочее время
*/30 9-18 * * 1-5 /usr/bin/php /var/www/html/local/cron/update_catalog.php
# Пересчёт цен со скидками каждую ночь
0 3 * * * /usr/bin/php /var/www/html/local/cron/recalculate_prices.php
# Отправка уведомлений о заканчивающихся товарах
0 10 * * * /usr/bin/php /var/www/html/local/cron/low_stock_notify.php
# Очистка корзин старше 30 дней
0 4 * * * /usr/bin/php /var/www/html/local/cron/clear_old_baskets.php
На одном проекте интернет-магазина автозапчастей каталог обновлялся из 1С каждые 15 минут. 200 000 товаров, изменение цен и остатков. Без правильно настроенного cron сайт просто «умирал» от нагрузки во время обновления.
Cron для Laravel приложений
Laravel имеет отличную систему планировщика задач (Task Scheduler). Вместо создания множества cron записей, я создаю всего одну, которая запускает планировщик Laravel:
# Единственная запись cron для Laravel
* * * * * cd /var/www/html && php artisan schedule:run >> /dev/null 2>&1
А всё расписание задач описываю в файле app/Console/Kernel.php:
command('backup:run')
->dailyAt('02:00')
->emailOutputOnFailure('admin@example.com');
// Обработка очереди писем каждые 5 минут
$schedule->command('queue:work --stop-when-empty')
->everyFiveMinutes()
->withoutOverlapping();
// Генерация отчётов по понедельникам
$schedule->command('reports:generate')
->weeklyOn(1, '09:00')
->onOneServer(); // Если несколько серверов
// Очистка старых логов раз в месяц
$schedule->command('logs:clear')
->monthly()
->when(function () {
// Дополнительная проверка условий
return Storage::disk('local')->size('logs') > 1000000; // 1MB
});
// Пинг внешнего API каждые 30 минут в рабочие часы
$schedule->call(function () {
Http::get('https://api.partner.com/ping');
})->everyThirtyMinutes()->between('9:00', '18:00');
}
}
Преимущества Laravel Scheduler:
- Всё расписание в одном месте
- Встроенная защита от перекрытия задач (
withoutOverlapping()) - Автоматическое логирование и уведомления об ошибках
- Условное выполнение задач (
when(),skip()) - Выполнение только на одном сервере в кластере (
onOneServer())
Я также создаю специальные Artisan команды для сложных задач:
# Генерация команды
php artisan make:command ProcessOrders
option('limit');
$orders = Order::where('status', 'pending')->limit($limit)->get();
$this->info("Processing {$orders->count()} orders...");
foreach ($orders as $order) {
try {
$order->process();
$this->line("Order #{$order->id} processed");
} catch (\Exception $e) {
$this->error("Failed to process order #{$order->id}: " . $e->getMessage());
}
}
$this->info('Done!');
}
}
Мониторинг и логирование cron задач
Главная проблема cron — он работает в фоне, и если что-то сломается, вы можете об этом не узнать. Поэтому я всегда настраиваю подробное логирование.
Системные логи cron находятся в /var/log/cron или /var/log/syslog. Но для веб-задач я создаю собственные логи:
# Перенаправление вывода в лог-файл
*/15 * * * * /usr/bin/php /var/www/html/sync.php >> /var/log/cron_sync.log 2>&1
# Логирование с временными метками
*/15 * * * * echo "$(date): Starting sync" >> /var/log/cron_sync.log && /usr/bin/php /var/www/html/sync.php >> /var/log/cron_sync.log 2>&1
# Ротация логов (не даём им разрастись)
0 0 * * * find /var/log/ -name "cron_*.log" -size +10M -exec truncate -s 0 {} \;
Я создал универсальный скрипт-обёртку для логирования cron задач:
#!/bin/bash
# /usr/local/bin/cron_wrapper.sh
SCRIPT_PATH=$1
LOG_DIR="/var/log/cron"
SCRIPT_NAME=$(basename "$SCRIPT_PATH" .php)
LOG_FILE="$LOG_DIR/${SCRIPT_NAME}.log"
PID_FILE="/var/run/cron_${SCRIPT_NAME}.pid"
# Создаём директорию для логов
mkdir -p $LOG_DIR
# Проверяем, не запущен ли уже этот скрипт
if [ -f "$PID_FILE" ]; then
PID=$(cat "$PID_FILE")
if ps -p $PID > /dev/null 2>&1; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - Script already running (PID: $PID)" >> $LOG_FILE
exit 1
fi
fi
# Сохраняем PID
echo $$ > $PID_FILE
# Логируем начало выполнения
echo "$(date '+%Y-%m-%d %H:%M:%S') - Starting $SCRIPT_NAME" >> $LOG_FILE
# Выполняем скрипт и логируем результат
if /usr/bin/php "$SCRIPT_PATH" >> $LOG_FILE 2>&1; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - $SCRIPT_NAME completed successfully" >> $LOG_FILE
else
echo "$(date '+%Y-%m-%d %H:%M:%S') - $SCRIPT_NAME failed with exit code $?" >> $LOG_FILE
# Отправляем уведомление об ошибке
echo "Cron job $SCRIPT_NAME failed on $(hostname)" | mail -s "Cron Error" admin@example.com
fi
# Удаляем PID файл
rm -f $PID_FILE
# Ротация лога если он больше 10MB
if [ $(stat -f%z "$LOG_FILE" 2>/dev/null || stat -c%s "$LOG_FILE" 2>/dev/null || echo 0) -gt 10485760 ]; then
tail -n 1000 "$LOG_FILE" > "${LOG_FILE}.tmp" && mv "${LOG_FILE}.tmp" "$LOG_FILE"
fi
И использую его в crontab:
*/15 * * * * /usr/local/bin/cron_wrapper.sh /var/www/html/sync_prices.php
0 2 * * * /usr/local/bin/cron_wrapper.sh /var/www/html/backup.php
Для критически важных задач настраиваю мониторинг через внешние сервисы. Например, мониторинг сайта может включать проверку успешности выполнения cron задач.
Безопасность и лучшие практики
За годы работы я выработал несколько правил безопасности для cron задач. Первое и главное — никогда не запускайте cron от root, если это не absolutely необходимо.
Создаю отдельного пользователя для cron задач:
# Создаём пользователя для cron
sudo useradd -r -s /bin/bash -d /var/www -G www-data cronuser
# Настраиваем права доступа
sudo chown -R cronuser:www-data /var/www/html/scripts/
sudo chmod 750 /var/www/html/scripts/
И настраиваю cron от этого пользователя:
sudo crontab -u cronuser -e
Основные правила безопасности, которых я придерживаюсь:
- Используйте полные пути к командам —
/usr/bin/phpвместоphp - Ограничивайте права доступа — скрипты должны иметь минимально необходимые права
- Проверяйте входные данные — даже в cron скриптах
- Используйте блокировки — предотвращайте одновременный запуск одинаковых задач
- Логируйте всё — без логов невозможно отладить проблемы
- Тестируйте на dev-окружении — новые cron задачи сначала проверяйте отдельно
Пример безопасного скрипта для обновления каталога:
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_TIMEOUT => 30
]);
// Обновляем товары
$updated = updateProducts($pdo);
$executionTime = round(microtime(true) - $startTime, 2);
// Логируем успех
error_log(date('Y-m-d H:i:s') . " - Catalog updated successfully. {$updated} products, {$executionTime}s", 3, '/var/log/cron_catalog.log');
} catch (Exception $e) {
// Логируем ошибку
error_log(date('Y-m-d H:i:s') . " - Catalog update failed: " . $e->getMessage(), 3, '/var/log/cron_catalog.log');
// Отправляем уведомление
mail('admin@example.com', 'Catalog Update Failed', $e->getMessage());
exit(1);
} finally {
// Освобождаем блокировку
flock($lockHandle, LOCK_UN);
fclose($lockHandle);
unlink($lockFile);
}
function updateProducts($pdo) {
// Логика обновления товаров
$stmt = $pdo->prepare("UPDATE products SET updated_at = NOW() WHERE status = 'active'");
$stmt->execute();
return $stmt->rowCount();
}
?>
Отладка и тестирование cron задач
Отладка cron задач — это отдельное искусство. Главная сложность в том, что cron выполняется в другом контексте, чем веб-запросы. Переменные окружения, пути, права доступа — всё может быть по-другому.
Вот мой чек-лист для отладки проблемных cron задач:
1. Проверяю базовые вещи:
# Работает ли cron вообще?
sudo systemctl status cron
# Есть ли задачи в crontab?
crontab -l
# Какие переменные окружения у cron?
* * * * * env > /tmp/cron_env.txt
2. Тестирую скрипт вручную:
# Запускаю от того же пользователя, что и cron
sudo -u cronuser /usr/bin/php /var/www/html/script.php
# Проверяю с теми же переменными окружения
sudo -u cronuser env -i /usr/bin/php /var/www/html/script.php
3. Добавляю временную отладочную задачу:
# Каждую минуту пишем отладочную информацию
* * * * * echo "$(date): Cron is working" >> /tmp/cron_debug.log 2>&1
* * * * * /usr/bin/whoami >> /tmp/cron_debug.log 2>&1
* * * * * /usr/bin/php -v >> /tmp/cron_debug.log 2>&1
Был случай: клиент жаловался, что cron не работает. Потратил час на поиски проблемы. Оказалось, что скрипт использовал относительные пути, а cron запускается из домашней директории пользователя. Добавил cd /var/www/html в начало команды — всё заработало.
Ещё одна частая проблема — кодировка. Cron может не устанавливать UTF-8 по умолчанию. Поэтому в начале crontab я всегда добавляю:
LANG=ru_RU.UTF-8
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash
MAILTO=admin@example.com
Для тестирования новых задач я использую такую схему:
# Сначала запускаю каждую минуту для быстрой проверки
* * * * * /var/www/html/scripts/new_task.php
# После успешного тестирования меняю на нужное расписание
0 2 * * * /var/www/html/scripts/new_task.php
И обязательно проверяю логи:
# Системный лог cron
sudo tail -f /var/log/cron
# Лог конкретной задачи
tail -f /var/log/cron_new_task.log
# Общий системный лог (на Ubuntu/Debian)
sudo tail -f /var/log/syslog | grep cron
Оптимизация производительности cron
На высоконагруженных проектах важно правильно распределять cron задачи по времени. Если все задачи запускать одновременно, сервер может не выдержать нагрузки.
Я использую принцип «размазывания» нагрузки:
# Плохо - все задачи в одно время
0 3 * * * /var/www/html/backup.php
0 3 * * * /var/www/html/cleanup.php
0 3 * * * /var/www/html/reports.php
# Хорошо - распределяем по времени
0 3 * * * /var/www/html/backup.php
15 3 * * * /var/www/html/cleanup.php
30 3 * * * /var/www/html/reports.php
Для задач, которые могут выполняться долго, использую очереди. Например, в Laravel:
# В Kernel.php создаём задачи в очереди, а не выполняем сразу
$schedule->job(new ProcessLargeDataset)->hourly()->onQueue('heavy');
$schedule->job(new SendNewsletters)->daily()->onQueue('emails');
И запускаю воркеры очереди через supervisor:
# /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/artisan queue:work --sleep=3 --tries=3
autostart=true
autorestart=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/log/worker.log
На одном проекте интернет-магазина было 15 различных cron задач. Изначально все запускались в 3:00 ночи — сервер «умирал» на 20 минут каждую ночь. После оптимизации распределил задачи с 2:00 до 6:00 утра с интервалом 15-30 минут. Нагрузка стала равномерной.
Также важно мониторить производительность задач:
Альтернативы cron и современные решения
Хотя cron остаётся стандартом де-факто, существуют более современные альтернативы, которые я использую в определённых ситуациях.
Systemd timers — современная замена cron в Linux:
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup timer
Requires=backup.service
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
# /etc/systemd/system/backup.service
[Unit]
Description=Backup service
[Service]
Type=oneshot
User=cronuser
ExecStart=/usr/bin/php /var/www/html/backup.php
Преимущества systemd timers:
- Лучшее логирование через journald
- Возможность настройки зависимостей между задачами
- Автоматический перезапуск при сбоях
- Более гибкое управление ресурсами
Kubernetes CronJobs для контейнеризированных приложений:
# cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: backup-cronjob
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: myapp:latest
command: ["php", "/var/www/html/backup.php"]
restartPolicy: OnFailure
Облачные решения типа AWS CloudWatch Events, Google Cloud Scheduler, Azure Logic Apps — отличный выбор для cloud-native приложений.
Но честно говоря, для 90% веб-проектов обычный cron остаётся оптимальным выбором. Он простой, надёжный, и работает везде где есть Linux.
Настройка автоматических задач — это инвестиция в стабильность вашего проекта. Правильно настроенный cron экономит часы ручной работы и предотвращает множество проблем. Главное — не забывать про мониторинг и логирование. Автоматизация без контроля может принести больше проблем, чем пользы.
Если у вас сложный проект с множеством задач, обратитесь за поддержкой Битрикс, поддержкой WordPress или доработкой сайта — поможем настроить всё правильно с первого раза.
Нужна помощь с настройкой автоматических задач?
Наши специалисты настроят cron jobs и автоматизируют процессы на вашем сайте.