在 docker 中运行 cron python 作业

Posted

技术标签:

【中文标题】在 docker 中运行 cron python 作业【英文标题】:Running cron python jobs within docker 【发布时间】:2015-01-05 11:22:30 【问题描述】:

我想以分离模式在 docker 容器内运行 python cron 作业。我的设置如下:

我的python脚本是test.py

#!/usr/bin/env python
import datetime
print "Cron job has run at %s" %datetime.datetime.now()

我的 cron 文件是 my-crontab

* * * * * /test.py > /dev/console

我的 Dockerfile 是

FROM ubuntu:latest
RUN apt-get update && apt-get install -y software-properties-common python-software-properties && apt-get update

RUN apt-get install -y python cron
ADD my-crontab /
ADD test.py /
RUN chmod a+x test.py

RUN crontab /my-crontab
ENTRYPOINT cron -f

这种方法有哪些潜在问题?还有其他方法吗?它们的优缺点是什么?

【问题讨论】:

有趣的想法。不会想到这一点,虽然我经常使用 supervisord 在容器中运行 cron。 @seanmcl 我很想知道你是如何使用 supervisord 做到这一点的。谢谢! docs.docker.com/articles/using_supervisord 我有一个 docker 容器,它在执行 CRON 作业 python 脚本期间使用环境变量。这是我在另一篇 SO 帖子中提供的答案的链接,***.com/a/41938139/5090330 未来自己的注意事项;根据容器,确保 cron 服务实际上正在运行 service cron status -> service cron start 【参考方案1】:

尝试在 docker 容器中运行 cron 作业时遇到的几个问题是:

    docker 容器中的时间是 UTC 而非本地时间; docker 环境未传递给 cron; 正如 Thomas 所指出的,cron 日志记录还有很多不足之处,通过 docker 访问它需要基于 docker 的解决方案。

列表中有特定于 cron 的问题和特定于 docker 的问题,但无论如何都必须解决它们才能使 cron 正常工作。

为此,我目前对问题中提出的问题的工作解决方案如下:

创建一个 docker 卷,所有在 cron 下运行的脚本都将写入该卷:

# Dockerfile for test-logs

# BUILD-USING:        docker build -t test-logs .
# RUN-USING:          docker run  -d -v /t-logs --name t-logs test-logs
# INSPECT-USING:      docker run -t -i  --volumes-from t-logs ubuntu:latest /bin/bash

FROM stackbrew/busybox:latest

# Create logs volume
VOLUME /var/log

CMD  ["true"]

将在 cron 下运行的脚本是 test.py:

#!/usr/bin/env python

# python script which needs an environment variable and runs as a cron job
import datetime
import os

test_environ = os.environ["TEST_ENV"]
print "Cron job has run at %s with environment variable '%s'" %(datetime.datetime.now(), test_environ)

为了将环境变量传递给我想在 cron 下运行的脚本,请遵循 Thomas 的建议,并在 @987654326 中为每个需要 docker 环境变量的脚本(或脚本组)放置一个 crontab 片段@ 带有必须设置的占位符 XXXXXXX

# placed in /etc/cron.d 
# TEST_ENV is an docker environment variable that the script test.py need

TEST_ENV=XXXXXXX
#
* * * * * root python /test.py >> /var/log/test.log

不要直接调用 cron,而是将 cron 包装在一个 python 脚本中,该脚本执行以下操作: 1. 从 docker 环境变量中读取环境变量,并在 crontab 片段中设置环境变量。

#!/usr/bin/env python

# run-cron.py
# sets environment variable crontab fragments and runs cron

import os
from subprocess import call
import fileinput

# read docker environment variables and set them in the appropriate crontab fragment
environment_variable = os.environ["TEST_ENV"]

for line in fileinput.input("/etc/cron.d/cron-python",inplace=1):
    print line.replace("XXXXXXX", environment_variable)

args = ["cron","-f", "-L 15"]
call(args)

运行cron作业的容器的Dockerfile如下:

# BUILD-USING:        docker build -t test-cron .
# RUN-USING docker run --detach=true --volumes-from t-logs --name t-cron test-cron

FROM debian:wheezy
#
# Set correct environment variables.
ENV HOME /root
ENV TEST_ENV test-value

RUN apt-get update && apt-get install -y software-properties-common python-software-properties && apt-get update

# Install Python Setuptools
RUN apt-get install -y python cron

RUN apt-get purge -y python-software-properties software-properties-common && apt-get clean -y && apt-get autoclean -y && apt-get autoremove -y && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

ADD cron-python /etc/cron.d/
ADD test.py /
ADD run-cron.py /

RUN chmod a+x test.py run-cron.py

# Set the time zone to the local time zone
RUN echo "America/New_York" > /etc/timezone && dpkg-reconfigure --frontend noninteractive tzdata

CMD ["/run-cron.py"]

最后,创建容器并运行它们:

    创建日志卷(test-logs)容器:docker build -t test-logs . 运行日志卷:docker run -d -v /t-logs --name t-logs test-logs 创建 cron 容器:docker build -t test-cron . 运行 cron 容器:docker run --detach=true --volumes-from t-logs --name t-cron test-cron 检查在 cron 下运行的脚本的日志文件:docker run -t -i --volumes-from t-logs ubuntu:latest /bin/bash。日志文件位于/var/log

【讨论】:

TEST_ENV=test-value cron -f -L 15 不适用于该命令吗? 我把这段代码上传到github上,方便使用github.com/Alexis-benoist/docker-cron-example 为了让它工作,我在我的 Dockerfile RUN chmod 644 /etc/cron.d/cron-python 中要求这样做,因为“/etc/cron.d 中的文件必须由 root 拥有,并且不能是组可写或其他可写的。 " [来自'man cron']。在此之前,我的 cron-python 文件是组可写的。 解释得很好,特别是问题。但对于我的简单用法来说太复杂了,this answer 完成了这项工作【参考方案2】:

这是对 rosskw 答案的补充。

为了将环境变量传递给 cron 作业,无需在 crontab 文件中进行一些字符串替换。

在运行 contrainer 时将环境变量存储在一个文件中,然后在每次 cron 执行时从该文件中加载它们会更简单。我找到了提示here。

在dockerfile中:

CMD mkdir -p /data/log && env > /root/env.txt && crond -n

在 crontab 文件中:

* * * * * root env - `cat /root/env.txt` my-script.sh

【讨论】:

之后必须使用export $(cat /root/env-params | xargs) 来加载环境。然后它起作用了 这项技术对我有用。它也在这里解释:ypereirareis.github.io/blog/2016/02/29/…【参考方案3】:

/etc/cron.d/ 中添加crontab 片段而不是使用root 的crontab 可能更可取。

这会:

让您可以通过将其他 cron 作业添加到该文件夹​​来添加它们。 为您节省几层。 模拟 Debian 发行版如何为自己的软件包执行此操作。

请注意,这些文件的格式与 crontab 条目有点不同。这是 Debian php 包中的一个示例:

# /etc/cron.d/php5: crontab fragment for php5
#  This purges session files older than X, where X is defined in seconds
#  as the largest value of session.gc_maxlifetime from all your php.ini
#  files, or 24 minutes if not defined.  See /usr/lib/php5/maxlifetime

# Look for and purge old sessions every 30 minutes
09,39 *     * * *     root   [ -x /usr/lib/php5/maxlifetime ] && [ -x /usr/lib/php5/sessionclean ] && [ -d /var/lib/php5 ] && /usr/lib/php5/sessionclean /var/lib/php5 $(/usr/lib/php5/maxlifetime)

总体而言,根据经验,在容器中运行 cron 确实效果很好(除了 cron 日志记录还有很多不足之处)。

【讨论】:

【参考方案4】:

这是另一种解决方案。

Dockerfile

ADD docker/cron/my-cron /etc/cron.d/my-cron
RUN chmod 0644 /etc/cron.d/my-cron

ADD docker/cron/entrypoint.sh /etc/entrypoint.sh

ENTRYPOINT ["/bin/sh", "/etc/entrypoint.sh"]

entrypoint.sh

 #!/usr/bin/env bash
  printenv | cat - /etc/cron.d/my-cron > ~/my-cron.tmp \
    && mv ~/my-cron.tmp /etc/cron.d/my-cron

cron -f

【讨论】:

请务必添加-f!如果你不这样做,你会得到一些奇怪的行为——比如无法进入盒子。 (docker exec 可能不起作用)【参考方案5】:

我们正在使用以下解决方案。它支持docker logs 功能和将 cron 进程挂在 PID 1 上的容器中的能力(如果您使用上面提供的tail -f 解决方法 - 如果 cron 崩溃,docker 将不会遵循重启策略):

cron.sh:

#!/usr/bin/env bash

printenv | cat - /etc/cron.d/cron-jobs > ~/crontab.tmp \
    && mv ~/crontab.tmp /etc/cron.d/cron-jobs

chmod 644 /etc/cron.d/cron-jobs

tail -f /var/log/cron.log &

cron -f

Dockerfile:

RUN apt-get install --no-install-recommends -y -q cron 

ADD cron.sh /usr/bin/cron.sh
RUN chmod +x /usr/bin/cron.sh

ADD ./crontab /etc/cron.d/cron-jobs
RUN chmod 0644 /etc/cron.d/cron-jobs

RUN touch /var/log/cron.log

ENTRYPOINT ["/bin/sh", "/usr/bin/cron.sh"]

crontab:

* * * * * root <cmd> >> /var/log/cron.log 2>&1

请不要忘记在你的 crontab 中添加令人毛骨悚然的新行

【讨论】:

【参考方案6】:

不要将 crond 和您的基础映像混用。更喜欢为您的语言使用本机解决方案(如 Anton 所说的调度或 crython),或将其解耦。解耦我的意思是,把事情分开,所以你不必为了成为python和crond之间的融合而维护一个图像。

如果你想保持解耦,你可以使用Tasker,一个支持 cron(调度程序)的任务运行器来解决它。

这是一个docker-compose.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: helloFromPython
                tasks:
                    docker:
                        - name: helloFromPython
                          image: python:3-slim
                          script:
                              - python -c 'print("Hello world from python")'

只需运行docker-compose up,就可以看到它工作了。这是包含完整文档的 Tasker 存储库:

http://github.com/opsxcq/tasker

【讨论】:

【参考方案7】:

这是我在 docker 中调试 cron python 脚本的清单:

    确保在某处运行cron 命令。 Cron 不会自动启动。您可以使用 RUNCMD 从 Dockerfile 运行它,或者将其添加到容器的启动脚本中。如果您使用CMD,您可以考虑使用cron -f 标志,它将cron 保持在前台并且不会让容器死亡。不过,我更喜欢在日志文件上使用 tail -f。 将环境变量存储在 /etc/envoronment 中。从 bash startscript 运行它:printenv &gt; /etc/environment。如果您在 python 脚本中使用环境变量,这是绝对必须的。默认情况下,Cron 对环境变量一无所知。通过它可以从/etc/environment 读取它们。 使用以下配置测试 Cron:
* * * * * echo "Cron works" >>/home/code/test.log
* * * * * bash -c "/usr/local/bin/python3 /home/code/test.py >>/home/code/test.log 2>/home/code/test.log"

python 测试文件应该包含一些 print 语句或其他显示脚本正在运行的内容。 2&gt;/home/code/test.log 也会记录错误。否则,您将根本看不到错误并继续猜测。

完成后,转到容器,使用 docker exec -it &lt;container_name&gt; bash 并检查:

    该 crontab 配置已使用 crontab -l 使用tail -f /home/code/test.log 监控日志

我已经花费了数小时和数天来解决所有这些问题。我希望这可以帮助某人避免这种情况。

【讨论】:

【参考方案8】:

单容器方法

您可以在 doing something closely related 所在的同一容器中运行 crond,并使用能够很好地处理 PID 0 的基本映像,例如 phusion/baseimage。

专门的容器方法

可能更简洁的方法是将另一个容器链接到它,它只运行crond。例如:

Dockerfile

 FROM busybox
 ADD crontab /var/spool/cron/crontabs/www-data
 CMD crond -f

crontab

 * * * * * echo $USER

然后运行:

 $ docker build -t cron .
 $ docker run --rm --link something cron

注意:在这种情况下,它将以www-data 运行作业。不能只将crontab 文件挂载为卷,因为它需要由root 拥有,只有root 的写访问权限,否则crond 将不会运行。此外,您还必须将 crond 运行为 root

【讨论】:

【参考方案9】:

另一种可能性是使用Crython。 Crython 允许您从单个 python 脚本/进程中定期安排 python 函数。它甚至可以理解 cron 语法:

@crython.job(expr='0 0 0 * * 0 *')
def job():
    print "Hello world"

使用 crython 避免了在 docker 容器中运行 crond 的各种麻烦——您的工作现在是一个可以在需要时唤醒的单个进程,这更适合 docker 执行模型。但它的缺点是将调度放入您的程序中,这并不总是可取的。不过,在某些用例中它可能会很方便。

【讨论】:

这个库很烂。我无法让它工作。 pip 中的默认版本被严重破坏(每秒触发任何工作)。 expr 语法会欺骗你,因为它与 cron 不兼容。 我最终选择了 python schedule 包。 github.com/dbader/schedule @TimLudwinski :如何使 python 调度代码在 docker 容器内运行 - 我正在尝试执行 nohup python -u ./run-scheduler.py > cmd.log & - 但是当它退出时我注销了 我怀疑您的问题可能是您不应该将 nohup 与 docker 进程一起使用。这会将进程置于后台,当主前台进程退出时,docker 可能正在退出。

以上是关于在 docker 中运行 cron python 作业的主要内容,如果未能解决你的问题,请参考以下文章

如何在 docker 容器中运行 cron 作业

docker(ubuntu)中安装cron运行crontab定时任务

在不同主机上的 Docker 中运行 cron 的问题

Docker 并行运行 cron 作业

apache 特定的 cron-jobs 不应该在 docker 映像中运行吗?

在我将 cron 文件重新保存在 docker 容器中之前,Cron 作业不起作用