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

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


当前回答

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

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 secrets) 当crontab文件无效时停止 当机器接收到SIGTERM信号时,优雅地停止cron作业

作为上下文,我在Kubernetes上使用Laravel应用程序使用之前的脚本。

其他回答

对于多个作业和各种依赖项(如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中挂载时立即反映出来。

下面是我的基于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映像中的命令。

此外,任务的输出显然不会出现在docker日志中。

所以,我的问题也一样。修复方法是改变docker-compose.yml中的命令部分。

From

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

To

命令:crontab /etc/crontab

命令:tail -f /etc/crontab

问题在于命令之间的“&&”。删除后,一切都好了。

VonC的回答相当彻底。此外,我想补充一件对我有帮助的事情。如果您只想运行一个cron作业而不跟踪文件,那么您可能会想从cron命令中删除&& tail -f /var/log/cron.log。

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

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

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

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" ]

但是任务的输出显然不能在docker日志中看到。