我正在尝试在调用shell脚本的docker容器中运行cronjob。
昨天我一直在网上搜索和堆栈溢出,但是我找不到真正可行的解决方案。
我怎样才能做到这一点?
编辑:
我已经创建了一个(带注释的)github存储库,上面有一个工作的docker cron容器,该容器以给定的间隔调用shell脚本。
我正在尝试在调用shell脚本的docker容器中运行cronjob。
昨天我一直在网上搜索和堆栈溢出,但是我找不到真正可行的解决方案。
我怎样才能做到这一点?
编辑:
我已经创建了一个(带注释的)github存储库,上面有一个工作的docker cron容器,该容器以给定的间隔调用shell脚本。
Answers:
您可以将crontab复制到映像中,以使从该映像启动的容器运行该作业。
请参阅“ 运行与码头工人cron作业从” 朱利安·布雷他Ekito/docker-cron
:
让我们创建一个名为“
hello-cron
” 的新文件来描述我们的工作。
* * * * * echo "Hello world" >> /var/log/cron.log 2>&1
# An empty line is required at the end of this file for a valid cron file.
以下Dockerfile描述了构建映像的所有步骤
FROM ubuntu:latest
MAINTAINER docker@ekito.fr
RUN apt-get update && apt-get -y install cron
# Copy hello-cron file to the cron.d directory
COPY hello-cron /etc/cron.d/hello-cron
# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/hello-cron
# Apply cron job
RUN crontab /etc/cron.d/hello-cron
# Create the log file to be able to run tail
RUN touch /var/log/cron.log
# Run the command on container startup
CMD cron && tail -f /var/log/cron.log
(请参阅Gaafar的评论和如何apt-get
降低安装噪音?:
apt-get -y install -qq --force-yes cron
也可以使用)
正如Nathan Lloyd在评论中指出的:
关于陷阱的快速说明:
如果要添加脚本文件并告诉cron运行它,请记住,如果您忘记了Cron会静默失败。
RUN chmod 0744 /the_script
或者,确保您的工作本身直接重定向到stdout / stderr而不是日志文件,如hugoShaka的答案所述:
* * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2
将最后一个Dockerfile行替换为
CMD ["cron", "-f"]
另请参阅(关于cron -f
,即cron“前景”)“ docker ubuntu cron -f
无法正常工作 ”
生成并运行它:
sudo docker build --rm -t ekito/cron-example .
sudo docker run -t -i ekito/cron-example
请耐心等待2分钟,您的命令行应显示:
Hello world
Hello world
请注意,
tail
如果在映像构建过程中创建了正确的文件,则可能无法显示正确的文件。
如果是这种情况,则需要在容器运行时创建或触摸文件,以便尾部拾取正确的文件。
请参阅“ 泊坞窗末尾的输出未显示tail -f
CMD
”。
-y
安装cron以避免
crontab -l
,我没有为root安装crontab,屏幕也保持空白。但是,当我检查'/etc/cron.d/'时,我看到crontab文件存在(甚至更令人惊讶),当我检查时/var/log/cron.log
,我看到脚本正在运行(文件内容附加了Hello World
)。我正在Dockerfile中提取此映像:FROM phusion/baseimage:0.10.0
。关于行为差异有什么想法吗?
在生产环境中采用的解决方案可能很危险。
在docker中,每个容器仅应执行一个进程,因为如果不执行,则不会监视派生到后台的进程,并且该进程可能会在不知情的情况下停止。
当您CMD cron && tail -f /var/log/cron.log
基本上使用cron流程进行分叉以便cron
在后台执行时,主流程会退出并让您tailf
在前台执行。后台cron进程可能会停止或失败,您不会注意到,您的容器仍将以静默方式运行,并且业务流程工具也不会重新启动它。
您可以通过直接的的cron的命令输出重定向到您的码头工人避免这样的事情
stdout
和stderr
分别位于中/proc/1/fd/1
和/proc/1/fd/2
。
使用基本的shell重定向,您可能需要执行以下操作:
* * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2
您的CMD为: CMD ["cron", "-f"]
cron -f
用于“ cron前景色”。我将您的答案包含在我的上面,以提高可见度。+1
对于那些想要使用简单轻巧的图像的人:
FROM alpine:3.6
# copy crontabs for root user
COPY config/cronjobs /etc/crontabs/root
# start crond with log level 8 in foreground, output to stderr
CMD ["crond", "-f", "-d", "8"]
其中cronjobs是包含您的cronjobs的文件,格式为:
* * * * * echo "hello stackoverflow" >> /test_file 2>&1
# remember to end this file with an empty new line
> /proc/1/fd/1 2> /proc/1/fd/2
重定向直接从docker日志访问cronjobs输出。
-d 8
参数的crond 不是标准的cron,它是busybox中的crond命令。例如,从ubuntu中,您可以将其运行为busybox crond -f -d 8
。对于较旧的版本,您必须使用-L /dev/stdout/
。
docker run -v ${PWD}/cronjobs:/etc/crontabs/root alpine:3.6 crond -f -d 8
。@Groostav,您可以在Docker Compose中使用类似的方法。
@VonC提出的建议很好,但我更喜欢在一行中完成所有cron作业配置。这样可以避免跨平台的问题,例如cronjob的位置,并且您不需要单独的cron文件。
FROM ubuntu:latest
# Install cron
RUN apt-get -y install cron
# Create the log file to be able to run tail
RUN touch /var/log/cron.log
# Setup cron job
RUN (crontab -l ; echo "* * * * * echo "Hello world" >> /var/log/cron.log") | crontab
# Run the command on container startup
CMD cron && tail -f /var/log/cron.log
运行Docker容器后,您可以通过以下方式确保cron服务是否正常运行:
# To check if the job is scheduled
docker exec -ti <your-container-id> bash -c "crontab -l"
# To check if the cron service is running
docker exec -ti <your-container-id> bash -c "pgrep cron"
如果您希望使用ENTRYPOINT而不是CMD,则可以将上面的CMD替换为
ENTRYPOINT cron start && tail -f /var/log/cron.log
RUN apt-get update && apt-get -y install cron
否则将无法找到包cron
RUN cat $APP_HOME/crons/* | crontab
像一个
cron
到入口点脚本似乎是最好的选择:ENTRYPOINT [“ entrypoint.sh”]
还有另一种方法可以使用Tasker,它是具有cron(调度程序)支持的任务运行程序。
为什么呢 有时要执行cron作业,您必须将基本图像(python,java,nodejs,ruby)与crond混合。这意味着要维护另一个图像。Tasker通过分离crond和您的容器来避免这种情况。您可以只关注要执行命令的图像,然后配置Tasker使其使用它。
这是一个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: hello
- every: minute
task: helloFromPython
- every: minute
task: helloFromNode
tasks:
docker:
- name: hello
image: debian:jessie
script:
- echo Hello world from Tasker
- name: helloFromPython
image: python:3-slim
script:
- python -c 'print("Hello world from python")'
- name: helloFromNode
image: node:8
script:
- node -e 'console.log("Hello from node")'
那里有3个任务,它们每分钟都会运行一次(every: minute
),并且每个任务都将script
在image
section中定义的图像内执行代码。
只需运行docker-compose up
,然后看它就可以工作。这是带有完整文档的Tasker存储库:
docker exec
在指定的容器上使用。
尽管这旨在通过Docker的容器在容器中的运行进程旁运行作业 exec
界面,但您可能对此很感兴趣。
我编写了一个守护程序,该守护程序可以观察容器并计划在容器中的元数据中定义的作业。例:
version: '2'
services:
wordpress:
image: wordpress
mysql:
image: mariadb
volumes:
- ./database_dumps:/dumps
labels:
deck-chores.dump.command: sh -c "mysqldump --all-databases > /dumps/dump-$$(date -Idate)"
deck-chores.dump.interval: daily
“经典”的,类似cron的配置也是可能的。
docker exec <container_name> <some_command>
按计划执行命令。
在其他主机上部署容器时,只需注意它不会自动启动任何进程。您需要确保“ cron”服务正在容器中运行。在我们的案例中,我将Supervisord与其他服务一起使用以启动cron服务。
[program:misc]
command=/etc/init.d/cron restart
user=root
autostart=true
autorestart=true
stderr_logfile=/var/log/misc-cron.err.log
stdout_logfile=/var/log/misc-cron.out.log
priority=998
在专用容器中定义cronjob,该容器通过docker exec对您的服务运行命令。
这具有更高的内聚性,正在运行的脚本将有权访问您为服务定义的环境变量。
#docker-compose.yml
version: "3.3"
services:
myservice:
environment:
MSG: i'm being cronjobbed, every minute!
image: alpine
container_name: myservice
command: tail -f /dev/null
cronjobber:
image: docker:edge
volumes:
- /var/run/docker.sock:/var/run/docker.sock
container_name: cronjobber
command: >
sh -c "
echo '* * * * * docker exec myservice printenv | grep MSG' > /etc/crontabs/root
&& crond -f"
myservice unknown
错误。
如果要在Windows上使用docker,请记住,如果打算将crontab文件从Windows导入到ubuntu容器,则必须将行尾格式从CRLF更改为LF(即从dos更改为unix)。如果没有,您的计划工作将无法进行。这是一个工作示例:
FROM ubuntu:latest
RUN apt-get update && apt-get -y install cron
RUN apt-get update && apt-get install -y dos2unix
# Add crontab file (from your windows host) to the cron directory
ADD cron/hello-cron /etc/cron.d/hello-cron
# Change line ending format to LF
RUN dos2unix /etc/cron.d/hello-cron
# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/hello-cron
# Apply cron job
RUN crontab /etc/cron.d/hello-cron
# Create the log file to be able to run tail
RUN touch /var/log/hello-cron.log
# Run the command on container startup
CMD cron && tail -f /var/log/hello-cron.log
实际上,这花了我几个小时才能弄清楚,因为在Docker容器中调试cron作业是一项繁琐的任务。希望它可以帮助其他无法使他们的代码正常工作的人!
从以上示例中,我创建了此组合:
在Nano中使用Crontab进行高山图像编辑(我讨厌vi)
FROM alpine
RUN apk update
RUN apk add curl nano
ENV EDITOR=/usr/bin/nano
# start crond with log level 8 in foreground, output to stderr
CMD ["crond", "-f", "-d", "8"]
# Shell Access
# docker exec -it <CONTAINERID> /bin/sh
# Example Cron Entry
# crontab -e
# * * * * * echo hello > /proc/1/fd/1 2>/proc/1/fd/2
# DATE/TIME WILL BE IN UTC
使用应该定期运行的作业创建一个脚本文件,例如run.sh。
#!/bin/bash
timestamp=`date +%Y/%m/%d-%H:%M:%S`
echo "System path is $PATH at $timestamp"
保存并退出。
如果在Docker容器化过程中有多个任务需要执行,请使用入口点文件将其全部运行。
入口点文件是一个脚本文件,在发出docker run命令时生效。因此,我们要运行的所有步骤都可以放在此脚本文件中。
例如,我们有2个作业要运行:
运行一次作业:回显“ Docker容器已启动”
运行定期作业:run.sh
#!/bin/bash
# Start the run once job.
echo "Docker container has been started"
# Setup a cron schedule
echo "* * * * * /run.sh >> /var/log/cron.log 2>&1
# This extra line makes it a valid cron" > scheduler.txt
crontab scheduler.txt
cron -f
让我们了解在文件中设置的crontab
* * * * *
:Cron时间表;该作业必须每分钟运行一次。您可以根据需要更新时间表。
/run.sh
:将定期运行的脚本文件的路径
/var/log/cron.log
:用于保存计划的cron作业的输出的文件名。
2>&1
:错误日志(如果有)也将重定向到上面使用的相同输出文件。
注意:不要忘记添加额外的新行,因为它使它成为有效的cron。
Scheduler.txt
:完整的cron设置将重定向到文件。
我实际的cron工作期望将大多数参数作为环境变量传递给docker run命令。但是,通过bash,我无法使用属于系统或docker容器的任何环境变量。
然后,将其作为解决此问题的解决方法:
declare -p | grep -Ev 'BASHOPTS|BASH_VERSINFO|EUID|PPID|SHELLOPTS|UID' > /container.env
SHELL=/bin/bash
BASH_ENV=/container.env
最后,你entrypoint.sh
应该看起来像
#!/bin/bash
# Start the run once job.
echo "Docker container has been started"
declare -p | grep -Ev 'BASHOPTS|BASH_VERSINFO|EUID|PPID|SHELLOPTS|UID' > /container.env
# Setup a cron schedule
echo "SHELL=/bin/bash
BASH_ENV=/container.env
* * * * * /run.sh >> /var/log/cron.log 2>&1
# This extra line makes it a valid cron" > scheduler.txt
crontab scheduler.txt
cron -f
FROM ubuntu:16.04
MAINTAINER Himanshu Gupta
# Install cron
RUN apt-get update && apt-get install -y cron
# Add files
ADD run.sh /run.sh
ADD entrypoint.sh /entrypoint.sh
RUN chmod +x /run.sh /entrypoint.sh
ENTRYPOINT /entrypoint.sh
而已。构建并运行Docker映像!
Cron作业存储在/ var / spool / cron / crontabs中(我知道的所有发行版中的公用位置)。顺便说一句,您可以使用以下方法在bash中创建cron选项卡:
crontab -l > cronexample
echo "00 09 * * 1-5 echo hello" >> cronexample
crontab cronexample
rm cronexample
这将使用cron任务创建一个临时文件,然后使用crontab对其进行编程。最后一行删除临时文件。
cron
守护进程通常不会在容器中运行。
crond
除了必须在容器中运行的服务之外,还必须运行,通常使用s6之类的服务管理器。可能会提出这样的问题以寻求适当的答案
当在一些限制了根访问权限的精简映像上运行时,我必须将用户添加到sudoers中并以 sudo cron
FROM node:8.6.0
RUN apt-get update && apt-get install -y cron sudo
COPY crontab /etc/cron.d/my-cron
RUN chmod 0644 /etc/cron.d/my-cron
RUN touch /var/log/cron.log
# Allow node user to start cron daemon with sudo
RUN echo 'node ALL=NOPASSWD: /usr/sbin/cron' >>/etc/sudoers
ENTRYPOINT sudo cron && tail -f /var/log/cron.log
也许对某人有帮助
到目前为止,我找到的最可靠的方法是运行一个独立的cron容器-安装docker客户端并绑定安装docker sock,以便您可以与主机上的docker服务器通信。
然后只需对每个cron作业使用env vars和一个入口点脚本来生成/ etc / crontab
这是我使用此原理创建的图像,并在过去3-4年中用于生产。
https://www.vip-consult.solutions/post/better-docker-cron#content
尝试使用发条级宝石安排任务。请遵循此链接中提供的步骤。
您可以如下所示在lib / clock.rb文件中调用rake任务。
every(1.day, 'Import large data from csv files', :at => '5:00') do |job|
`rake 'portal:import_data_from_csv'`
end
在docker-compose文件中创建一个单独的容器,并在容器内运行以下命令。
command: bundle exec clockwork lib/clock.rb
RUN apt-get update && apt-get install cron