如何在Docker容器内运行定时任务(cron job)

61

我尝试在Docker容器内运行cron作业,但都无法正常工作。
我的容器只有cron.dailycron.weekly文件。
crontab、cron.d、cron.hourly在我的容器中不存在。
crontab -e 也不起作用。
我的容器使用的是/bin/bash


请查看我的答案这里 - x-yuri
5个回答

168

以下是我如何运行我的cron容器。

Dockerfile:

FROM alpine:3.3

ADD crontab.txt /crontab.txt
ADD script.sh /script.sh
COPY entry.sh /entry.sh
RUN chmod 755 /script.sh /entry.sh
RUN /usr/bin/crontab /crontab.txt

CMD ["/entry.sh"]

crontab.txt

*/30 * * * * /script.sh >> /var/log/script.log

entry.sh

#!/bin/sh

# start cron
/usr/sbin/crond -f -l 8

script.sh

#!/bin/sh

# code goes here.
echo "This is a script, run by cron!"

像这样构建
docker build -t mycron .

这样运行

docker run -d mycron

添加自己的脚本并编辑crontab.txt,然后构建镜像并运行。由于基于alpine,因此镜像非常小。


1
这很棒。我遇到的一个问题是,在运行docker run之后,我无法使用ctrl-c停止正在运行的容器?即使在关闭终端后,当我执行docker ps时,我仍然可以看到容器仍在运行。有人遇到同样的问题吗? - ztech
6
请在答案中添加 RUN apk add --update apk-cron && rm -rf /var/cache/apk/* 以提供完整示例。有很多种方法可以将 cron 添加到 Alpine 中,而这个方法是适用于您的示例的一种方法。 - Fmstrat
5
嗨,为什么有2个ADD和1个COPY?根据我阅读的内容,似乎3个COPY或3个ADD也可以(在这种情况下)正常工作。我对吗? - NamNamNam
7
“crond -f -l 8”的意思是什么?根据手册,日志级别使用大写的“L”。其中的数字8表示日志级别的等级。 - jcarlosweb
2
我在容器中默认情况下没有看到crond在运行。我必须执行并运行crond,这实际上触发了cron作业。这里有什么我错过的东西吗? - Avi
显示剩余7条评论

13

crondtiny 在 Alpine 上良好配合。

RUN apk add --no-cache tini

ENTRYPOINT ["/sbin/tini", "--"]
CMD ["/usr/sbin/crond", "-f"]

但不应作为容器的主进程(PID 1)运行,因为存在僵尸进程回收问题和信号处理问题。请参见此 Docker PR此博客文章获取详细信息。


2
为什么不应该作为PID1运行? - Oscar Fanelli

9

@ken-cochrane的解决方案可能是最好的,但也有一种方法可以在不需要创建额外文件的情况下完成。

不需要额外文件的方法:

做法是在entrypoint.sh文件中设置cron。

Dockerfile


...

# Your Dockerfile above


COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh


echo "* * * * * echo 'I love running my crons'" >> /etc/crontabs/root
crond -l 2 -f > /dev/stdout 2> /dev/stderr &

# You can put the rest of your entrypoint.sh below this line

...


如果您正在使用没有bash的alpine linux,请不要忘记在sh文件的开头添加以下内容:#!/bin/ash - Daniel Miranda

2
你并没有详细说明你做了什么,但一种方法是利用 dcron 和 Alpine Linux: docker-compose.yml:
services:
  dcron:
    build: .
    command: crond -fl info
    init: yes

Dockerfile:

FROM alpine:3.17
RUN set -x \
    && apk add --no-cache dcron shadow \
    && useradd -m app
COPY crontab /etc/crontabs/app

crontab:

* * * * * date >/dev/null 2>&1

我的gist中可以找到更多信息。

我在这里让它在非root用户下运行任务,因为你不应该给予比所需权限更高的权限(最小特权原则),即使是在docker容器内部也是如此。

这种解决方案的缺点是你无法在docker logs中看到任务的输出。这就是为什么我建议你尝试一下适用于dockercron实现,例如supercronic

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 .

crontab:

* * * * * date

您还可以查看我关于supercronic要点,以及我的其他回答,其中提供了使用其他cron实现的示例。


我想问一下@ChrisF关于我的被删除的答案,是否足够贴合问题。但显然我不能在这里提到他。如果你要删除这个答案,让我们先讨论什么是重复的,什么不是。或者给我一些沟通的方式,以便我可以改进我的答案。 - x-yuri

0

这里有一个关于Docker容器内cron问题的好解释:

Docker文件示例:

FROM alpine

# Copy script which should be run
COPY ./myawesomescript /usr/local/bin/myawesomescript
# Run the cron every minute
RUN echo '*  *  *  *  *    /usr/local/bin/myawesomescript' > /etc/crontabs/root

CMD ['crond', '-l 2', '-f']

就我而言,这种方法行不通。我尝试了几个变体,似乎在最终容器中对/etc/crontabs/root所做的更改并不存在。 - JesusIniesta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接