我试图在调用shell脚本的docker容器内运行cronjob。
昨天我一直在网上搜索,堆栈溢出,但我真的找不到一个有效的解决方案。 我该怎么做呢?
我试图在调用shell脚本的docker容器内运行cronjob。
昨天我一直在网上搜索,堆栈溢出,但我真的找不到一个有效的解决方案。 我该怎么做呢?
当前回答
在生产环境中,公认的答案可能是危险的。
在docker中,你应该每个容器只执行一个进程,因为如果你不这样做,fork和后台进程就不会被监控,可能会在你不知道的情况下停止。
当你使用CMD cron && tail -f /var/log/cron.log时,cron进程基本上会分叉,以便在后台执行cron,主进程退出,让你在前台执行tailf。后台cron进程可能会停止或失败,但你不会注意到,你的容器仍然会默默地运行,你的编排工具也不会重新启动它。
你可以通过直接将cron的命令输出重定向到分别位于/proc/1/fd/1和/proc/1/fd/2的docker stdout和stderr中来避免这样的事情
使用基本的shell重定向,你可能想做这样的事情:
* * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2
你的CMD将是:CMD ["cron", "-f"]
但是:如果你想以非根用户的身份运行任务,这就行不通了。
其他回答
专注于在接收到SIGTERM或SIGQUIT信号时优雅地停止cronjob(例如在运行docker stop时)。
这并不容易。默认情况下,cron进程只是被杀死,而不注意运行cronjob。我在详细阐述pablorsk的回答:
Dockerfile:
FROM ubuntu:latest
RUN apt-get update \
&& apt-get -y install cron procps \
&& rm -rf /var/lib/apt/lists/*
# Copy cronjobs file to the cron.d directory
COPY cronjobs /etc/cron.d/cronjobs
# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/cronjobs
# similarly prepare the default cronjob scripts
COPY run_cronjob.sh /root/run_cronjob.sh
RUN chmod +x /root/run_cronjob.sh
COPY run_cronjob_without_log.sh /root/run_cronjob_without_log.sh
RUN chmod +x /root/run_cronjob_without_log.sh
# Apply cron job
RUN crontab /etc/cron.d/cronjobs
# to gain access to environment variables, we need this additional entrypoint script
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# optionally, change received signal from SIGTERM TO SIGQUIT
#STOPSIGNAL SIGQUIT
# Run the command on container startup
ENTRYPOINT ["/entrypoint.sh"]
entrypoint.sh:
#!/bin/bash
# make global environment variables available within crond, too
printenv | grep -v "no_proxy" >> /etc/environment
# SIGQUIT/SIGTERM-handler
term_handler() {
echo 'stopping cron'
service cron stop
echo 'stopped'
echo 'waiting'
x=$(($(ps u -C run_cronjob.sh | wc -l)-1))
xold=0
while [ "$x" -gt 0 ]
do
if [ "$x" != "$xold" ]; then
echo "Waiting for $x running cronjob(s):"
ps u -C run_cronjob.sh
xold=$x
sleep 1
fi
x=$(($(ps u -C run_cronjob.sh | wc -l)-1))
done
echo 'done waiting'
exit 143; # 128 + 15 -- SIGTERM
}
# cron service with SIGTERM and SIGQUIT support
service cron start
trap "term_handler" QUIT TERM
# endless loop
while true
do
tail -f /dev/null & wait ${!}
done
的计划
* * * * * ./run_cronjob.sh cron1
*/2 * * * * ./run_cronjob.sh cron2
*/3 * * * * ./run_cronjob.sh cron3
假设您将所有cronjob包装在run_cronjob.sh脚本中。这样,您就可以执行任意代码,关机将优雅地等待。
Run_cronjobs.sh(保持cronjob定义干净的可选帮助脚本)
#!/bin/bash
DIR_INCL="${BASH_SOURCE%/*}"
if [[ ! -d "$DIR_INCL" ]]; then DIR_INCL="$PWD"; fi
cd "$DIR_INCL"
# redirect all cronjob output to docker
./run_cronjob_without_log.sh "$@" > /proc/1/fd/1 2>/proc/1/fd/2
run_cronjob_without_log.sh
your_actual_cronjob_src()
顺便说一下,当接收到SIGKILL时,容器仍然会立即关闭。这样你就可以使用docker-compose stop -t 60 cron-container这样的命令来等待60秒,让cronjob优雅地完成,但仍然在超时后终止它们。
还有另一种方法,就是使用Tasker,它是一种支持cron(调度器)的任务运行器。
为什么?有时为了运行cron作业,你必须将你的基本映像(python, java, nodejs, ruby)与crond混合。这意味着要维持另一个形象。Tasker通过解耦crond和you容器来避免这种情况。您可以只关注想要执行命令的映像,并配置Tasker来使用它。
这里是船坞式作曲。Yml文件,它将为您运行一些任务
version: "2"
services:
tasker:
image: strm/tasker
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
environment:
configuration: |
logging:
level:
ROOT: WARN
org.springframework.web: WARN
sh.strm: DEBUG
schedule:
- every: minute
task: hello
- every: minute
task: helloFromPython
- every: minute
task: helloFromNode
tasks:
docker:
- name: hello
image: debian:jessie
script:
- echo Hello world from Tasker
- name: helloFromPython
image: python:3-slim
script:
- python -c 'print("Hello world from python")'
- name: helloFromNode
image: node:8
script:
- node -e 'console.log("Hello from node")'
这里有3个任务,它们都将每分钟运行一次(every: minute),并且每个任务都将在image section中定义的图像中执行脚本代码。
只要运行docker-compose up,就能看到它在工作。以下是Tasker回购的完整文档:
http://github.com/opsxcq/tasker
VonC的回答相当彻底。此外,我想补充一件对我有帮助的事情。如果您只想运行一个cron作业而不跟踪文件,那么您可能会想从cron命令中删除&& tail -f /var/log/cron.log。
然而,这将导致Docker容器在运行后不久退出,因为当cron命令完成时,Docker认为上一个命令已经退出,因此杀死了容器。这可以通过在前台通过cron -f运行cron来避免。
我想分享一些我发现更灵活的其他建议的典型的修改。我想用一个环境变量来启用更改cron时间,最后添加了一个额外的脚本,在我的entrypoint.sh中运行,但在调用cron -f之前
*updatecron.sh*
#!/bin/sh
#remove old cron files
rm -rf /etc/cron.*/*
#create a new formatted cron definition
echo "$crondef [appname] >/proc/1/fd/1 2>/proc/1/fd/2" >> /etc/cron.d/restart-cron
echo \ >> /etc/cron.d/restart-cron
chmod 0644 /etc/cron.d/restart-cron
crontab /etc/cron.d/restart-cron
这将删除任何现有的cron文件,使用crondef的ENV变量创建一个新的cronfile,然后加载它。
所以,我的问题也一样。修复方法是改变docker-compose.yml中的命令部分。
From
命令:crontab /etc/crontab && tail -f /etc/crontab
To
命令:crontab /etc/crontab
命令:tail -f /etc/crontab
问题在于命令之间的“&&”。删除后,一切都好了。