在docker中运行cron python作业

时间:2014-11-08 21:04:02

标签: python cron docker

我想在分离模式下在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

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

9 个答案:

答案 0 :(得分:33)

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

  1. docker容器中的时间是UTC而不是本地时间;
  2. docker环境未传递给cron;
  3. 正如Thomas所说,cron日志记录有很多不足之处,通过docker访问它需要基于docker的解决方案。
  4. 列表中存在特定于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'建议并为/etc/cron.d中需要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,而是在执行操作的python脚本中包装cron: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"]
    

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

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

答案 1 :(得分:15)

以下是对rosksw答案的补充。

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

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

在dockerfile中:

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

在crontab文件中:

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

答案 2 :(得分:8)

/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日志记录之外还有很多需要)。

答案 3 :(得分:5)

这是另一种解决方案。

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

答案 4 :(得分:3)

我们正在使用以下解决方案。它支持docker logs功能和在PID 1上的容器中挂起cron进程的能力(如果使用上面提供的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中添加令人毛骨悚然的新行

答案 5 :(得分:2)

单一容器方法

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

专业化容器方法

可能更干净的是将另一个与其链接的Container运行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运行。

答案 6 :(得分:2)

不要混合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 repo的完整文档:

http://github.com/opsxcq/tasker

答案 7 :(得分:1)

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

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

使用crython避免了在docker容器中运行crond的各种麻烦 - 你的工作现在是一个单独的进程,在需要时唤醒,这更适合docker执行模型。但是它将调度安排在你的程序中是不利的,这并不总是令人满意的。不过,在某些用例中它可能很方便。

答案 8 :(得分:0)

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

  1. 确保在某处运行cron命令。 Cron不会自动启动。您可以使用RUNCMD从Dockerfile运行它,或将其添加到容器的启动脚本中。如果您使用CMD,则可以考虑使用cron -f标志,它将cron置于前台,并且不会让容器死亡。但是,我更喜欢在日志文件上使用tail -f
  2. 将环境变量存储在/ etc / envoronment中。从bash起始脚本printenv > /etc/environment运行此命令。如果在python脚本中使用环境变量,这是绝对必须的。默认情况下,Cron对环境变量一无所知。通过它可以从/etc/environment中读取它们。
  3. 使用以下配置测试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>/home/code/test.log还将记录错误。否则,您将根本看不到错误,并且会继续猜测。

完成后,使用docker exec -it <container_name> bash转到容器并检查:

  1. 使用crontab -l到位了crontab配置
  2. 使用tail -f /home/code/test.log
  3. 监视日志

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