Docker容器中的定时任务:使用Supercronic运行Python脚本

告别Docker内Cron的环境变量烦恼,Supercronic成为更优选择

上一篇文章中,我介绍了 eudic-maimemo-sync 项目——利用 Python 脚本调用欧路词典墨墨背单词的开放API将生词本自动同步到墨墨背单词,并演示了如何用 Docker 将其部署到树莓派上,每天自动执行同步。

这篇文章,我想分享我在配置 Docker 容器实现定时任务时遇到的一个问题,以及我是如何解决它的:我最初在容器内使用标准的 Cron 服务来调度 Python 脚本。但我发现,即使把环境变量传入了容器,Cron 触发脚本时依然读不到这部分配置,导致 Python 读取不到 API 密钥。原因是 Cron 不会继承容器的环境变量,它运行在相对隔离的环境中。

为了解决这个问题,我开始在网上搜索并向大语言模型求助,最后得到几个可能的方案:

  • .env 文件直接挂载进容器,让脚本从指定路径读取
  • entrypoint.sh 文件中,读取环境变量,然后显式地将它们写入 crontab 文件或注入到 Cron 进程环境中
  • 使用 Supercronic 或其他替代 Cron 方案

虽然前两种方法可行,但需要额外的配置和脚本处理。经过测试,我最终选择了 Supercronic。它是一个与 Cron 语法兼容、专为容器设计的任务调度工具,用法和原生 Cron 几乎一致,且能够直接继承容器内的环境变量,大大简化了配置过程。将 Cron 替换为 Supercronic 后,问题就迎刃而解了。

如果你有同样的需求,想要通过容器定时运行 Python 脚本或其他程序,我推荐试试 Supercronic。我精简了eudic-maimemo-sync 项目,编写一个基础示例 docker-python-supercronic-example。示例中展示了如何在 Docker 容器里配置 Supercronic,并定时执行 Python 脚本,供大家参考。


接下来我讲讲如何使用这个示例:

.
├── Dockerfile          # 用于构建包含 Supercronic 的 Python 镜像
├── docker-compose.yml  # 容器部署的配置
├── entrypoint.sh       # 生成 crontab 并启动 Supercronic 的脚本
├── test.py             # 按计划运行的示例 Python 脚本
└── requirements.txt    # Python 依赖项

开始使用

  1. 克隆此仓库

    git clone https://github.com/emuqi/docker-python-supercronic-example.git
    cd docker-python-supercronic-example
  2. 为你的架构选择 Supercronic

    Dockerfile 默认配置为 ARM64 架构。对于其他架构(例如 x86_64),需要修改 Dockerfile

    访问 Supercronic Releases,找到适合你的 CPU 架构的 安装说明。安装说明内一般会包含要添加到 Dockerfile 的代码。

    替换以下部分:

    # 最新版本请访问 https://github.com/aptible/supercronic/releases
    # 根据你的CPU架构选择合适的 Supercronic 版本
    ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.2.33/supercronic-linux-arm64 \
    SUPERCRONIC_SHA1SUM=e0f0c06ebc5627e43b25475711e694450489ab00 \
    SUPERCRONIC=supercronic-linux-arm64
    RUN curl -fsSLO "$SUPERCRONIC_URL" \
    && echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - \
    && chmod +x "$SUPERCRONIC" \
    && mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \
    && ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic
  3. 创建 .env 文件用于设置环境变量

    CUSTOM_MESSAGE=来自计划任务的问候!
    CRON_SCHEDULE=*/5 * * * *  # 每 5 分钟运行一次
  4. 构建并启动容器

    docker compose up -d --build
  5. 检查日志以验证程序是否按预期运行

    docker logs my_python_cron_job

    你应该能看到类似下面的日志输出:

    my_python_cron_job  | time="2025-04-23T18:53:00+08:00" level=info msg="Python script start" channel=stdout iteration=0 job.command="/usr/local/bin/python /app/test.py" job.position=0 job.schedule="* * * * *"
    my_python_cron_job  | time="2025-04-23T18:53:00+08:00" level=info msg="Message: Hello World!" channel=stdout iteration=0 job.command="/usr/local/bin/python /app/test.py" job.position=0 job.schedule="* * * * *"
    my_python_cron_job  | time="2025-04-23T18:53:00+08:00" level=info msg="job succeeded" iteration=0 job.command="/usr/local/bin/python /app/test.py" job.position=0 job.schedule="* * * * *"

配置

环境变量

变量 描述
TZ 容器时区
CRON_SCHEDULE 用于调度的 Cron 表达式
CUSTOM_MESSAGE 传递给 Python 脚本的示例变量

Cron 计划表达式示例

  • * * * * * - 每分钟
  • */5 * * * * - 每 5 分钟
  • 0 * * * * - 每小时(0分时)
  • 0 0 * * * - 每天午夜
  • 0 18 * * * - 每天下午 6:00

自定义

  1. 用你自己的 Python 脚本替换或修改 test.py
  2. 通过编辑 .env 文件或 docker-compose.yml 中的 environment: 部分来扩展或覆盖变量。添加的任何新变量(例如 NEW_VAR=value)都将被加载到容器中。
  3. 将所需的任何依赖项添加到 requirements.txt
  4. 重新构建并运行容器:docker-compose up -d --build