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

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


当前回答

我决定使用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日志中看到。

其他回答

专注于在接收到SIGTERM或SIGQUIT信号时优雅地停止cronjob(例如在运行docker stop时)。

这并不容易。默认情况下,cron进程只是被杀死,而不注意运行cronjob。我在详细阐述pablorsk的回答:

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

的计划

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

假设您将所有cronjob包装在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秒,让cronjob优雅地完成,但仍然在超时后终止它们。

下面是我的基于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的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一样的配置也是可能的。

这是文档,这是图像存储库。

对于多个作业和各种依赖项(如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.yml中的命令部分。

From

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

To

命令:crontab /etc/crontab

命令:tail -f /etc/crontab

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