我试图在调用shell脚本的docker容器内运行cronjob。
昨天我一直在网上搜索,堆栈溢出,但我真的找不到一个有效的解决方案。 我该怎么做呢?
我试图在调用shell脚本的docker容器内运行cronjob。
昨天我一直在网上搜索,堆栈溢出,但我真的找不到一个有效的解决方案。 我该怎么做呢?
您可以将crontab复制到映像中,以便从映像中启动的容器运行作业。
重要提示:docker-cron问题3中提到:cron文件使用LF,而不是CRLF。
参见Julien Boulay在他的Ekito/ Docker -cron中的“Run a cron job with Docker”:
让我们创建一个名为“hello-cron”的新文件来描述我们的工作。
# must be ended with a new line "LF" (Unix) and not "CRLF" (Windows)
* * * * * echo "Hello world" >> /var/log/cron.log 2>&1
# An empty line is required at the end of this file for a valid cron file.
如果你想知道什么是2>&1,Ayman Hourieh解释道。
下面的Dockerfile描述了构建映像的所有步骤
FROM ubuntu:latest
MAINTAINER docker@ekito.fr
RUN apt-get update && apt-get -y install cron
# Copy hello-cron file to the cron.d directory
COPY hello-cron /etc/cron.d/hello-cron
# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/hello-cron
# Apply cron job
RUN crontab /etc/cron.d/hello-cron
# Create the log file to be able to run tail
RUN touch /var/log/cron.log
# Run the command on container startup
CMD cron && tail -f /var/log/cron.log
但是:如果cron死亡,容器将继续运行。
(见Gaafar的评论和我如何使apt-get安装噪音更小?: Apt-get -y install -qq——force-yes cron也可以工作)
正如Nathan Lloyd在评论中所指出的:
关于一个问题的简单说明: 如果您正在添加一个脚本文件并告诉cron运行它,请记住这样做 运行chmod 0744 /the_script 如果你忘记了,Cron会默默地失败。
或者,确保你的作业本身直接重定向到stdout/stderr,而不是一个日志文件,如hugoShaka的回答所述:
* * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2
将最后一行Dockerfile替换为
CMD ["cron", "-f"]
但是:如果你想以非根用户的身份运行任务,它就不起作用了。
参见(关于cron -f,这是说cron“前景”)“docker ubuntu cron -f不能正常工作”
构建并运行它:
sudo docker build --rm -t ekito/cron-example .
sudo docker run -t -i ekito/cron-example
耐心等待2分钟,你的命令行应该显示:
Hello world
Hello world
Eric在评论中补充道:
请注意,如果tail是在映像构建期间创建的,那么它可能不会显示正确的文件。 如果是这种情况,您需要在容器运行时创建或触摸该文件,以便tail获得正确的文件。
参见“docker CMD末尾的tail -f输出不显示”。
更多信息请参见Jason Kulatunga的“在Docker中运行Cron”(2021年4月),他在下面评论道
参见Jason的图片AnalogJ/docker-cron基于:
Dockerfile安装cronie/crond,取决于发行版。 一个入口点初始化/etc/environment,然后调用 Cron -f -l 2
我根据其他答案创建了一个Docker映像,可以像这样使用
docker run -v "/path/to/cron:/etc/cron.d/crontab" gaafar/cron
/path/to/cron: crontab文件的绝对路径,或者你可以在Dockerfile中使用它作为基础文件:
FROM gaafar/cron
# COPY crontab file in the cron directory
COPY crontab /etc/cron.d/crontab
# Add your commands here
作为参考,图片在这里。
当您将容器部署到另一个主机上时,请注意它不会自动启动任何进程。你需要确保'cron'服务在你的容器中运行。 在我们的例子中,我使用了监工和其他服务来启动cron服务。
[program:misc]
command=/etc/init.d/cron restart
user=root
autostart=true
autorestart=true
stderr_logfile=/var/log/misc-cron.err.log
stdout_logfile=/var/log/misc-cron.out.log
priority=998
虽然这旨在通过Docker的exec接口在容器中运行进程的旁边运行作业,但您可能会感兴趣。
我已经编写了一个守护进程,用于观察容器并调度在其元数据中定义的作业。例子:
version: '2'
services:
wordpress:
image: wordpress
mysql:
image: mariadb
volumes:
- ./database_dumps:/dumps
labels:
deck-chores.dump.command: sh -c "mysqldump --all-databases > /dumps/dump-$$(date -Idate)"
deck-chores.dump.interval: daily
“经典”,cron一样的配置也是可能的。
这是文档,这是图像存储库。
VonC的回答相当彻底。此外,我想补充一件对我有帮助的事情。如果您只想运行一个cron作业而不跟踪文件,那么您可能会想从cron命令中删除&& tail -f /var/log/cron.log。
然而,这将导致Docker容器在运行后不久退出,因为当cron命令完成时,Docker认为上一个命令已经退出,因此杀死了容器。这可以通过在前台通过cron -f运行cron来避免。
@VonC的建议很好,但我更喜欢在一行中完成所有cron作业配置。这将避免像cronjob位置这样的跨平台问题,并且您不需要单独的cron文件。
FROM ubuntu:latest
# Install cron
RUN apt-get -y install cron
# Create the log file to be able to run tail
RUN touch /var/log/cron.log
# Setup cron job
RUN (crontab -l ; echo "* * * * * echo "Hello world" >> /var/log/cron.log") | crontab
# Run the command on container startup
CMD cron && tail -f /var/log/cron.log
运行docker容器后,你可以通过以下方式确定cron服务是否在工作:
# To check if the job is scheduled
docker exec -ti <your-container-id> bash -c "crontab -l"
# To check if the cron service is running
docker exec -ti <your-container-id> bash -c "pgrep cron"
如果你喜欢用ENTRYPOINT代替CMD,那么你可以用
ENTRYPOINT cron start && tail -f /var/log/cron.log
但是:如果cron死亡,容器将继续运行。
在生产环境中,公认的答案可能是危险的。
在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"]
但是:如果你想以非根用户的身份运行任务,这就行不通了。
还有另一种方法,就是使用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
当在一些限制根访问的经过修剪的映像上运行时,我必须将我的用户添加到sudoers并作为sudo cron运行
FROM node:8.6.0
RUN apt-get update && apt-get install -y cron sudo
COPY crontab /etc/cron.d/my-cron
RUN chmod 0644 /etc/cron.d/my-cron
RUN touch /var/log/cron.log
# Allow node user to start cron daemon with sudo
RUN echo 'node ALL=NOPASSWD: /usr/sbin/cron' >>/etc/sudoers
ENTRYPOINT sudo cron && tail -f /var/log/cron.log
也许这对某人有帮助
但是:如果cron死亡,容器将继续运行。
对于那些想要使用简单和轻量级图像的人:
FROM alpine:3.6
# copy crontabs for root user
COPY config/cronjobs /etc/crontabs/root
# start crond with log level 8 in foreground, output to stderr
CMD ["crond", "-f", "-d", "8"]
其中cronjobs是包含cronjobs的文件,格式如下:
* * * * * echo "hello stackoverflow" >> /test_file 2>&1
# remember to end this file with an empty new line
但显然你不会在docker日志中看到hello stackoverflow。
在专用容器中定义cronjob,该容器通过docker exec向服务运行命令。
这是更高的内聚性,运行脚本将能够访问为服务定义的环境变量。
#docker-compose.yml
version: "3.3"
services:
myservice:
environment:
MSG: i'm being cronjobbed, every minute!
image: alpine
container_name: myservice
command: tail -f /dev/null
cronjobber:
image: docker:edge
volumes:
- /var/run/docker.sock:/var/run/docker.sock
container_name: cronjobber
command: >
sh -c "
echo '* * * * * docker exec myservice printenv | grep MSG' > /etc/crontabs/root
&& crond -f"
所以,我的问题也一样。修复方法是改变docker-compose.yml中的命令部分。
From
命令:crontab /etc/crontab && tail -f /etc/crontab
To
命令:crontab /etc/crontab
命令:tail -f /etc/crontab
问题在于命令之间的“&&”。删除后,一切都好了。
如果你在windows上使用docker,请记住,如果你打算将crontab文件从windows导入到ubuntu容器中,你必须将行结束格式从CRLF更改为LF(即从dos更改为unix)。如果不是,你的工作就不会起作用。下面是一个工作示例:
FROM ubuntu:latest
RUN apt-get update && apt-get -y install cron
RUN apt-get update && apt-get install -y dos2unix
# Add crontab file (from your windows host) to the cron directory
ADD cron/hello-cron /etc/cron.d/hello-cron
# Change line ending format to LF
RUN dos2unix /etc/cron.d/hello-cron
# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/hello-cron
# Apply cron job
RUN crontab /etc/cron.d/hello-cron
# Create the log file to be able to run tail
RUN touch /var/log/hello-cron.log
# Run the command on container startup
CMD cron && tail -f /var/log/hello-cron.log
这实际上花了我几个小时才弄清楚,因为在docker容器中调试cron作业是一项乏味的任务。希望它能帮助那些不能让他们的代码工作的人!
但是:如果cron死亡,容器将继续运行。
从上面的例子中,我创建了这样的组合:
在Nano中使用Crontab编辑高山图像(我讨厌vi)
FROM alpine
RUN apk update
RUN apk add curl nano
ENV EDITOR=/usr/bin/nano
# start crond with log level 8 in foreground, output to stderr
CMD ["crond", "-f", "-d", "8"]
# Shell Access
# docker exec -it <CONTAINERID> /bin/sh
# Example Cron Entry
# crontab -e
# * * * * * echo hello > /proc/1/fd/1 2>/proc/1/fd/2
# DATE/TIME WILL BE IN UTC
与一次性作业并行设置cron
创建一个脚本文件,例如run.sh,其中包含应该定期运行的作业。
#!/bin/bash
timestamp=`date +%Y/%m/%d-%H:%M:%S`
echo "System path is $PATH at $timestamp"
保存并退出。
使用入口点而不是CMD
如果在docker容器化过程中有多个作业要启动,使用入口点文件来运行它们。
入口点文件是一个脚本文件,在发出docker run命令时起作用。因此,我们想要运行的所有步骤都可以放在这个脚本文件中。
例如,我们有两个作业要运行:
运行一次job: echo " Docker容器已启动"
运行定时任务:Run .sh
创建entrypoint.sh
#!/bin/bash
# Start the run once job.
echo "Docker container has been started"
# Setup a cron schedule
echo "* * * * * /run.sh >> /var/log/cron.log 2>&1
# This extra line makes it a valid cron" > scheduler.txt
crontab scheduler.txt
cron -f
让我们了解一下在文件中设置的crontab
* * * * *: Cron调度;该工作必须每分钟运行一次。您可以根据自己的需求更新时间表。
/run.sh:要定时运行的脚本文件的路径
/var/log/cron.log:保存定时cron作业输出的文件名。
2>&1:错误日志(如果有)也将被重定向到上面使用的相同输出文件。
注意:不要忘记添加额外的新行,因为这会使它成为一个有效的cron。 txt:完整的cron设置将被重定向到一个文件。
在cron中使用系统/用户特定的环境变量
我实际的cron作业期望将大多数参数作为环境变量传递给docker run命令。但是,使用bash时,我不能使用属于系统或docker容器的任何环境变量。
然后,这个问题就出现了:
在entrypoint.sh中添加以下行
declare -p | grep -Ev 'BASHOPTS|BASH_VERSINFO|EUID|PPID|SHELLOPTS|UID' > /container.env
更新cron设置并指定-
SHELL=/bin/bash
BASH_ENV=/container.env
最后,你的entrypoint.sh应该是这样的
#!/bin/bash
# Start the run once job.
echo "Docker container has been started"
declare -p | grep -Ev 'BASHOPTS|BASH_VERSINFO|EUID|PPID|SHELLOPTS|UID' > /container.env
# Setup a cron schedule
echo "SHELL=/bin/bash
BASH_ENV=/container.env
* * * * * /run.sh >> /var/log/cron.log 2>&1
# This extra line makes it a valid cron" > scheduler.txt
crontab scheduler.txt
cron -f
最后但并非最不重要的:创建Dockerfile
FROM ubuntu:16.04
MAINTAINER Himanshu Gupta
# Install cron
RUN apt-get update && apt-get install -y cron
# Add files
ADD run.sh /run.sh
ADD entrypoint.sh /entrypoint.sh
RUN chmod +x /run.sh /entrypoint.sh
ENTRYPOINT /entrypoint.sh
就是这样。构建并运行Docker映像!
下面是我的基于docker-compose的解决方案:
cron:
image: alpine:3.10
command: crond -f -d 8
depends_on:
- servicename
volumes:
- './conf/cron:/etc/crontabs/root:z'
restart: unless-stopped
带有cron条目的行在./conf/cron文件中。
注意:这将不会运行不在alpine映像中的命令。
此外,任务的输出显然不会出现在docker日志中。
这一行帮助我运行了预先计划好的任务。
ADD mycron/root /etc/cron.d/root
RUN chmod 0644 /etc/cron.d/root
RUN crontab /etc/cron.d/root
RUN touch /var/log/cron.log
CMD ( cron -f -l 8 & ) && apache2-foreground # <-- run cron
我的项目运行在:FROM php:7.2-apache
但是:如果cron死亡,容器将继续运行。
不幸的是,上面的答案都不适合我,尽管所有的答案都指向解决方案,并最终指向我的解决方案,如果它对某人有帮助,这里是一个片段。谢谢
这个问题可以用bash文件解决,由于Docker的分层架构,cron服务不会通过RUN/CMD/ENTRYPOINT命令启动。
只需添加一个bash文件,该文件将启动cron和其他服务(如果需要)
DockerFile
FROM gradle:6.5.1-jdk11 AS build
# apt
RUN apt-get update
RUN apt-get -y install cron
# Setup cron to run every minute to print (you can add/update your cron here)
RUN touch /var/log/cron-1.log
RUN (crontab -l ; echo "* * * * * echo testing cron.... >> /var/log/cron-1.log 2>&1") | crontab
# entrypoint.sh
RUN chmod +x entrypoint.sh
CMD ["bash","entrypoint.sh"]
entrypoint.sh
#!/bin/sh
service cron start & tail -f /var/log/cron-2.log
如果任何其他服务也需要与cron一起运行,那么在同一个命令中添加&,例如:/opt/wildfly/bin/standalone.sh & service cron start & tail -f /var/log/cron-2.log
一旦你进入docker容器,你可以看到测试cron....将每分钟打印在文件:/var/log/cron-1.log
但是,如果cron死亡,容器将继续运行。
我决定使用busybox,因为它是最小的图像之一。
crond在前台(-f)执行,日志发送到stderr (-d),我没有选择更改日志级别。 Crontab文件拷贝到默认路径:/var/spool/ Crontab
FROM busybox:1.33.1
# Usage: crond [-fbS] [-l N] [-d N] [-L LOGFILE] [-c DIR]
#
# -f Foreground
# -b Background (default)
# -S Log to syslog (default)
# -l N Set log level. Most verbose 0, default 8
# -d N Set log level, log to stderr
# -L FILE Log to FILE
# -c DIR Cron dir. Default:/var/spool/cron/crontabs
COPY crontab /var/spool/cron/crontabs/root
CMD [ "crond", "-f", "-d" ]
但是任务的输出显然不能在docker日志中看到。
这个问题有很多答案,但有些很复杂,另一些有一些缺点。我试着解释问题并提出解决方案。
cron-entrypoint.sh:
#!/bin/bash
# copy machine environment variables to cron environment
printenv | cat - /etc/crontab > temp && mv temp /etc/crontab
## validate cron file
crontab /etc/crontab
# cron service with SIGTERM support
service cron start
trap "service cron stop; exit" SIGINT SIGTERM
# just dump your logs to std output
tail -f \
/app/storage/logs/laravel.log \
/var/log/cron.log \
& wait $!
解决问题
环境变量在cron环境中不可用(如env vars或kubernetes secrets) 当crontab文件无效时停止 当机器接收到SIGTERM信号时,优雅地停止cron作业
作为上下文,我在Kubernetes上使用Laravel应用程序使用之前的脚本。
专注于在接收到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优雅地完成,但仍然在超时后终止它们。
只要添加到答案列表中,你也可以使用这张图片: https://hub.docker.com/repository/docker/cronit/simple-cron
并使用它作为启动cron作业的基础,像这样使用它:
FROM cronit/simple-cron # Inherit from the base image
#Set up all your dependencies
COPY jobs.cron ./ # Copy your local config
显然,可以在容器中(在根用户下)与其他进程一起运行cron,使用Dockerfile中的ENTRYPOINT语句和start.sh脚本,其中包括行进程cron start。更多信息请点击这里
#!/bin/bash
# copy environment variables for local use
env >> etc/environment
# start cron service
service cron start
# start other service
service other start
#...
如果映像不包含任何守护进程(因此只有短时间运行的脚本或进程),还可以考虑从外部启动cron,只需用cron信息定义一个LABEL,再加上调度器本身。这样,您的默认容器状态是“exited”。如果您有多个脚本,这可能会比拥有多个并行运行的cron实例减少系统占用空间。
参见:https://github.com/JaciBrunning/docker-cron-label
示例docker-compose.yaml:
version: '3.8'
# Example application of the cron image
services:
cron:
image: jaci/cron-label:latest
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "/etc/localtime:/etc/localtime:ro"
hello:
image: hello-world
restart: "no"
labels:
- "cron.schedule=* * * * * "
我想分享一些我发现更灵活的其他建议的典型的修改。我想用一个环境变量来启用更改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,然后加载它。
我们的是一个作为cron作业运行的nodejs应用程序,它也依赖于环境变量。
下面的解决方案适用于我们。
码头工人文件:
# syntax=docker/dockerfile:1
FROM node:12.18.1
ENV NODE_ENV=production
COPY ["startup.sh", "./"]
# Removed steps to build the node js application
#--------------- Setup cron ------------------
# Install Cron
RUN apt-get update
RUN apt-get -y install cron
# Run every day at 1AM
#/proc/1/fd/1 2>/proc/1/fd/2 is used to redirect cron logs to standard output and standard error
RUN (crontab -l ; echo "0 1 * * * /usr/local/bin/node /app/dist/index.js > /proc/1/fd/1 2>/proc/1/fd/2") | crontab
#--------------- Start Cron ------------------
# Grant execution rights
RUN chmod 755 startup.sh
CMD ["./startup.sh"]
startup.sh:
!/bin/bash
echo "Copying env variables to /etc/environment so that it is available for cron jobs"
printenv >> /etc/environment
echo "Starting cron"
cron -f
对于多个作业和各种依赖项(如zsh和curl),这是一种很好的方法,同时还结合了其他答案的最佳实践。额外的好处:这并不需要你在myScript.sh上设置+x个执行权限,这在新环境中很容易错过。
cron.dockerfile
FROM ubuntu:latest
# Install dependencies
RUN apt-get update && apt-get -y install \
cron \
zsh \
curl;
# Setup multiple jobs with zsh and redirect outputs to docker logs
RUN (echo "\
* * * * * zsh -c 'echo "Hello World"' 1> /proc/1/fd/1 2>/proc/1/fd/2 \n\
* * * * * zsh /myScript.sh 1> /proc/1/fd/1 2>/proc/1/fd/2 \n\
") | crontab
# Run cron in forground, so docker knows the task is running
CMD ["cron", "-f"]
将此与docker compose集成,如下所示:
docker-compose.yml
services:
cron:
build:
context: .
dockerfile: ./cron.dockerfile
volumes:
- ./myScript.sh:/myScript.sh
请记住,当您更改cron的内容时,您需要docker编写构建cron。但对myScript.sh的更改将在compose中挂载时立即反映出来。
所有的答案都需要容器内的根访问权限,因为'cron'本身请求UID 0。 请求根访问(例如通过sudo)是违反docker最佳实践的。 我使用https://github.com/gjcarneiro/yacron来管理计划任务。
我偶尔尝试寻找一个docker友好的cron实现。最后一次尝试,我找到了一对。
我所说的对docker友好的意思是,“任务的输出可以在docker日志中看到,而不是诉诸于技巧。”
目前我认为最有希望的是超级克隆。它可以提供一个crontab文件,同时对docker友好。利用它:
docker-compose.yml:
services:
supercronic:
build: .
command: supercronic crontab
Dockerfile:
FROM alpine:3.17
RUN set -x \
&& apk add --no-cache supercronic shadow \
&& useradd -m app
USER app
COPY crontab .
定时任务:
* * * * * date
有更多信息的要点。
另一个不错的是yacron,但它使用YAML。
可以使用Ofelia,但它们似乎专注于在单独的容器中运行任务。这可能不是一个缺点,但我不确定为什么我想这么做。
还有一些传统的cron实现:dcron, fcron, cronie。但它们“没有简单的方法来查看任务的输出”。