Что такое cron
cron — самый старый и самый надёжный планировщик задач в UNIX. Появился в 1975 году в Version 7 UNIX, работает практически без изменений до сих пор. Его задача — запускать команды по расписанию.
В data engineering это критично: каждый DAG в Airflow, любой ETL pipeline начинается с того, что нужно «запускать каждый день в 06:00». До Airflow многие DE-задачи планировались напрямую через cron. Сейчас в production обычно сложнее (Airflow, Dagster, Prefect), но cron всё ещё используется для:
- запуск самого Airflow scheduler как сервиса (через systemd или cron @reboot);
- backup-скрипты;
- мониторинговые проверки (
if ! curl http://localhost:8080/health; then send_alert); - log rotation (когда не хватает logrotate);
- cleanup-задачи (см. урок
03-find-by-size-and-cleanupмодуля 14); - legacy data pipelines.
Знать cron — обязательно для Junior DE. Это одна из тех технологий, которая «никогда не уходит».
crontab: что это и как редактировать
crontab — это таблица заданий для одного пользователя. У каждого user — своя crontab. Хранятся они в /var/spool/cron/crontabs/USERNAME (Debian/Ubuntu) или /var/spool/cron/USERNAME (RHEL).
Никогда не редактируй эти файлы напрямую — используй crontab -e. Это запустит editor, проверит синтаксис при save, и атомарно заменит твою таблицу.
$ crontab -e
Откроется $EDITOR (обычно nano, vim или vi). Файл будет пустой (если crontab ещё нет) или с твоими существующими задачами.
# crontab -l — показать текущую таблицу
# crontab -e — редактировать свою crontab
# crontab -r — УДАЛИТЬ свою crontab полностью (опасно, нет подтверждения!)
# crontab -ri — то же с подтверждением
# sudo crontab -u USER -e — редактировать crontab другого user (требует root)
crontab -r без подтверждения удаляет всю crontab немедленно. На пустой клавиатуре crontab -r вместо crontab -e — катастрофа: можно потерять часы настройки. Используй crontab -ri (с подтверждением) или сохраняй crontab в файл (crontab -l > my-crontab.txt) для бэкапа.
Формат crontab: 5 полей + команда
Каждая строка crontab — это одна задача. Формат:
MIN HOUR DOM MON DOW COMMAND
Пять полей времени + команда. Запоминается через мнемонику 'минута час день месяц неделя'.
Синтаксис каждого поля
В каждом поле можно использовать:
*— любое значение.5— конкретное число.1,3,5— список значений.1-5— диапазон.*/15— каждые 15 единиц (/N= step).0-30/5— каждые 5 единиц в диапазоне 0-30 (0, 5, 10, 15, 20, 25, 30).
Примеры расписаний
# Минуты Часы День Месяц День_недели Команда
# Каждый день в 06:00 (DE стандарт: ночной ETL завершился, обновляем витрины):
0 6 * * * /opt/etl/run-daily.sh
# Каждые 15 минут (мониторинг health):
*/15 * * * * /opt/monitor/check-health.sh
# Каждый час в :00 (hourly aggregation):
0 * * * * /opt/etl/run-hourly.sh
# Каждое воскресенье в полночь (weekly backup):
0 0 * * 0 /opt/backup/run.sh
# Первое число каждого месяца в 02:30 (monthly report):
30 2 1 * * /opt/reports/monthly.sh
# С понедельника по пятницу в 09:00 (business hours job):
0 9 * * 1-5 /opt/jobs/business-day.sh
# Каждые 6 часов:
0 */6 * * * /opt/jobs/six-hourly.sh
# 5, 35, 50 минут каждого часа (нерегулярный pattern):
5,35,50 * * * * /opt/jobs/specific.sh
# Каждый день в 03:15 и 15:15 (twice daily):
15 3,15 * * * /opt/jobs/twice.sh
Двойное условие: DOM и DOW
Когда указаны и DOM, и DOW (оба не *), cron применяет OR, а не AND:
# Запустит 15-го числа ИЛИ в понедельник:
0 6 15 * 1 /opt/jobs/run.sh
Если хочешь AND («15-го числа, только если это понедельник»), нужно делать через bash:
0 6 15 * * [ "$(date +\%u)" = "1" ] && /opt/jobs/run.sh
(\% экранирует %, потому что в crontab % — особый символ — newline для stdin команды.)
Special @-shortcuts
Для частых расписаний есть алиасы:
@reboot /opt/etl/start.sh # один раз при загрузке системы
@yearly /opt/jobs/annual.sh # = "0 0 1 1 *"
@annually /opt/jobs/annual.sh # синоним @yearly
@monthly /opt/jobs/monthly.sh # = "0 0 1 * *"
@weekly /opt/jobs/weekly.sh # = "0 0 * * 0"
@daily /opt/jobs/daily.sh # = "0 0 * * *"
@midnight /opt/jobs/daily.sh # синоним @daily
@hourly /opt/jobs/hourly.sh # = "0 * * * *"
@reboot особенный — это не «по расписанию», а «один раз при boot». Полезно для запуска dev-демонов от user-а: @reboot /home/levo/etl-script.sh.
Окружение cron — почему скрипты ломаются
Когда cron запускает команду, окружение очень минимальное. Это первая причина, почему «у меня в shell работает, а в cron нет».
# Что видит cron:
$ crontab -e
* * * * * env > /tmp/cron-env.log
Через минуту проверь /tmp/cron-env.log:
HOME=/home/levo
LANG=en_US.UTF-8
LOGNAME=levo
PATH=/usr/bin:/bin
PWD=/home/levo
SHELL=/bin/sh
LC_ALL=C
Сравни со своим shell:
$ env | wc -l
46
Всего 7 vs 46 переменных. Что не загружено в cron:
- PATH — крошечный
/usr/bin:/bin. Нет/usr/local/bin, нет~/bin, нет PATH из virtual environments. .bashrc/.bash_profile— не источниваются. Алиасы, функции, экспорты из dotfiles — недоступны.- DISPLAY — нет (понятно, нет терминала).
SHELL—/bin/sh(минималистичная Bourne), не/bin/bash. Bash-specific syntax типа[[ ... ]]упадёт.
Решение: задавай env в crontab
В начале crontab можно указать переменные:
# Дополнительный PATH для использования внутри cron-задач:
PATH=/usr/local/bin:/usr/bin:/bin
# Куда слать stderr (по умолчанию — email root):
[email protected]
# Использовать bash вместо sh:
SHELL=/bin/bash
# Задачи:
0 6 * * * /opt/etl/run.sh
0 7 * * * cd /opt/etl && /opt/etl/venv/bin/python script.py
SHELL=/bin/bash важен, если в твоих cron-командах нужны bash-features (массивы, [[ ]], <()).
Подробнее про проблемы cron-окружения — в следующем уроке 02-cron-gotchas.
Логи: куда уходит вывод
По умолчанию stdout/stderr cron-задач отправляются email на UID-владельца. В современных VM email обычно не настроен — вывод просто теряется в локальном postfix queue (/var/mail/USERNAME).
Чтобы видеть результаты, нужно явно redirect:
# stdout -> файл, stderr тоже:
0 6 * * * /opt/etl/run.sh >> /var/log/etl/run.log 2>&1
# stdout -> файл, stderr -> отдельный файл:
0 6 * * * /opt/etl/run.sh >> /var/log/etl/run.log 2>> /var/log/etl/run.err
# Стандартный совет: всё в один лог, перезаписывая:
0 6 * * * /opt/etl/run.sh > /var/log/etl/run.log 2>&1
>> — append, > — overwrite. Для cron обычно append (история).
Сам cron логирует свою активность через systemd journal или syslog:
# Что cron делал (на современной Ubuntu/Debian):
$ journalctl _COMM=cron
# Или (legacy):
$ grep CRON /var/log/syslog
May 13 06:00:01 prod-vm CRON[12345]: (etl) CMD (/opt/etl/run.sh)
(etl) — от чьего имени. CMD (...) — что запустил. Не показывает stdout/stderr — только сам факт запуска.
DE-сценарий: запустить ETL в 06:00 и логировать
Реальная задача: каждый день в 06:00 запустить Python ETL-скрипт, логи писать в файл, иметь возможность retry.
# /etc/cron.d/orders-etl или crontab -e
SHELL=/bin/bash
PATH=/opt/orders-etl/venv/bin:/usr/local/bin:/usr/bin:/bin
[email protected]
# Daily orders ETL at 06:00, log to file, capture exit code
0 6 * * * etl cd /opt/orders-etl && python -m orders_etl >> /var/log/orders-etl/run.log 2>&1
Заметь: 0 6 * * * etl COMMAND — здесь после расписания указан user (etl) от имени которого запускать. Это формат system crontab (в /etc/cron.d/ или /etc/crontab), а не user crontab (crontab -e).
System crontab vs user crontab:
Два места, где живут cron-задачи. Разный формат, разные права.
Для production DE-задач предпочтительнее /etc/cron.d/orders-etl — он управляется через config management (Ansible, Puppet, Salt), легче audit, не привязан к одному user.
Service: проверка что cron работает
# Запущен ли cron daemon?
$ systemctl status cron
# На RHEL/CentOS:
$ systemctl status crond
# Перезагрузить cron (после правки /etc/cron.d/):
$ sudo systemctl restart cron
После правки /etc/cron.d/SVC cron daemon обычно сам подхватит изменения (он мониторит mtime каталога). Но если правишь /etc/crontab и опасаешься — systemctl restart cron. После crontab -e (user crontab) restart НЕ нужен — crontab сам шлёт сигнал daemon.
DE-кейс: cron + Airflow
Стандартный сценарий: Airflow scheduler сам управляет DAG-ами по расписанию (он сам внутри cron-like). Но сам Airflow scheduler должен запускаться, и тут варианты:
- systemd service (предпочтительно для production) — урок
04-writing-service-unitмодуля 15. - cron @reboot (для dev/staging) —
@reboot /opt/airflow/start.sh. - systemd timer (для batch oneshot tasks).
Один из распространённых паттернов: cron-задача каждые 5 минут проверяет, жив ли scheduler, и если нет — рестартит. До systemd это был основной способ supervise:
*/5 * * * * etl systemctl is-active --quiet airflow-scheduler || systemctl restart airflow-scheduler
(systemctl restart требует root, поэтому такое обычно сложнее настроить — sudoers нужно править. Просто как пример паттерна.)
Попробуй сам
- Посмотри свою текущую crontab:
crontab -l - Добавь тестовую задачу:
crontab -e # Внутри: */2 * * * * date > /tmp/cron-test.log # Подожди 2 минуты и: cat /tmp/cron-test.log - Что cron делал недавно:
journalctl _COMM=cron --since "10 min ago" - Проверь, запущен ли cron:
systemctl status cron - Посмотри system crontab каталоги:
ls /etc/cron.{hourly,daily,weekly,monthly} - Сложный test — что считается «завтра в 03:00» в cron-выражении?
# 0 3 * * * -> каждый день в 03:00 (т.е. и завтра в 03:00) # Чтобы РОВНО завтра — нужен DOM с конкретным числом, например: # 0 3 14 5 * -> 14 мая в 03:00 (если сегодня 13-е)
macOS-различия
- На macOS cron установлен по умолчанию (
/usr/sbin/cron). Работает аналогично. - Apple предпочитает
launchd(см. модуль 15, macOS-секция урока 04). Для launchd используются.plistс расписанием. crontab -eна macOS работает — но Apple рекомендует launchd для новых задач./etc/cron.d/существует, но используется меньше — обычно отдельные launchd jobs.
Главное
crontab -e— редактировать свою crontab.-lпоказать,-rудалить (опасно!).- Формат:
MIN HOUR DOM MON DOW COMMAND. 5 полей времени. - Каждое поле:
*(любое),5(точно),1,3,5(список),1-5(диапазон),*/15(каждые 15). - Special:
@reboot,@daily,@hourly,@weekly,@monthly,@yearly. - Окружение cron очень минимальное:
PATH=/usr/bin:/bin, нет.bashrc,SHELL=/bin/sh. Задавай переменные в начале crontab. - Логи: stdout/stderr cron уходят в email (
MAILTO=). Чтобы видеть — redirect:>> /var/log/script.log 2>&1. - System crontab:
/etc/cron.d/SVC— формат с дополнительным полемUSER, для admin/пакетных задач. - Каталоги
/etc/cron.{hourly,daily,weekly,monthly}/— любой исполняемый файл там запустится по расписанию. - DE-применения: ETL daily, monitoring каждые 15 min, cleanup logs, backup, supervise scheduler.