Docker 容器中运行定时任务

FreeGuideOnline 最新 2026-07-06

bash #!/bin/bash echo "$(date): 开始归档日志..." >> /var/log/app/cron.log

实际的业务逻辑,例如压缩旧日志

find /var/log/app/ -name "*.log" -mtime +7 -exec gzip {} ;


确保脚本有可执行权限:`chmod +x /usr/local/bin/archive-logs.sh`

### 步骤 2:创建 Crontab 定义文件
在项目目录下创建 `crontab` 文件,例如 `my-crontab`:

每分钟执行一次(仅用于测试)

          • /usr/local/bin/archive-logs.sh >> /var/log/app/cron.log 2>&1

每天凌晨2点执行数据清理

0 2 * * * /usr/local/bin/cleanup-tmp.sh


### 步骤 3:编写 Dockerfile 并让 Cron 作为主进程
关键技术点:**将 cron 运行在前台**,并处理环境变量传递。

```dockerfile
FROM ubuntu:22.04

# 安装 cron 和必要的工具
RUN apt-get update && apt-get install -y cron && rm -rf /var/lib/apt/lists/*

# 创建应用目录和日志文件
RUN mkdir -p /var/log/app && touch /var/log/app/cron.log

# 复制脚本和 crontab
COPY archive-logs.sh /usr/local/bin/archive-logs.sh
COPY cleanup-tmp.sh /usr/local/bin/cleanup-tmp.sh
COPY my-crontab /etc/cron.d/my-crontab

# 赋予执行权限
RUN chmod +x /usr/local/bin/*.sh

# 应用 crontab
RUN crontab /etc/cron.d/my-crontab

# 创建启动脚本,解决环境变量传递与前台运行问题
RUN echo '#!/bin/bash\nprintenv | grep -v "no_proxy" >> /etc/environment\ncron -f' > /start.sh
RUN chmod +x /start.sh

CMD ["/start.sh"]

核心解释

  • cron -f 让 cron 守护进程在前台运行,成为容器的主进程。
  • printenv | grep -v "no_proxy" >> /etc/environment 这一步极为关键:因为 cron 运行任务时使用的环境非常干净,不会自动继承 Docker 容器传入的环境变量。通过将环境变量写入 /etc/environment,cron 的子进程便能够读取到它们。
  • 我们也可以在任务脚本内部手动加载所需变量,但上述方法更为通用。

构建并运行:

docker build -t my-cron-app .
docker run -d --name cron-test my-cron-app

查看日志验证任务执行:

docker exec -it cron-test tail -f /var/log/app/cron.log

适用场景与局限

  • 优点:依赖简单,镜像自包含,方便移植。
  • 缺点:每个容器仅能跑一个 cron 守护进程;任务失败通知较困难;日志容易被淹没;需自行处理环境变量。

方法二:使用 轻量级 Cron 替代品 —— supercronic

Supercronic 是一个用 Go 编写的 cron 运行器,专为容器而生。它能够正确转发信号、处理日志、且无需复杂的启动脚本。

Dockerfile 示例(基于 Alpine)

FROM alpine:latest

# 安装 supercronic(直接下载二进制)
ENV SUPERCRONIC_VERSION=v0.2.29
RUN wget -O /usr/local/bin/supercronic \
    https://github.com/aptible/supercronic/releases/download/${SUPERCRONIC_VERSION}/supercronic-linux-amd64 \
    && chmod +x /usr/local/bin/supercronic

COPY my-crontab /etc/crontab
COPY archive-logs.sh /usr/local/bin/archive-logs.sh

CMD ["/usr/local/bin/supercronic", "/etc/crontab"]

Supercronic 自动将整个环境变量传递给任务,且会输出结构化的任务执行日志到 stdout/stderr,非常适合配合 Docker 日志驱动。遇到任务失败时,它会以非零状态退出,便于容器编排平台检测。


方法三:宿主机 Cron 调用容器执行一次性命令

如果你不希望修改镜像,或者定时任务属于“管理类”操作(如备份整个应用),可以由宿主机的 cron 配合 docker exec 完成。

宿主机 Crontab 示例

# 每天凌晨1点执行容器内的备份脚本
0 1 * * * docker exec my-app-container /usr/local/bin/backup.sh

优点:无需改动镜像,集中管理任务。 缺点:与宿主机耦合,容器名称或 ID 变化时需要更新;需关注容器存活状态;破坏了容器的自包含性。


方法四:使用容器编排工具的原生定时任务支持

Docker Swarm 模式

Swarm 本身不直接支持 CronJob,但可以通过调度全局或副本服务并结合 sleep 技巧实现,但不够优雅。更好的方式是利用第三方工具如 Ofelia。

Kubernetes 中的 CronJob

在生产环境中,Kubernetes CronJob 是运行定时任务的首选方案。它完全遵循云原生理念。

定义一个 CronJob YAML

apiVersion: batch/v1
kind: CronJob
metadata:
  name: archive-logs
spec:
  schedule: "0 2 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: archiver
            image: your-registry/log-archiver:latest
            args:
            - /bin/sh
            - -c
            - /usr/local/bin/archive-logs.sh
          restartPolicy: OnFailure
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1