在docker中运行cron python作业

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

这种方法有什么潜在的问题? 有没有其他的方法,他们有什么优点和缺点?

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

  1. 在docker集装箱的时间是在UTC不是当地时间;
  2. docker环境不传递给cron;
  3. 正如Thomas指出的那样,cron日志logging留下了许多不足之处,通过docker访问它需要一个基于docker的解决scheme。

有cron特定的问题,在列表中是docker特定的问题,但无论如何,他们必须解决,以获得cron工作。

为此,我现在对这个问题的解决办法如下:

创build一个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) 

为了将环境variables传递给我想要在cron下运行的脚本,请遵循Thomas的build议,并为需要/etc/cron.d中的docker环境variables的每个脚本(或一组脚本)添加一个crontab片段/etc/cron.d ,必须设置占位符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环境variables中读取环境variables,并在crontab片段中设置环境variables。

 #!/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"] 

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

  1. 创build日志卷(testing日志)容器: docker build -t test-logs .
  2. 运行日志卷: docker run -d -v /t-logs --name t-logs test-logs
  3. 创buildcron容器: 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

这是对rosksw答案的补充。

为了将环境variables传递给cron作业,不需要在crontab文件中进行一些stringreplace。

在运行约束程序时,将环境variables存储在文件中更简单,然后在每个cron执行时从这个文件加载它们。 我在这里find了提示。

在dockerfile中:

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

在crontab文件中:

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

/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日志还有很多不足之处)。

这是一个替代解决scheme。

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 

单容器方法

你可以在同一个容器中运行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将不会运行。 你也必须以root身份运行crond

我们使用下面的解决scheme。 它支持docker logsfunction和在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中添加令人毛骨悚然的新行

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

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

使用crython可以避免在Docker容器中运行crond的各种令人头疼的事情 – 您的工作现在是一个当需要时唤醒的单个进程,这更符合docker执行模型。 但是它有把时间安排在你的程序中的缺点,这并不总是可取的。 不过,在某些使用情况下,它可能会很方便。

不要混合crond和你的基本形象。 倾向于使用您的语言的本地解决scheme(安东说,安排或冷却),或解耦它。 通过解耦,我的意思是说,保持事物分离,所以你不必保持一个图像只是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 ,并看到它的工作。 这是塔斯克回购与全面的文件:

http://github.com/opsxcq/tasker