ChatGPT解决这个技术问题 Extra ChatGPT

如何在 docker 容器中运行 cron 作业?

我正在尝试在调用 shell 脚本的 docker 容器中运行 cronjob。

昨天我一直在网上搜索和堆栈溢出,但我真的找不到有效的解决方案。我怎样才能做到这一点?


V
VonC

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

请参阅他的 Ekito/docker-cron 中来自 Julien Boulay 的“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 explains

以下 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

(参见 GaafarcommentHow do I make apt-get install less noisy?
apt-get -y install -qq --force-yes cron 也可以)

the comments 中的 Nathan Lloyd 所述:

关于一个陷阱的快速说明:如果您要添加一个脚本文件并告诉 cron 运行它,请记住 RUN chmod 0744 /the_script 如果您忘记了,Cron 会静默失败。

或者,确保您的作业本身直接重定向到 stdout/stderr 而不是日志文件,如 hugoShakaanswer 中所述:

 * * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2

将最后 Dockerfile 行替换为

CMD ["cron", "-f"]

另请参见(关于 cron -f,即 cron “前景”)“docker ubuntu cron -f is not working

构建并运行它:

sudo docker build --rm -t ekito/cron-example .
sudo docker run -t -i ekito/cron-example

请耐心等待 2 分钟,您的命令行应该会显示:

Hello world
Hello world

Eric 添加 in the comments

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

请参阅“Output of tail -f at the end of a docker CMD is not showing”。

Jason Kulatunga 的“Running Cron in Docker”(2021 年 4 月)中查看更多信息,因为他commented below

查看 Jason 的图片 AnalogJ/docker-cron,基于:

Dockerfile 安装 cronie/crond,取决于发行版。

初始化 /etc/environment 然后调用 cron -f -l 2 的入口点


您可能应该将 -y 添加到安装 cron 以避免 docker build 退出
这个解决方案仍然有效吗?当我遵循给定的指导方针时,当我以 root 身份登录容器并输入 crontab -l 时,我得到 No crontab installed for root,而且我的屏幕仍然是空白的。但是,当我检查“/etc/cron.d/”时,我看到 crontab 字段在那里(更令人惊讶的是),当我检查 /var/log/cron.log 时,我看到脚本正在运行(正在附加文件内容与Hello World)。我在我的 Dockerfile 中提取这个图像:FROM phusion/baseimage:0.10.0。关于行为差异的任何想法?
自 2018 年起,这种方法不再有效;有没有人能够让他们的 cronjob 使用 Ubuntu 作为基础映像?我对开箱即用的 cron 附带的 Alpine 映像不感兴趣
关于一个陷阱的快速说明:如果您要添加一个脚本文件并告诉 cron 运行它,请记住 RUN chmod 0744 /the_script。如果你忘记了,Cron 会默默地失败。
我写了一篇博文,将这个建议(以及我在 docker 中运行 cron 时发现的其他问题)实施到多个发行版(ubuntu、alpine、centos)的工作 docker 映像中:blog.thesparktree.com/cron-in-docker
E
Evgeniy Berezovsky

接受的答案在生产环境中可能是危险的。

在 docker 中,每个容器应该只执行一个进程,因为如果不这样做,分叉并进入后台的进程不会受到监控,并且可能会在您不知情的情况下停止。

当您使用 CMD cron && tail -f /var/log/cron.log 时,cron 进程基本上 fork 以便在后台执行 cron,主进程退出并让您在前台执行 tailf。后台 cron 进程可能会停止或失败,您不会注意到,您的容器仍将静默运行,并且您的编排工具不会重新启动它。

您可以通过将 cron 的命令输出直接重定向到分别位于 /proc/1/fd/1 和 /proc/1/fd/2 中的 docker stdout 和 stderr 来避免这种情况。

使用基本的 shell 重定向,您可能想要执行以下操作:

* * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2

您的 CMD 将是:CMD ["cron", "-f"]


不错:cron -f 用于“cron 前台”。我已将您的答案包含在我的上面,以获得更多可见性。 +1
假设我的程序没有输出任何东西。我还能使用这种方法并确保我的进程不会在后台停止吗?
@Arcsector 这种方法避免将进程置于后台,这就是它不会静默失败的原因。在 docker 容器中拥有后台进程并不简单。如果您希望有一个正在运行的后台进程,您可能希望使用一个 init 进程来监控您在容器中运行的多个进程。另一种方法是在名为“sidecar”的主容器旁边的另一个容器中启动该过程。最好的方法通常是避免容器中的多个进程。
这是一个很好的解决方案,除了一个问题外,对我们来说效果很好。当容器收到 SIGTERM 信号时,它似乎并没有等待计划的进程完成并正常关闭,而是杀死了可能导致问题的进程。
该解决方案适用于 Debian/Alpine/CentOS 容器。这是最“便携”的解决方案。感谢这个@hugoShaka
O
Oscar Fanelli

对于那些想要使用简单轻量级图像的人:

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 输出。
对于不使用 alpine 的人:支持 -d 8 参数的 crond 不是标准的 cron,它是来自 busybox 的 crond 命令。例如,在 ubuntu 中,您可以将其作为 busybox crond -f -d 8 运行。对于旧版本,您必须使用 -L /dev/stdout/
如果可以的话,我会给这个+100。这是迄今为止在 Docker 环境中运行 cron 作业的最佳方式。
这可以完全由带有 image:alpine 的 docker-compose.yml 完成吗?
CMD ["crond" 还是 CMD ["cron"
Y
Youness

@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
谢谢 Youness,您给了我执行以下操作的想法,这在我的情况下有效,每个 cron 都在不同的文件中指定:RUN cat $APP_HOME/crons/* | crontab 就像一个魅力 :)
cron 添加到入口点脚本似乎是最好的选择:ENTRYPOINT ["entrypoint.sh"]
ENTRYPOINT 中使用 2 个命令是危险的。我相信第一个(cron)分叉到后台,而第二个(tail)在前台运行。如果 cron 停止,您将永远不会知道。如果 tail 停止,则 docker 会注意到。
这在某种程度上是有道理的,尽管您可以在它周围添加一些监控/日志记录(使用另一个入口点或其他一些监控机制)来检查 cron 服务的健康状态
O
OPSXCQ

还有另一种方法是使用 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 部分中定义的图像内。

只需运行 docker-compose up,就可以看到它工作正常。这是包含完整文档的 Tasker 存储库:

http://github.com/opsxcq/tasker


Dockerception(从另一个容器运行 docker 容器)是一种不好的做法,应该仅限于持续集成。一种解决方法是在指定容器上使用 docker exec
Tasker 在 docker (Dind/Dockerception) 中不使用 docker,请注意,作为映射传递给 docker 套接字,所有生成的容器都在 tasker 运行的守护进程中生成。如果您不想在 docker 中运行 tasker,您可以将其部署为任何其他应用程序。
我没有使用 tasker 的优势。对我来说,使用 java 和 sh*** 只是为了运行一个 cron 作业似乎真的有点过头了。
混合 cron 和您需要的基本映像(例如 python/node)创建一个需要维护和部署的额外依赖项,在这种情况下,所有作业共享同一个容器,这意味着您必须担心之后清理所有内容每个作业都运行。在 tasker 上运行的作业是幂等的,因此您不必担心。
f
funky-future

尽管这旨在通过 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 的配置也是可能的。

这是 docs,这是 image repository


谢谢你。这个答案最适合 Docker 容器环境。 Docker 镜像没有任何变化,只添加了用于执行任务的特殊容器,它的工作方式类似于命令 docker exec <container_name> <some_command> 的调度。
这是黄金!在容器范式中,一个非常优雅且易于使用的解决方案。摆脱拐杖。
h
halfer

VonC's 答案非常彻底。此外,我想补充一件对我有帮助的事情。如果您只想运行 cron 作业而不拖尾文件,您可能会很想从 cron 命令中删除 && tail -f /var/log/cron.log

然而,这将导致 Docker 容器在运行后不久退出,因为当 cron 命令完成时,Docker 认为最后一个命令已经退出并因此终止容器。这可以通过 cron -f 在前台运行 cron 来避免。


A
Andreas Forslöw

如果您使用 docker for windows,请记住,如果您打算将 crontab 文件从 windows 导入到 ubuntu 容器,则必须将行尾格式从 CRLF 更改为 LF(即从 dos 到 unix)。如果没有,您的 cron-job 将无法工作。这是一个工作示例:

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 作业是一项乏味的任务。希望它可以帮助其他无法让他们的代码工作的人!


这有助于解决我在尝试使输出重定向工作时遇到的问题。像 cat /proc/1/status > /proc/1/fd/1 这样的命令会从 crond 返回一个错误,说明 crond: USER root pid 6 cmd root cat /proc/1/status > /proc/1/fd/1: nonexistent directory/proc/1/fd/1。将行尾更改为 Unix 使我能够成功运行命令。谢谢,这花了我几个多小时才弄清楚!
G
Gaurav Tyagi

不幸的是,上述答案都没有对我有用,尽管所有答案都会导致解决方案并最终导致我的解决方案,如果它对某人有帮助,这里是片段。谢谢

这可以通过 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"]

入口点.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 容器后,您可以看到 testing cron.... 将在文件中每分钟打印一次:/var/log/cron-1.log


它不应该做 tail -f /var/log/cron-1.log 而不是 /var/log/cron-2.log,因为 cron-1.log 是 STDOUT/STDERR 被引导的地方吗? (除非我错过了什么)
是的,正确,这是一个错字,/var/log/cron-1.log 应该在每个地方
v
vinzee

我根据其他答案创建了一个 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

供参考,图像 is here


J
Jakob Eriksson

在专用容器中定义 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"

我无法使用 docker swarm 让它工作。出现 myservice unknown 错误。
应该有关于安装 docker 套接字的高安全性影响的警告:lvh.io/posts/…
I
Iljanne

我决定使用busybox,因为它是最小的图像之一。

crond 在前台执行(-f),日志发送到 stderr(-d),我没有选择更改日志级别。 crontab 文件被复制到默认路径:/var/spool/cron/crontabs

FROM busybox:1.33.1

# Usage: crond [-fbS] [-l N] [-d N] [-L LOGFILE] [-c DIR]
#
#   -f  Foreground
#   -b  Background (default)
#   -S  Log to syslog (default)
#   -l N    Set log level. Most verbose 0, default 8
#   -d N    Set log level, log to stderr
#   -L FILE Log to FILE
#   -c DIR  Cron dir. Default:/var/spool/cron/crontabs

COPY crontab /var/spool/cron/crontabs/root

CMD [ "crond", "-f", "-d" ]

P
Priya

当您在另一台主机上部署容器时,请注意它不会自动启动任何进程。您需要确保“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

我在 supervisor.log 中收到一个错误,表明 cron 服务多次停止并进入 FATAL 状态。但是 cron 似乎在顶部运行并正常执行 cronjobs。谢谢你!
是的,同样的事情也发生在我身上,但它正常工作,所以不需要打扰。
h
himanshuIIITian

设置一个与一次性作业并行的 cron

创建一个脚本文件,比如 run.sh,其中包含应该定期运行的作业。

#!/bin/bash
timestamp=`date +%Y/%m/%d-%H:%M:%S`
echo "System path is $PATH at $timestamp"

保存并退出。

使用入口点而不是 CMD

如果您在 docker 容器化期间有多个工作要启动,请使用入口点文件来运行它们。

入口点文件是一个脚本文件,它在发出 docker run 命令时生效。所以,我们想要运行的所有步骤都可以放在这个脚本文件中。

例如,我们有 2 个作业要运行:

运行一次作业:echo “Docker 容器已启动”

运行定期作业:run.sh

创建 entrypoint.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 中使用系统/用户特定的环境变量

我实际的 cron 工作期望将大多数参数作为传递给 docker run 命令的环境变量。但是,使用 bash,我无法使用属于系统或 docker 容器的任何环境变量。

然后,这是解决此问题的方法:

在 entrypoint.sh 中添加以下行

declare -p | grep -Ev 'BASHOPTS|BASH_VERSINFO|EUID|PPID|SHELLOPTS|UID' > /container.env

更新 cron 设置并指定 -

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

最后但并非最不重要的一点:创建 Dockerfile

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 镜像!


@himanshuIIITian 我试过这个,问题是“运行一次作业”的脚本永远不会返回,并且玉米 -f 也没有返回所以......这对我不起作用,有什么想法吗?谢谢
@DoronLevi - 你能分享一些日志来调查这个问题吗?或者您可以从这里检查整个代码 - github.com/nehabhardwaj01/docker-cron
D
Dan Watts

从上面的例子中,我创建了这个组合:

在 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

T
That Brazilian Guy

这是我基于 docker-compose 的解决方案:

  cron:
    image: alpine:3.10
    command: crond -f -d 8
    depends_on:
      - servicename
    volumes:
      - './conf/cron:/etc/crontabs/root:z'
    restart: unless-stopped

带有 cron 条目的行位于 ./conf/cron 文件中。

注意:这不会运行不在 alpine 映像上的命令。


p
pablorsk

这个问题有很多答案,但有些很复杂,有些有一些缺点。我尝试解释问题并尝试提供解决方案。

cron-entrypoint.sh

#!/bin/bash

# copy machine environment variables to cron environment
printenv | cat - /etc/crontab > temp && mv temp /etc/crontab

## validate cron file
crontab /etc/crontab

# cron service with SIGTERM support
service cron start
trap "service cron stop; exit" SIGINT SIGTERM

# just dump your logs to std output
tail -f  \
    /app/storage/logs/laravel.log \
    /var/log/cron.log \
    & wait $!

解决的问题

环境变量在 cron 环境中不可用(如 env vars 或 kubernetes 机密)

当 crontab 文件无效时停止

当机器收到 SIGTERM 信号时,优雅地停止 cron 作业

对于上下文,我在 Kubernetes 上使用以前的脚本和 Laravel 应用程序。


如果我使用此设置运行 docker stop,则不会发生任何事情,即 service cron stop 不会被执行。如果我在容器内手动运行后者,cron 进程会立即停止,而不是等待 cronjobs。 cronjobs 仍然会完成它们的运行,所以这可能没问题。完成后,容器也不会停止。我错过了什么?
现在可以工作了。我认为陷阱处理程序没有被触发,因为我将 entryscript 定义为 CMD "/bin/sh" ENTRYPOINT /entrypoint.sh 而不是 ENTRYPOINT ["/entrypoint.sh"]。这样,它就被包裹在另一个没有传递信号的外壳中。我必须做一些进一步的步骤才能真正等待运行 cronjobs 完成。详细说明您的答案 over here
S
Santiago Vasquez

这条线是帮助我运行我预先安排的任务的那条线。

ADD mycron/root /etc/cron.d/root

RUN chmod 0644 /etc/cron.d/root

RUN crontab /etc/cron.d/root

RUN touch /var/log/cron.log

CMD ( cron -f -l 8 & ) && apache2-foreground # <-- run cron

--> 我的项目在里面运行:FROM php:7.2-apache


t
turivishal

在一些限制 root 访问的精简图像上运行时,我必须将我的用户添加到 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

也许这可以帮助某人


我相信节点图像使用节点用户;所以也许您需要为该用户添加权限
r
random2137

所以,我的问题是一样的。解决方法是更改 docker-compose.yml 中的命令部分。

命令:crontab /etc/crontab && tail -f /etc/crontab

命令:crontab /etc/crontab

命令:tail -f /etc/crontab

问题是命令之间的“&&”。删除后,一切都很好。


J
Johnson_145

专注于在接收到 SIGTERMSIGQUIT 信号时(例如,在运行 docker stop 时)优雅地停止 cronjobs。

这不太容易。默认情况下,cron 进程只是被杀死而没有注意运行 cronjobs。我正在详细说明 pablorsk's answer

Dockerfile

FROM ubuntu:latest

RUN apt-get update \
    && apt-get -y install cron procps \
    && rm -rf /var/lib/apt/lists/*

# Copy cronjobs file to the cron.d directory
COPY cronjobs /etc/cron.d/cronjobs

# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/cronjobs

# similarly prepare the default cronjob scripts
COPY run_cronjob.sh /root/run_cronjob.sh
RUN chmod +x /root/run_cronjob.sh
COPY run_cronjob_without_log.sh /root/run_cronjob_without_log.sh
RUN chmod +x /root/run_cronjob_without_log.sh

# Apply cron job
RUN crontab /etc/cron.d/cronjobs

# to gain access to environment variables, we need this additional entrypoint script
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

# optionally, change received signal from SIGTERM TO SIGQUIT
#STOPSIGNAL SIGQUIT

# Run the command on container startup
ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh

#!/bin/bash
# make global environment variables available within crond, too
printenv | grep -v "no_proxy" >> /etc/environment

# SIGQUIT/SIGTERM-handler
term_handler() {
  echo 'stopping cron'
  service cron stop
  echo 'stopped'
  echo 'waiting'
  x=$(($(ps u -C run_cronjob.sh | wc -l)-1))
  xold=0
  while [ "$x" -gt 0 ]
  do
    if [ "$x" != "$xold" ]; then
      echo "Waiting for $x running cronjob(s):"
      ps u -C run_cronjob.sh
      xold=$x
      sleep 1
    fi
    x=$(($(ps u -C run_cronjob.sh | wc -l)-1))
  done
  echo 'done waiting'
  exit 143; # 128 + 15 -- SIGTERM
}

# cron service with SIGTERM and SIGQUIT support
service cron start
trap "term_handler" QUIT TERM

# endless loop
while true
do
  tail -f /dev/null & wait ${!}
done

cronjobs

* * * * * ./run_cronjob.sh cron1
*/2 * * * * ./run_cronjob.sh cron2
*/3 * * * * ./run_cronjob.sh cron3

假设您将所有 cronjobs 包装在一个 run_cronjob.sh 脚本中。这样,您可以执行关闭将正常等待的任意代码。

run_cronjobs.sh(保持 cronjob 定义干净的可选帮助脚本)

#!/bin/bash

DIR_INCL="${BASH_SOURCE%/*}"
if [[ ! -d "$DIR_INCL" ]]; then DIR_INCL="$PWD"; fi
cd "$DIR_INCL"

# redirect all cronjob output to docker
./run_cronjob_without_log.sh "$@" > /proc/1/fd/1 2>/proc/1/fd/2

run_cronjob_without_log.sh

your_actual_cronjob_src()

顺便说一句,当收到 SIGKILL 时,容器仍会立即关闭。这样,您可以使用像 docker-compose stop -t 60 cron-container 这样的命令来等待 60 秒以使 cronjobs 正常完成,但在超时后仍然可以肯定地终止它们。