Как стать автором
Поиск
Написать публикацию
Обновить

Cron в Linux: полное руководство для админов + скрытые проблемы

Уровень сложностиПростой
Время на прочтение8 мин
Количество просмотров1K

Привет, Хабр! Все, кто администрирует Linux, рано или поздно сталкивается с cron - стандартным планировщиком задач. Но если настроить его "на скорую руку", можно обнаружить неприятные сюрпризы:

  • Скрипт не запустился, тк cron работает в другом окружении

  • Сервер лёг от нагрузки, потому что 100 задач стартовали одновременно

  • Вы не узнали об ошибке из-за того, что вывод скрипта попал в /dev/null

В этой статье разбор не только основы работы с cron, но и:

  • Продвинутые форматы расписания - как задавать сложные интервалы и комбинировать условия

  • Типичные подводные камни - работа с переменными окружения, логирование, управление параллельным выполнением

  • Альтернативы для сложных сценариев - когда cron уже недостаточно и стоит обратить внимание на systemd.timer

А также, дополнительная информация:

  • Как избежать "падений" из-за наложения задач

  • Когда cron - хороший выбор, а когда лучше использовать другие инструменты

Статья будет полезна как начинающим администраторам, так и тем, кто хочет глубже разобраться в автоматизации задач в Linux.

Что такое cron

Cron - классический планировщик задач в Unix-подобных операционных системах, позволяющий автоматизировать выполнение команд и скриптов по расписанию. Позволяет запускать команды/скрипты в определенное время или с определенной периодичностью.

Состоит cron из нескольких ключевых компонентов:

  • Демон crond - фоновый процесс, который запускается при старте системы и работает постоянно

  • Таблицы cron (crontab) - файлы с расписанием задач

  • Журнал выполнения - обычно записывается в syslog или отдельные логи

Демон cron просыпается каждую минуту, проверяет crontab и выполняет задачи, которые должны запуститься в текущую минуту.

Синтаксис:

Что это значит? Разберём несколько примеров:

  1. 0 * * * * - каждый час в начале часа

  2. 0 0 * * * - каждый день в полночь

  3. 0 0 * * 0 - каждое воскресенье в полночь

  4. */15 * * * * - каждые 15 минут

  5. 0 4 1 * * - первое число каждого месяца в 4:00 утра

Также, в cron существуют "специальные строки" для часто используемых интервалов, частота которых предопределена:

  • @yearly или @annually - один раз в год (0 0 1 1 *)

  • @monthly - один раз в месяц (0 0 1 * *)

  • @weekly - один раз в неделю (0 0 * * 0)

  • @daily или @midnight - один раз в день (0 0 * * *)

  • @hourly - один раз в час (0 * * * *)

  • @reboot - при запуске системы

Следующим важным аспектом являются переменные окружения. Cron выполняет команды в минимальном окружении:

  • PATH - обычно очень ограничен (/usr/bin:/bin)

  • SHELL - обычно /bin/sh. Рекомендуется для сложных сценариев (поддержка массивов, функций)

  • HOME - домашний каталог пользователя. Подходит для задач, работающих с пользовательскими файлами

  • MAILTO - email для отправки результатов (можно использовать несколько, через запятую). Для отправки в syslog использовать: MAILTO=syslog

Крайне рекомендуется всегда указывать полные пути к командам и файлам в заданиях.

Почему рекомендуется указывать полные пути

Всегда указывать полные пути к командам и файлам в cron-заданиях связана с особенностями работы и потенциальными проблемами, которые могут (но необязательно возникнут) при их игнорировании. Постараюсь учесть все возможные:

1. Ограниченное окружение cron

Когда cron запускает задание, оно выполняется в минимальном окружении, которое сильно отличается от вашего обычного пользовательского окружения:

  • Очень ограниченная переменная PATH (обычно только /usr/bin:/bin)

  • Отсутствуют многие пользовательские переменные окружения

  • Нет .bashrc.profile и других конфигурационных файлов

Пример проблемы:

* * * * * myscript.sh  # Может не найти команду

В то время, как:

* * * * * /home/user/scripts/myscript.sh  # Точно найдёт скрипт

2. Разные версии программ

Полные пути помогают избежать ситуаций, когда cron находит другую версию программы, которая нам не нужна.

Пример:

* * * * * python3 script.py  # Какой именно python3?

Правильнее:

* * * * * /usr/local/bin/python3 /path/to/script.py

3. Проблемы с относительными путями

Cron выполняет команды с разными рабочими каталогами (часто это домашний каталог пользователя или корневой каталог). Относительные же пути могут вести не туда, куда изначально было запланировано.

Пример:

* * * * * ./script.sh  # Где ищется этот файл?

Лучше:

* * * * * /absolute/path/to/script.sh

4. Проблемы с перенаправлением вывода

Перенаправление вывода может повести себя крайне неожиданно без полных путей.

Пример:

* * * * * some_command > output.log  # Куда именно запишется файл?

Более верно:

* * * * * /usr/bin/some_command > /full/path/to/output.log

Как найти полные пути?

Помечаю для тех, кому мало знаком Linux. В данном вопросе поможет команда which или whereis:

which python3
# /usr/local/bin/python3

whereis bash
# bash: /bin/bash /usr/share/man/man1/bash.1.gz

Управление crontab-файлами

Основные команды для работы cron:

Основные команды для работы с cron:

  1. crontab -e - редактирование crontab текущего пользователя

  2. crontab -l - просмотр текущего crontab

  3. crontab -r - удаление crontab

  4. crontab -u username -e - редактирование crontab другого пользователя (требует прав root)

Системные crontab-файлы обычно расположены в:

  1. /etc/crontab - системный crontab

  2. /etc/cron.d/ - каталог для дополнительных crontab-файлов

  3. /var/spool/cron/crontabs/ - каталог с пользовательскими crontab (в некоторых дистрибутивах)

Продвинутые возможности cron

В данном блоке разберём продвинутые возможности, которые могут быть знакомы не каждому. Они позволяют использовать не только простые временные интервалы, но и сложные шаблоны для точного управления планированием задач. Подобная гибкость позволяет адаптировать базовый планировщик под различные нужды - от периодического обслуживания до сложных многоэтапных процессов.

1. Сложные интервалы и комбинации

Cron поддерживает гибкие форматы расписания, но не все знают, как их комбинировать. Далее приведу примеры использования различных "не базовых" интервалов и комбинаций:

Диапазоны (-)

  • 0 9-18 * * * - каждый час с 9:00 до 18:00

  • 0 0 1-7 * * - первые 7 дней месяца в 00:00

Списки (,)

  • 0 0 1,15 * * - 1-го и 15-го числа каждого месяца

  • 0 0 * * 1,3,5 - по понедельникам, средам и пятницам

Шаги (/)

  • */5 * * * * - каждые 5 минут

  • 0 */3 * * * - каждые 3 часа

Комбинирование

  • 0 8-18/2 * * 1-5 - каждые 2 часа с 8:00 до 18:00, но только в рабочие дни

  • 0 0,12 1-10,15 * * - в 00:00 и 12:00 с 1-го по 10-е и 15-го числа

2. Управление параллельным выполнением

Если задача выполняется дольше, чем интервал запуска - существует риск возникновения конфликтов. Возникает это по различным причинам:

  1. Накопление процессов (например, выполнение задачи 10 минут, а интервал запуска - 5 минут, то через некоторое время будут работать несколько экземпляров скрипта)

  2. Блокировка файлов и ресурсов (например, если скрипт работает с БД или логами, несколько экземпляров могут пытаться изменять идентичные данные одновременно)

  3. Проблемы с состоянием (например, первый запуск меняет конфиг, а второй запуск об изменениях "не знает", и действует некорректно)

  4. Ошибки в логировании (несколько процессов пишут в один лог. В лучшем случае данные просто перемешаются, в худшем - у Вас появится битый файл)

В данном случае мы можем решить проблему следующими способами:

  1. Ограничение времени выполнения (timeout)

* * * * * timeout 300 /path/to/script.sh  # Остановить через 5 минут
  1. Блокировка через flock

* * * * * /usr/bin/flock -n /tmp/myscript.lock /path/to/script.sh

Где -n - не ждать, если скрипт уже работает (-w 10 - ждать 10 секунд, если требуется)

Что такое flock

flock — утилита управления блокировкой файлов. Позволяет предотвратить одновременный запуск нескольких экземпляров скрипта, что критично для задач cron, если:

  • Скрипт выполняется дольше, чем интервал между запусками

  • Несколько процессов могут конфликтовать при работе с общими ресурсами (файлами, БД и тд)

Синтаксис:

  • -n - не ждать

  • -w N - ждать N секунд

  • -x - эксклюзивная блокировка (запрещает доступ всем процессам)

  • -s - разделяемая блокировка (разрешает остальным доступ на чтение)

3. Логирование и обработка ошибок

Перенаправление вывода:

  • >> file.log 2>&1 - stdout + stderr в один файл

  • >> file.log 2>> error.log - раздельные логи

Логирование с ротацией:

0 0 * * * /path/to/script.sh >> "/var/log/script_$(date +\%Y\%m\%d).log" 2>&1

Примеры сложных сценариев cron

Теория - безусловно хороший аспект, но с примерами сценариев, информация, надеюсь, усвоится лучше. Ниже я приведу несколько примеров:

1. Каскадные задачи с задержкой и проверкой статуса

# Основная задача в 2:00
0 2 * * * /path/to/first_task.sh && touch /tmp/first_success

# Вторая задача через 15 минут, только если первая успешна
15 2 * * * [ -f /tmp/first_success ] && /path/to/second_task.sh && rm /tmp/first_success
  • first_task.sh запускается в 2:00, и если успешно (&&), создаётся файл-флаг /tmp/first_success

  • В 2:15 проверяется наличие флага ([ -f ... ]), и если он есть, запускается second_task.sh, после чего флаг удаляется

  • Потенциальное применение: цепочки задач (например, сбор данных - обработка - отправка отчёта)

2. Запуск только при низкой загрузке CPU

0 22 * * * [ $(awk '{print 100-$NF}' /proc/loadavg | cut -d. -f1) -lt 30 ] && /path/to/backup.sh
  • awk '{print 100-$NF}' /proc/loadavg - вычисляет свободный CPU (100 — загрузка)

  • cut -d. -f1 - оставляет только целую часть

  • -lt 30 - проверяет, что свободно больше 70% (загрузка < 30%)

  • Потенциальное применение: тяжёлые задачи (анализ логов, бэкапы).

3. Пропуск запуска, если процесс уже выполняется

*/5 * * * * [ $(pgrep -c -f "script.sh") -eq 0 ] && /path/to/script.sh
  • pgrep -c -f "script.sh" - подсчитывает количество запущенных процессов

  • -eq 0 - если процесс не найден (0), задача запускается

  • Потенциальное применение: предотвращение дублирующихся задач (например, импорт данных).

4. Ротация логов

0 3 1 * * find /var/log/app/ -name "*.log" -mtime +30 -exec gzip {} \;
  • find /var/log/app/ - ищет файлы .log старше 30 дней

  • -exec gzip {} \; - сжимает их

5. Ограничение времени выполнения скрипта

0 * * * * timeout 600 /path/to/long_script.sh
  • timeout 600 - убивает процесс через 600 секунд (10 минут).

Альтернатива cron

Возможно, в момент прочтения блоков выше, были вопросы из-за наличия Systemd Timers, как наилучшей альтернативе cron. Как и в любой тематике нашей сферы, аналоги есть буквально у всего.

Я поведаю о нём кратко, тк статья немного не про него. Ниже - ключевые особенности и сравнение.

1. Ключевые особенности

  1. Точное расписание, а именно - запуск по календарю (OnCalendar=*-*-* 03:00:00 — ежедневно в 3:00) и относительные интервалы (OnBootSec=5min - через 5 минут после старта системы).

  2. Зависимости. К примеру, можно указать зависимость от других сервисов (старт задачи после запуска сети).

  3. Устойчивость к пропускам. Если сервер был отключен - таймер не запустит задачу при следующем включении (Persistent=true).

  4. Интеграция с systemd. Логирование в journalctl, управление командами:

systemctl start mytimer.timer  # Запустить таймер
journalctl -u mytimer.service  # Посмотреть логи

2. Сравнение

Возможность

Systemd Timer

Cron

Зависимости

Да (Requires=)

Нет

Запуск после пропуска

Да (Persistent)

Нет

Логирование

journalctl

Файлы (/var/log/)

Точное время

Секунды

Только минуты

Заключение

Какой вывод можно сделать из статьи? Cron - простой и полезный инструмент для автоматизации задач, но при не самом аккуратном использовании может "подкинуть" Вам проблем: от падения сервера из-за перегрузки до "тихих" ошибок, о которых можно долго не знать. Самые важные правила при его использовании:

  1. Всегда указывать полные пути

  2. По возможности контролировать параллельное выполнение

  3. Обязательно настраивать логирование (это, наверное, для всей ИТ-сферы применимо. Не мне учить, так сказать)

Для сложных сценариев, где обязательны зависимости между задачами или более точное время выполнения - безусловно systemd.timer будет более приоритетным, тк даёт в разы больше контроля и лучше интегрируется. В конечном итоге, выбор между cron и альтернативами напрямую зависит от задач. Что-то простое? Используем cron. Необходима гибкость и надёжность? Systemd timers незаменим.

P.S. Я веду свою группу в Телеграмм, буду рад видеть всех, кому интересен процесс написания скриптов и автоматизация в мире IT.

Теги:
Хабы:
+6
Комментарии0

Публикации

Ближайшие события