如何在 Docker 容器内运行 cron 定时任务?
容器内跑 cron:Dockerfile 装 cron、COPY crontab 文件、入口脚本用 cron -f 前台运行;cron 环境继承取决于实现,应显式传递允许的任务变量。K8s 场景推荐改用 CronJob。本文给出完整 Dockerfile 示例。
基本步骤:镜像里装 cron → COPY crontab 文件到 /etc/cron.d/ → 专用调度容器里用 cron -f 前台运行;环境继承行为取决于 cron 实现,应显式传递允许的任务变量。不过容器里跑 cron 是妥协方案,K8s 环境优先用 CronJob,单机也可用宿主机 cron 调 docker exec。
完整 Dockerfile 示例
FROM python:3.12-slim
RUN apt-get update && apt-get install -y cron && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# 拷贝定时任务定义
COPY crontab /etc/cron.d/app-cron
RUN chown root:root /etc/cron.d/app-cron && chmod 0644 /etc/cron.d/app-cron
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]
crontab 文件:
*/10 * * * * root /usr/local/bin/python /app/jobs/run_cleanup.py >> /proc/1/fd/1 2>&1
#!/bin/bash
set -e
umask 077
/usr/local/bin/python - <<'PYENV'
import json, os
with open('/run/job-env.json', 'w') as f:
json.dump({k: os.environ[k] for k in ('APP_ENV', 'API_URL') if k in os.environ}, f)
PYENV
exec cron -f # 专用调度容器,前台运行
任务包装脚本 /app/jobs/run_cleanup.py:
import json, os, sys
with open('/run/job-env.json') as f:
allowed = json.load(f)
env = {**os.environ, **allowed}
os.execve(sys.executable, [sys.executable, '/app/jobs/cleanup.py'], env)
/etc/cron.d 文件使用系统格式,必须有用户字段且末尾换行,不能再用 crontab 命令当用户表重复安装。
三个必踩的坑
- 环境变量:按实现核对环境;示例只将允许的变量存成权限受控 JSON,避免 printenv 内容被当 shell 代码执行
- 任务输出:cron 默认可能通过邮件投递输出,但容器未必配置了可用的邮件服务,重定向到
/proc/1/fd/1(主进程 stdout),这样docker logs和日志采集能拿到 - 双进程:若必须同时运行 cron 和应用,需要明确的监督与信号转发;上例专用调度容器不再启动 app.py
任务输出应带上任务名、执行时间和最终结果,并确认在容器日志中可见。用 DataKit接入观测云后,可按实际调度周期配置日志检测及无数据通知,窗口中留出执行与采集延迟。收到无成功记录的通知时,再区分任务未启动、执行失败和采集链路中断。
更推荐的替代方案
| 方案 | 适用 |
|---|---|
| K8s CronJob | K8s 环境首选,任务独立 Pod,可显式配置环境变量;调度不保证恰好一次执行,任务须幂等并处理重叠 |
| 宿主机 cron + docker exec | 单机少量任务,容器保持单进程 |
| 应用内调度器 | APScheduler / node-cron,与应用同进程 |
常见问题(FAQ)
Q:cron 容器时区不对?
A:镜像默认 UTC,挂载 -v /etc/localtime:/etc/localtime:ro 或镜像里设置 TZ 环境变量并装 tzdata。
Q:任务没执行怎么查?
A:进容器 grep CRON /var/log/syslog(需 rsyslog)或直接看重定向到 stdout 的输出;先手动跑一遍命令确认命令本身没问题。
Q:Alpine 镜像怎么装 cron?
A:Alpine 自带 crond(busybox),crond -f 前台启动即可,crontab 文件放 /etc/crontabs/root。