我试图在调用shell脚本的docker容器内运行cronjob。

昨天我一直在网上搜索,堆栈溢出,但我真的找不到一个有效的解决方案。 我该怎么做呢?


当前回答

不幸的是,上面的答案都不适合我,尽管所有的答案都指向解决方案,并最终指向我的解决方案,如果它对某人有帮助,这里是一个片段。谢谢

这个问题可以用bash文件解决,由于Docker的分层架构,cron服务不会通过RUN/CMD/ENTRYPOINT命令启动。

只需添加一个bash文件,该文件将启动cron和其他服务(如果需要)

DockerFile

FROM gradle:6.5.1-jdk11 AS build
# apt
RUN apt-get update
RUN apt-get -y install cron
# Setup cron to run every minute to print (you can add/update your cron here)
RUN touch /var/log/cron-1.log
RUN (crontab -l ; echo "* * * * * echo testing cron.... >> /var/log/cron-1.log 2>&1") | crontab
# entrypoint.sh
RUN chmod +x entrypoint.sh
CMD ["bash","entrypoint.sh"]

entrypoint.sh

#!/bin/sh
service cron start & tail -f /var/log/cron-2.log

如果任何其他服务也需要与cron一起运行,那么在同一个命令中添加&,例如:/opt/wildfly/bin/standalone.sh & service cron start & tail -f /var/log/cron-2.log

一旦你进入docker容器,你可以看到测试cron....将每分钟打印在文件:/var/log/cron-1.log

但是,如果cron死亡,容器将继续运行。

其他回答

您可以将crontab复制到映像中,以便从映像中启动的容器运行作业。


重要提示:docker-cron问题3中提到:cron文件使用LF,而不是CRLF。


参见Julien Boulay在他的Ekito/ Docker -cron中的“Run a cron job with Docker”:

让我们创建一个名为“hello-cron”的新文件来描述我们的工作。

# must be ended with a new line "LF" (Unix) and not "CRLF" (Windows)
* * * * * 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.

如果你想知道什么是2>&1,Ayman Hourieh解释道。

下面的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

但是:如果cron死亡,容器将继续运行。

(见Gaafar的评论和我如何使apt-get安装噪音更小?: Apt-get -y install -qq——force-yes cron也可以工作)

正如Nathan Lloyd在评论中所指出的:

关于一个问题的简单说明: 如果您正在添加一个脚本文件并告诉cron运行它,请记住这样做 运行chmod 0744 /the_script 如果你忘记了,Cron会默默地失败。


或者,确保你的作业本身直接重定向到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

Eric在评论中补充道:

请注意,如果tail是在映像构建期间创建的,那么它可能不会显示正确的文件。 如果是这种情况,您需要在容器运行时创建或触摸该文件,以便tail获得正确的文件。

参见“docker CMD末尾的tail -f输出不显示”。


更多信息请参见Jason Kulatunga的“在Docker中运行Cron”(2021年4月),他在下面评论道

参见Jason的图片AnalogJ/docker-cron基于:

Dockerfile安装cronie/crond,取决于发行版。 一个入口点初始化/etc/environment,然后调用 Cron -f -l 2

我根据其他答案创建了一个Docker映像,可以像这样使用

docker run -v "/path/to/cron:/etc/cron.d/crontab" gaafar/cron

/path/to/cron: crontab文件的绝对路径,或者你可以在Dockerfile中使用它作为基础文件:

FROM gaafar/cron

# COPY crontab file in the cron directory
COPY crontab /etc/cron.d/crontab

# Add your commands here

作为参考,图片在这里。

我偶尔尝试寻找一个docker友好的cron实现。最后一次尝试,我找到了一对。

我所说的对docker友好的意思是,“任务的输出可以在docker日志中看到,而不是诉诸于技巧。”

目前我认为最有希望的是超级克隆。它可以提供一个crontab文件,同时对docker友好。利用它:

docker-compose.yml:

services:
  supercronic:
    build: .
    command: supercronic crontab

Dockerfile:

FROM alpine:3.17
RUN set -x \
    && apk add --no-cache supercronic shadow \
    && useradd -m app
USER app
COPY crontab .

定时任务:

* * * * * date

有更多信息的要点。

另一个不错的是yacron,但它使用YAML。

可以使用Ofelia,但它们似乎专注于在单独的容器中运行任务。这可能不是一个缺点,但我不确定为什么我想这么做。

还有一些传统的cron实现:dcron, fcron, cronie。但它们“没有简单的方法来查看任务的输出”。

我们的是一个作为cron作业运行的nodejs应用程序,它也依赖于环境变量。

下面的解决方案适用于我们。

码头工人文件:

# syntax=docker/dockerfile:1
FROM node:12.18.1
ENV NODE_ENV=production

COPY ["startup.sh", "./"]

# Removed steps to build the node js application

#--------------- Setup cron ------------------
# Install Cron
RUN apt-get update
RUN apt-get -y install cron
 
# Run every day at 1AM
#/proc/1/fd/1 2>/proc/1/fd/2 is used to redirect cron logs to standard output and standard error
RUN (crontab -l ; echo "0 1 * * * /usr/local/bin/node /app/dist/index.js  > /proc/1/fd/1 2>/proc/1/fd/2") | crontab
    
#--------------- Start Cron ------------------
# Grant execution rights
RUN chmod 755 startup.sh
CMD ["./startup.sh"]

startup.sh:

!/bin/bash
echo "Copying env variables to /etc/environment so that it is available for cron jobs"
printenv >> /etc/environment
echo "Starting cron"
cron -f

对于多个作业和各种依赖项(如zsh和curl),这是一种很好的方法,同时还结合了其他答案的最佳实践。额外的好处:这并不需要你在myScript.sh上设置+x个执行权限,这在新环境中很容易错过。

cron.dockerfile

FROM ubuntu:latest

# Install dependencies
RUN apt-get update && apt-get -y install \
  cron \
  zsh \
  curl;

# Setup multiple jobs with zsh and redirect outputs to docker logs
RUN (echo "\
* * * * * zsh -c 'echo "Hello World"' 1> /proc/1/fd/1 2>/proc/1/fd/2 \n\
* * * * * zsh /myScript.sh 1> /proc/1/fd/1 2>/proc/1/fd/2 \n\
") | crontab

# Run cron in forground, so docker knows the task is running
CMD ["cron", "-f"]

将此与docker compose集成,如下所示:

docker-compose.yml

services:
  cron:
    build:
      context: .
      dockerfile: ./cron.dockerfile
    volumes:
      - ./myScript.sh:/myScript.sh

请记住,当您更改cron的内容时,您需要docker编写构建cron。但对myScript.sh的更改将在compose中挂载时立即反映出来。