在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作业时遇到的几个问题是:
- 在docker集装箱的时间是在UTC不是当地时间;
- docker环境不传递给cron;
- 正如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容器并运行它们:
- 创build日志卷(testing日志)容器:
docker build -t test-logs .
- 运行日志卷:
docker run -d -v /t-logs --name t-logs test-logs
- 创buildcron容器:
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
。
这是对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 logs
function和在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
,并看到它的工作。 这是塔斯克回购与全面的文件: