Эта статья — часть Большого Мануала по настройке lamp-сервера на debian.
Предыдущая часть цикла — Настраиваем locales и клавиатуру для ipmi/vnc-консолей.
Следующая часть цикла — учимся генерировать случайные пароли.
Итак, cron. Один из (и это важно — это всего лишь один из) планировщиков задач в *nix-системах. Да, самый распространенный. Да, он есть почти во всех дистрибутивах из коробки.
Придется сначала развеять несколько мифов про крон и рассказать, как им лучше пользоваться на своём сервере. Оговорюсь сразу, что в dear lazynet есть множество точек зрения про крон. И всё что ниже — это моя личная точка зрения. Многие люди «варят» крон другими способами.
Для начала о мифах.
Первый и главный миф состоит в том, что cron — неотъемлемая часть linux-ов, которая обязательно есть и обязательно работает. Нифига подобного. Cron — это всего-лишь одна из программ, которую можно удалить-установить (одноименный пакет cron). Заодно cron вполне себе отзывается на привычное всем /etc/init.d/cron stop|restart|start (или service cron stop|restart|start в дистрибутивах поновее). Соответственно, две новости — крона в системе может не быть, крон может быть остановлен.
Второе — скорее не миф, а просто пояснение. Cron — просто программа, которая запущена в режиме демона. Раз в минуту он «просыпается», перечитывает конфиг и выполняет то, что должно быть запущено в данный момент в соответствии с его конфигом. То бишь, рестартить его не нужно на каждый чих — он сам перечитает новый конфиг.
Теперь о конфиге. Я не буду зачитывать вам вслух мануал по крону, укажу вам только на важные моменты, с которыми связано множество ошибок.
Итак, /etc/cron.d/*. Эта функциональность не встроена в крон — cron.d приезжает в debian с патчем от самих дебианщиков. Конечно же, убунта и прочие утащили этот патч к себе и там cron.d есть, а RH написали свой аналогичный. Но если вы используете маргинальные дистрибутивы или собрали cron руками — там такой фичи может не оказаться.
Теперь о формате времени в cron. У нас есть 5 столбцов — минуты, часы, число месяца, месяц, день недели. Запомните на веки вечные, что звездочка в любом из этих столбцов является именно звездочкой в любом из этих столбцов, а не какой-то магической хренью, которая превратится в то, что вы имели в виду, когда писали периодичность задач.
То есть, «* * * * *» — это каждая минута вообще. «0 12 * * *» — это 12:00 каждого дня. А вот «* 12 * * *» — это не то, о чём вы подумали, а каждая минута с 12:00 до 12:59.
Also, некоторые забывают про то, что третий и пятый столбцы совмещаются между собой по условию «или». То есть запись «0 1 1 * 1» — это и 1:00 понедельника, и 1:00 первого числа каждого месяца, а не только понедельник, если сегодня 1е число.
Ещё я встречал такую хрень — «2,9,16,23,30,37,44,51,58 * * * *» — дескать, раз в 7 минут со сдвигом со второй. Пишите лучше «2-59/7 * * * *» — это тоже самое.
Теперь о самих командах (которые мы пытаемся запустить из крона). Важно понимать, что всё, что вы напишете в качестве команды в cron, будет запущено из sh, а не из bash (есть, конечно, глупые дистрибутивы, где sh — это симлинк на bash или dash, но всё же). Стоит считать, что башизмы из крона работать не будут (те же [[ … ]] двойные условные скобки), ваши алиасы в шелле работать не будут (скорее всего, вы их настраивали через .bashrc).
Если вы не уверены, то стоит попробовать запустить команду через sh, например так:
Ещё важно помнить, что переменная $PATH в кроне по дефолту отличается от той, которую вам написали любимые мейнтейнеры вашего дистра в .profile или .bashrc.
Либо прописываем нормальный $PATH в cron-файлы, либо прописываем $PATH внутри каждого скрипта, либо используем полные пути до бинарников.
Теперь о безопасности. Cron в руках неопытного сисадмина — это такая клевая штука, которая позволяет другому админу получить рута на сервере, когда неопытный сисадмин пропадет, без выключения сервера и перехода в single mode (то есть без даунтайма).
Поговорим о таких ошибках.
Первая ошибка — разрешить редактирование конфиг в /etc/cron.d/ другим пользователям (кроме рута). Современные дистрибутивы в таких случаях не будут читать такой конфиг, но какие-нибудь древние могут такими дуростями не заниматься.
Вторая — запускать из крона от рута файлы, которые могут редактировать другие пользователи. Ну все же видели, как от рута запускают php-cron из состава битрикса )? А я почти на каждом сервере такое встречаю. Или скрипт бэкапа написать под пользователем, а потом запускать от рута — тоже шикарное деяние =)
Третья ошибка — использование звездочки в командах в кроне. Конечно, редко можно выстрелить себе в ногу звездочкой, но возможно. Подробнее — http://www.opennet.ru/opennews/art.shtml?num=40100
Четвертая ошибка — это три предыдущие ошибки второго уровня вложенности. То бишь вы запускаете скрипт от рута, внутри которого запускается другой скрипт от рута, который пользователь может редактировать. Или программу, конфиг которой пользователь может редактировать. Или использовать * на каталог, где пользователи могут создавать папки (что позволит им передать какие-либо параметры тому же tar-у).
В общем, выведем себе правила — стараемся не запускать от рута те скрипты/программы, которые рутом запускать не обязательно (вообще стоит всегда этого правила придерживаться, не только в кроне), а если уж пришлось — то внимательно следим за тем, чтобы пользователь не мог редактировать ничего жизненно важного для запуска этого скрипта.
Теперь о выхлопе крона. По дефолту крон отправляет весь выхлоп скрипта на почту пользователю, который его запустил (да, да, внутри любого linux-а есть свой маленький почтовый сервер, ну или по крайней мере мы его потом настроим). Для любого локального пользователя можно настроить внешний ящик, куда будет отправляться почта, предназначенная этому пользователю. Эти ящики можно вписать в конфиг /etc/aliases (после его редактирования нужно запустить команду newaliases). Если не настраивать, то пользователи смогут почитать свою локальную почту командой mail.
Можно заткнуть cron-конфиг навеки директивой MAILTO. А можно использовать привычные конструкции:
1>/var/log/file.log , чтобы отправить STDOUT в /var/log/file.log (а ошибки из STDERR отправлять на почту)
1>/var/log/file.log 2>&1, чтобы отправить STDOUT и STDERR в /var/log/file.log
1>/var/log/file.log 2>/var/log/file2.log, чтобы писать STDOUT и STDERR в разные логи.
1>/dev/null 2>&1 , чтобы отправить весь выхлоп скрипта в /dev/null (в никуда).
Ну и теперь мои личные загоны. Я очень не люблю, когда пользователи сами настраивают себе cron (командой crontab -e). Эти кроны (они живут в /var/spool/cron/crontabs/) потом теряются при переезде на другой сервер, либо пользователи пишут там какую-нибудь чушь. Поэтому я стараюсь на всех серверах запретить кроны всем, оставив только руту (и создаю руками для каждого пользователя/сайта файлы в /etc/cron.d/*, где руками создаю задачи для этих пользователей).
Поэтому я создаю пустой файл /etc/cron.allow , что запрещает выполнение конфигов из /var/spool/cron/crontabs/ (но не затрагивает /etc/crontab и /etc/cron.d/*).
Вроде всё.
>(или service cron stop|restart|start в дистрибутивах поновее)
Я думал systemctl stop|start|restart cron.service :)
> Я думал systemctl
А в d8 (и u14) всё ещё /etc/init.d/cron работает =)
Стабильность, чо. И неважно, что там под капотом делается.
Ну ок, хотим мы сделать логирование крона, но не комильфо же просто перенаправлять его в файл, а как ротация и прочее. А если мы хотим это сохранять /var/log/package_name/cron.log?
> Ну ок, хотим мы сделать логирование крона, но не комильфо же просто перенаправлять его в файл
Комильфо писать логи прямо в скрипте/программе ;)
А так да — просто перенаправляем в файл через 1>file 2>&1
Ротировать файлы можно по маске.