我试图在调用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

其他回答

您可以将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'本身请求UID 0。 请求根访问(例如通过sudo)是违反docker最佳实践的。 我使用https://github.com/gjcarneiro/yacron来管理计划任务。

VonC的回答相当彻底。此外,我想补充一件对我有帮助的事情。如果您只想运行一个cron作业而不跟踪文件,那么您可能会想从cron命令中删除&& tail -f /var/log/cron.log。

然而,这将导致Docker容器在运行后不久退出,因为当cron命令完成时,Docker认为上一个命令已经退出,因此杀死了容器。这可以通过在前台通过cron -f运行cron来避免。

我们的是一个作为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