Learning Platform
Глоссарий Troubleshooting
Урок 17.01 · 22 мин
Начальный
croncrontabschedulingdata engineering

Что такое 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)
DANGER

crontab -r без подтверждения удаляет всю crontab немедленно. На пустой клавиатуре crontab -r вместо crontab -e — катастрофа: можно потерять часы настройки. Используй crontab -ri (с подтверждением) или сохраняй crontab в файл (crontab -l > my-crontab.txt) для бэкапа.

Формат crontab: 5 полей + команда

Каждая строка crontab — это одна задача. Формат:

MIN HOUR DOM MON DOW COMMAND
Поля crontab

Пять полей времени + команда. Запоминается через мнемонику 'минута час день месяц неделя'.

MIN (минуты)0-59
HOUR (часы)0-23 (24-часовой)
DOM (день месяца)1-31
MON (месяц)1-12 или JAN-DEC
DOW (день недели)0-6 (0=воскресенье)
COMMANDкоманда (sh -c)

Синтаксис каждого поля

В каждом поле можно использовать:

  • * — любое значение.
  • 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:

System crontab vs User crontab

Два места, где живут cron-задачи. Разный формат, разные права.

user crontabcrontab -e, без поля user
/etc/cron.d/SVC6 полей: расписание + USER + cmd
/etc/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.

Kubernetes CronJob: cron в контейнерном мире

DE-кейс: cron + Airflow

Стандартный сценарий: Airflow scheduler сам управляет DAG-ами по расписанию (он сам внутри cron-like). Но сам Airflow scheduler должен запускаться, и тут варианты:

  1. systemd service (предпочтительно для production) — урок 04-writing-service-unit модуля 15.
  2. cron @reboot (для dev/staging) — @reboot /opt/airflow/start.sh.
  3. 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 нужно править. Просто как пример паттерна.)

Попробуй сам

  1. Посмотри свою текущую crontab:
    crontab -l
  2. Добавь тестовую задачу:
    crontab -e
    # Внутри: */2 * * * * date > /tmp/cron-test.log
    # Подожди 2 минуты и:
    cat /tmp/cron-test.log
  3. Что cron делал недавно:
    journalctl _COMM=cron --since "10 min ago"
  4. Проверь, запущен ли cron:
    systemctl status cron
  5. Посмотри system crontab каталоги:
    ls /etc/cron.{hourly,daily,weekly,monthly}
  6. Сложный 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.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Какое crontab-выражение запустит задачу каждый день в 06:00?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4