#linux #docker #docker-compose #cron
Вопрос:
Я пытаюсь использовать контейнер docker на основе изображения Alpine для выполнения запланированного задания cron, следуя этому руководству, но после печати инструкции в моем сценарии запуска контейнер просто завершает работу, не выполняя другой мой сценарий.
Моя служба docker-compose настроена следующим образом:
cron:
image: alpine:3.11
command: /usr/local/startup.sh amp;amp; crond -f -l 8
volumes:
- ./cron_tasks_folder/1min:/etc/periodic/1min/:ro
- ./cron_tasks_folder/15min:/etc/periodic/15min/:ro
- ./cron_tasks_folder/hourly:/etc/periodic/hourly/:ro
- ./scripts/startup.sh:/usr/local/startup.sh:ro
Таким образом, он запускает начальный сценарий, называемый startup.sh
, а затем запускает демона cron. startup.sh
Сценарий содержит следующее:
#!/bin/sh
echo "Starting startup.sh.."
echo "* * * * * run-parts /etc/periodic/1min" >> /etc/crontabs/root
crontab -l
sleep 300
Я сбросил туда команду sleep только для того, чтобы запустить интерактивную оболочку в контейнере и убедиться, что все внутри выглядит хорошо. Скрипт создает другую папку для 1мин скриптов. Я добавил туда тестовый сценарий, и я могу убедиться, что он там есть:
/etc/periodic/1min # ls -a
. .. testScript
Скрипт является исполняемым:
/etc/periodic/1min # ls -l testScript
-rwxr-xr-x 1 root root 31 Jul 30 01:51 testScript
И testScript
это просто эхо-утверждение, чтобы убедиться, что оно работает в первую очередь:
echo "The donkey is in charge"
И, просматривая root
файл в etc/crontabs, я вижу следующее (я несколько раз запускал контейнер повторно, и каждый раз он создавал новую папку на 1 минуту, что не нужно, но я думаю, что проблема здесь не в этом):
# do daily/weekly/monthly maintenance
# min hour day month weekday command
*/15 * * * * run-parts /etc/periodic/15min
0 * * * * run-parts /etc/periodic/hourly
0 2 * * * run-parts /etc/periodic/daily
0 3 * * 6 run-parts /etc/periodic/weekly
0 5 1 * * run-parts /etc/periodic/monthly
* * * * * run-parts /etc/periodic/1min
* * * * * run-parts /etc/periodic/1min
* * * * * run-parts /etc/periodic/1min
* * * * * run-parts /etc/periodic/1min
* * * * * run-parts /etc/periodic/1min
Оператор echo в testScript
никогда не печатается на моем терминале, и контейнер выходит с кодом выхода 0 вскоре после запуска. Я хочу печатать это заявление каждую минуту… что я упускаю?
Комментарии:
1. как вы думаете, где вы запускаете планировщик хрона?
crontab -l
перечисляет задания cron, но не запускает планировщик. Исходя из предоставленных вами данных, я бы сказал, что на самом деле вы не запускаете cron.2. @DanielFarrell, насколько я понимаю,
amp;amp; crond -f -l 8
вcommand
docker-compose это делает.3. Ладно, ладно, я это пропустил. Но
startup.sh
придется выйти до того, как запущенная оболочкаcommand
выполнит crond. Ты ждешь эти 5 минут? На твоем месте я бы поставилexec crond -f -l 8
на местоsleep
«вstartup.sh
«. Убедитесь, что вы проверяете журналы докеров, если это не работает.4. Это работает! Сейчас я получаю следующую ошибку, но это другая проблема:
run-parts: can't execute '/etc/periodic/1min/testScript': Exec format error
. Тем не менее, я также могу удалить командуamp;amp; crond -f -l 8
из моего docker-compose. Является ли такая конфигурация приемлемой практикой? Если вы хотите ответить этим ответом, я приму его. Спасибо.5. я дам реальный ответ. Можете ли вы указать
ENTRYPOINT
, если таковые имеются, в своем файле dockerfile?
Ответ №1:
В файле компоновки docker у вас есть
command: /usr/local/startup.sh amp;amp; crond -f -l 8
Намерение состоит в том, чтобы работать как команда оболочки, но из вопроса совсем не ясно, что произойдет; это зависит от вас ENTRYPOINT
. Поскольку он определен в []
скобках, дополнительная оболочка не будет предоставлена. command
Значение будет передано в качестве аргументов в ENTRYPOINT
.
Предполагая, что это станет командой оболочки, amp;amp;
в оболочке выполняется левая сторона, и если это удастся, то выполняется правая сторона. Поэтому startup.sh
необходимо завершить до crond
того, как будет выполнено. startup.sh
заканчивается на
sleep 300
crond
вызывается только после этого 300 секунд.
В любом случае crond
либо вообще не вызывается, либо sleep
не завершается. Комментарии показывают, что crond
была обнаружена ошибка запуска.
Использование точки входа, подобной этой, является стандартной практикой для настройки среды до или предоставления параметров среды выполнения при вызове основного исполняемого файла. Чтобы сделать это правильно, вы должны убедиться, что используете exec
для запуска основной исполняемый файл, чтобы он получал сигналы, которые в противном случае отправлялись бы в оболочку bash, выполняющую сценарий точки входа.
Итак, в конце startup.sh
:
exec crond -f -l 8
Заменит запущенную оболочку startup.sh
crond
, чтобы crond
она принимала все сигналы (в этот момент оболочка исчезла). Это тонко, но важно!
В общем, сохраняйте вызов приложения как можно более простым. В данном случае процесс выполнения был разделен между точкой входа, командой и сценарием запуска без четкого интерфейса между ними. Вы бы не зациклились на вызове, если бы вставили crond
его непосредственно в файл Dockerfile и оставили на этом. Иногда аргументы должны предоставляться во время выполнения, но переменные среды, которые имеют имена, а не только позиции, часто являются предпочтительными. Это упрощает вызов и упрощает отладку. Но, когда это не работает, точка входа в сценарий оболочки — отличное решение-просто убедитесь в exec
своем окончательном процессе!
Комментарии:
1. Вы могли бы рассмотреть возможность завершения сценария точки входа с
exec "$@"
помощью , а затем настройкиCMD crond -f -l 8
. Это будет иметь тот же чистый эффект, за исключением того, что вы можетеdocker run --rm -it your-image sh
запустить сценарий установки точки входа, но завершить его интерактивной оболочкой вместо демона cron или иным образом запустить какую-либо альтернативную команду после выполнения первой настройки.2. Важно отметить, что моя последняя ошибка была устранена путем добавления
#!/bin/sh
ошибки в верхней части моего сценария, а не только самого оператора echo.