Вопрос или проблема
Я использую cron уже некоторое время; мне кажется, я понимаю некоторые его недостатки и как их обойти. Один из этих недостатков заключается в том, что cron не осознает статус других служб в процессе загрузки; т.е. это может вызвать проблемы для задания, запланированного с помощью @reboot
, если служба, необходимая для этого задания, еще недоступна. Я назову это проблемой cron @reboot
.
Одно из обходных решений этой проблемы заключается в том, чтобы вставить команду sleep
перед запуском скрипта @reboot
. Например, в записи crontab
:
@reboot sleep 15; /path/to/script.sh
Насколько мне известно, это обычная практика для решения проблемы cron @reboot.
Говорят, что systemd может заменить @reboot
и предоставляет пользователям больший контроль во время инициализации/загрузки. “Стоимость” этого улучшения — замена одной строки в crontab
на файл юнита и кривая обучения systemd. Но поскольку служба cron сама по себе запускается под управлением systemd (через /lib/systemd/system/cron.service
на моей Raspberry Pi с ОС buster), похоже, что cron можно запустить таким образом, чтобы устранить проблему cron @reboot
. Это кажется немного неуместным, что под управлением systemd все еще нужно продолжать использовать sleep
хак для решения проблемы cron @reboot
.
Я все еще работаю над своим значком заслуг systemd, но у меня есть представление о том, что cron должен быть одной из последних инициализированных служб. Я говорю это, потому что если cron является последней службой в порядке запуска, то затем следует, что все зависимости также должны быть удовлетворены, и sleep
хак в моих заданиях @reboot
в cron можно устранить.
Попытавшись узнать, как можно указать systemd запустить cron в конце последовательности инициализации, я нашел ответ, в котором говорится, что cron.service
можно запустить как последний и финальный процесс, вставив Type=idle
в /lib/systemd/system/cron.service
. Тестирование этого с помощью версии до и после файла юнита cron.service
показалось разумным подходом; т.е.:
- выполнить
systemd-analyze plot > startup_order_noidle.svg
- добавить
Type=idle
вcron.service
- перезагрузить
- выполнить
systemd-analyze plot > startup_order_idle.svg
К сожалению, перечисление порядка запуска служб под управлением systemd кажется непонятной задачей. Использование systemd-analyze
в соответствии с этой статьей, похоже, предложило ответ, но это не работает так, как я надеялся — cron.service
не всегда отображается. Позвольте мне объяснить:
Размеры файлов .SVG огромны, и я не могу их поделиться, но startup_order_noidle.svg
нигде не показывало cron.service
в списке. С другой стороны, startup_order_idle.svg
показывало cron.service
на строке, следующей за systemd-logind.service
. Другими словами, без добавления Type=idle
cron.service
не отображается, даже несмотря на то, что он был запущен.
Чтобы подытожить: Существует ли надежный способ под управлением systemd запустить cron после того, как все другие службы/устройства/цели и т.д. были запущены? Существует ли надежный способ это проверить?
В общем, в файлах юнитов вы указываете флаги “After=” и “Requires=”, поскольку обычно у вас есть конкретное “требование”, после которого служба должна быть загружена.
В вашем случае, когда вы хотите, чтобы это была абсолютно последняя загружаемая служба, я бы предложил создать свою собственную цель, которая будет загружаться после в настоящее время настроенной последней цели (multi-user.target или graphical.target и т.д.).
В этом сценарии, если новые службы установлены, они обычно по умолчанию будут соответствовать заранее настроенной цели, например, multi-user, оставляя вашу службу cron абсолютно последней, независимо от изменений.
Случайно, вопрос, на который вы ссылались, содержит подробное описание того, как это сделать: https://superuser.com/a/1543365.
Что касается проверки порядка запуска – вы можете использовать команду:
systemctl list-units [-all] [--state=xxx]
*
С состояниями:
- неактивный (по желанию)
- активируется
- не удачно
В рамках предварительного запуска в службе cron для вывода/логирования всех служб, которые не были запущены (или которые не удалось запустить), если вывод активируется
пуст (или содержит службы, которые, вы знаете, должны запускаться после cron), тогда вы можете быть уверены, что cron действительно был последним запущенным.
Обратите внимание, что это обходное решение для systemd-analyze plot
, поскольку вы утверждаете, что оно было ненадежным в вашем случае.
* Доступны дополнительные состояния: active
, deactivating
, dead
, not-found
. Краткое объяснение с дополнительными источниками можно найти здесь: https://superuser.com/a/896951.
Ответ или решение
Чтобы заставить systemd запускать cron в самом конце процесса загрузки, вам необходимо адаптировать системные файлы. В данном руководстве мы рассмотрим надежные шаги, которые помогут вам настроить cron так, чтобы он запускался после всех остальных служб и устройств, что устранит проблему с @reboot в cron и позволит вашему серверу работать более надежно.
Общая Информация о sysftemd и cron
Systemd — это система инициализации, которая управляет службами и их зависимостями во время загрузки. Cron, с другой стороны, не осознает статусы других служб в этот критический момент, что может привести к проблемам, когда скрипты, запланированные для выполнения при запуске, требуют наличие других служб.
Шаги по Настройке systemd для Запуска cron в Последнюю Очередь
-
Создание собственного целевого уровня:
- Чтобы убедиться, что cron запускается последним, создайте новый целевой уровень, который будет загружаться после стандартных целевых уровней, таких как
multi-user.target
илиgraphical.target
. - Создайте новый файл юнита, например
/etc/systemd/system/cron-last.target
.
[Unit] Description=Custom Target for Last Service Requires=multi-user.target After=multi-user.target
- Чтобы убедиться, что cron запускается последним, создайте новый целевой уровень, который будет загружаться после стандартных целевых уровней, таких как
-
Изменение файла cron.service:
- Откройте и измените файл
/lib/systemd/system/cron.service
. Добавьте параметрыAfter=
иWants=
, чтобы связать вашу службу с новым целевым уровнем.
[Unit] Description=Regular background program processing daemon After=cron-last.target Wants=cron-last.target
- Откройте и измените файл
-
Перезагрузка конфигурации systemd:
- После внесения изменений выполните команду для перезагрузки конфигурации и обновления systemd:
sudo systemctl daemon-reload
-
Измените зависимости для cron:
- Запустите cron как часть вашего нового целевого уровня, добавив его к
cron-last.target
:
sudo systemctl enable cron.service --now
- Запустите cron как часть вашего нового целевого уровня, добавив его к
-
Проверка порядка запуска:
- Используйте команду для проверки списка запущенных служб:
systemctl list-units --all --state=active
- Или используйте команду для анализа порядка загрузки:
systemd-analyze plot > startup_order.svg
Это позволит вам увидеть порядок, в котором сервисы были запущены.
Верификация Настройки
Чтобы убедиться, что cron теперь действительно запускается последним, вы можете добавить логирование или создать простую команду в ExecStartPre
вашего юнита cron.service
, чтобы получить статус всех зависимых служб.
ExecStartPre=/bin/sh -c 'systemctl list-units --state=activating'
Заключение
Использование собственных целевых уровней в сочетании с правильными настройками юнита cron позволяет вам обеспечить надежность и устранить проблемы, связанные с запуском служб при старте системы. Теперь ваша система сможет обрабатывать задачи cron, когда все другие службы будут активны и готовы к работе, минимизируя необходимость в использовании временных задержек, таких как команда sleep
.
Следуя данным шагам, вы сможете избежать дополнительных сложностей и повысить стабильность работы вашего cron.