Supercronic 有一篇发布公告博客文章!
Supercronic 是一个与 crontab 兼容的任务运行器,专门设计用于在容器中运行。
Crontab 是任务调度的通用语言,但典型的服务器 cron 实现不适合容器环境:
stdout / stderr 通常更容易处理。SIGINT / SIGTERM / SIGQUIT,并且在收到信号时可能会使正在运行的任务变成孤儿。这在服务器环境中是有意义的,因为 init 会处理孤儿任务,而且 Cron 也不经常重启,但在容器环境中这是不合适的,因为当容器退出时会导致任务被强制终止(即 SIGKILL)。stdout 或 stderr。最后,它们通常是静默的,使这些问题难以理解和调试!
Supercronic 的目标是完全按照您期望在容器中运行的 cron 的行为方式运行:
stdout / stderrSIGTERM 触发优雅关闭(SIGINT 也是如此,当以交互方式使用时,您可以通过 CTRL+C 发送)stdout / stderrSIGUSR2 触发优雅关闭并重新加载 crontab 配置SIGQUIT 触发优雅关闭supercronic CRONTAB我们(Aptible)最初创建 Supercronic 是为了让我们的无基础设施平台的客户能够轻松地在他们的应用中加入定期任务,但它更广泛地适用于任何在容器中运行 cron 作业的人。
安装 Supercronic 最简单的方法是下载预构建的二进制文件。
导航到发布页面,获取适合您系统的构建版本。发布版本包括安装 Supercronic 的示例 Dockerfile 语句,您可以轻松地将它们包含在您自己的 Dockerfile 中或根据需要进行调整。
注意:如果您不确定哪个二进制文件适合您,请尝试 supercronic-linux-amd64。
您也可以从源代码构建 Supercronic。
运行以下命令来获取 Supercronic,安装其依赖项,并安装它:
go get -d cnb.cool/svn/supercronic cd "${GOPATH}/src/cnb.cool/svn/supercronic" go mod vendor go install
总的来说,Supercronic 尝试像 Vixie cron 一样处理 crontab。在大多数情况下,它应该与您现有的 crontab 兼容。
然而,有几个例外:
cronexpr 包,所以请参考其文档以了解您可以做的确切操作。USER 将不会产生效果。在容器环境中,更改用户通常通过其他方式完成,例如,向您的 Dockerfile 添加 USER 指令。# 语法:秒 分 时 日 月 星期 年 * * * * * * * # 每秒执行 */10 * * * * * * # 每10秒执行 0 30 */2 * * * * # 每2小时30分0秒执行 0 0 12 * * 1-5 * # 工作日中午12点执行
# 语法:分 时 日 月 星期 年 * * * * * 2026 # 2026 年内每分钟执行 0 0 * * * 2026 # 2026 年内每天午夜执行 30 8 * * 1-5 2026 # 2026 年工作日 08:30 执行
# 语法:分 时 日 月 星期 * * * * * # 每分钟执行(自动添加0秒) */1 * * * * # 每分钟执行 0 */2 * * * # 每2小时执行 0 0 * * * # 每天午夜执行
* : 匹配任意值*/n : 每隔 n 个单位x-y : 范围,从 x 到 yx,y,z : 列表,匹配多个值L : 最后(如每月最后一天 L,最后一个星期五 5L)W : 最近的工作日# : 第几个星期(如 2#3 表示第 3 个星期二)@reboot # 启动时执行(不支持) @yearly # 每年1月1日午夜 等同于 0 0 0 1 1 * * @annually # 每年1月1日午夜 等同于 0 0 0 1 1 * * @monthly # 每月1日午夜 等同于 0 0 0 1 * * * @weekly # 每周日午夜 等同于 0 0 0 * * 0 * @daily # 每天午夜 等同于 0 0 0 * * * * @hourly # 每小时开始 等同于 0 0 * * * * *
# 传统5字段格式 * * * * * echo "每分钟执行一次" */5 * * * * echo "每5分钟执行一次" 0 2 * * * echo "每天凌晨2点执行" # 6字段格式(添加年份) * * * * * 2026 echo "2026年内每分钟执行一次" 0 2 * * * 2026 echo "2026年每天凌晨2点执行" # 7字段格式(秒级+年份) */2 * * * * * 2024 echo "2024年每2秒执行" 0 0 12 1 * * 2024 echo "2024年每月1日中午12点执行" # 复杂表达式 0 9 * * 1-5 echo "工作日上午9点" 0 0 1 * * echo "每月1日午夜" 0 0 L * * echo "每月最后一天午夜" 5 0 * * 6 echo "每周六凌晨0点5分"
0 秒和 * 年份与常规 cron 一样,Supercronic 允许您在 crontab 中使用 KEY=VALUE 语法指定环境变量。
然而,这只是为了与现有 crontab 兼容,在使用 Supercronic 时通常不推荐使用此功能。
确实,Supercronic 不会在运行作业之前清除您的环境,因此如果您需要在作业运行时环境变量可用,只需在启动 Supercronic 本身之前设置它们,您的作业将继承它们。
例如,如果您使用 Docker,Supercronic 启动的作业将继承使用您 Dockerfile 中的 ENV 指令定义的环境变量,以及运行容器时传递的变量(例如,通过 docker run -e SOME_VARIABLE=SOME_VALUE)。
除非您以前使用过 cron,否则这正是您期望环境变量的工作方式!
Supercronic 使用来自 /etc/localtime 的当前时区来调度作业。您也可以通过设置环境变量 TZ(例如 TZ=Europe/Berlin)在运行 Supercronic 时覆盖时区。您可能需要安装 tzdata 以便 Supercronic 能够找到提供的时区。
您可以覆盖 TZ 以使用不同的时区,但如果您需要让 cron 作业在时区 A 中调度并在时区 B 中运行,您可以在 /etc/localtime 或 TZ 设置为 B 的情况下运行,并向您的 crontab 添加一行 CRON_TZ=A。
如果您不确定 Supercronic 正在使用哪个时区,可以使用 -debug 标志运行它以确认。
--prometheus-listen-address: 绑定 Prometheus HTTP 端口,不设置则不启动。--prometheus-include-command: 是否在指标中包含 command 标签,默认关闭以避免高基数;可通过环境变量 SUPERCRONIC_PROMETHEUS_INCLUDE_COMMAND=true 开启(可能导致动态 crontab 场景下指标维度爆炸、内存上升)。command 标签以避免敏感信息泄露和高基数问题详细变更请参考 CHANGELOG.md和 迁移指南。
Supercronic 提供丰富的日志记录,并将告诉您确切是什么命令触发了给定的消息。这是一个示例:
$ cat ./my-crontab */5 * * * * * * echo "hello from Supercronic" $ ./supercronic ./my-crontab INFO[2017-07-10T19:40:44+02:00] read crontab: ./my-crontab INFO[2017-07-10T19:40:50+02:00] starting iteration=0 job.command="echo "hello from Supercronic"" job.position=0 job.schedule="*/5 * * * * * *" INFO[2017-07-10T19:40:50+02:00] hello from Supercronic channel=stdout iteration=0 job.command="echo "hello from Supercronic"" job.position=0 job.schedule="*/5 * * * * * *" INFO[2017-07-10T19:40:50+02:00] job succeeded iteration=0 job.command="echo "hello from Supercronic"" job.position=0 job.schedule="*/5 * * * * * *" INFO[2017-07-10T19:40:55+02:00] starting iteration=1 job.command="echo "hello from Supercronic"" job.position=0 job.schedule="*/5 * * * * * *" INFO[2017-07-10T19:40:55+02:00] hello from Supercronic channel=stdout iteration=1 job.command="echo "hello from Supercronic"" job.position=0 job.schedule="*/5 * * * * * *" INFO[2017-07-10T19:40:55+02:00] job succeeded iteration=1 job.command="echo "hello from Supercronic"" job.position=0 job.schedule="*/5 * * * * * *"
Supercronic 支持自动日志文件轮转和压缩功能,无需外部工具即可管理日志文件。
使用 --log-file 参数将日志输出到文件:
$ ./supercronic --log-file=/var/log/supercronic.log ./my-crontab
| 参数 | 默认值 | 说明 |
|---|---|---|
--log-file | (空) | 日志文件路径,未设置时输出到 stdout |
--log-max-size | 100 | 单个日志文件最大大小(MB) |
--log-max-age | 30 | 保留日志文件的最大天数 |
--log-max-backups | 30 | 保留的旧日志文件最大数量 |
--log-compress | true | 是否压缩轮转后的日志文件为 .gz 格式 |
完整配置示例:
$ ./supercronic \
--log-file=/var/log/supercronic.log \
--log-max-size=50 \
--log-max-age=7 \
--log-max-backups=10 \
--log-compress=true \
./my-crontab
当日志文件达到 50MB 时,会自动轮转:
supercronic-2024-12-15T14-30-25.123456789.logsupercronic-2024-12-15T14-30-25.123456789.log.gzsupercronic.log 继续记录在 Dockerfile 中配置日志轮转:
# 创建日志目录 RUN mkdir -p /var/log/app # 使用日志轮转运行 CMD ["/usr/local/bin/supercronic", \ "--log-file=/var/log/app/cron.log", \ "--log-max-size=100", \ "--log-max-age=30", \ "/etc/crontab"]
或通过 docker-compose.yml 配置:
services:
cron:
image: your-app
command:
- supercronic
- --log-file=/var/log/cron.log
- --log-max-size=50
- --log-max-age=7
- /etc/crontab
volumes:
- ./logs:/var/log # 挂载日志目录到宿主机
max-age 和 max-backups 避免占用过多磁盘如果您的作业没有运行,或者您只是想再次检查您的 crontab 语法,请传递 -debug 标志以获取更详细的日志记录:
$ ./supercronic -debug ./my-crontab INFO[2017-07-10T19:43:51+02:00] read crontab: ./my-crontab DEBU[2017-07-10T19:43:51+02:00] try parse(7): */5 * * * * * * echo "hello from Supercronic"[0:15] = */5 * * * * * * DEBU[2017-07-10T19:43:51+02:00] job will run next at 2017-07-10 19:44:00 +0200 CEST job.command="echo "hello from Supercronic"" job.position=0 job.schedule="*/5 * * * * * *"
Supercronic 会等待给定作业完成后再次调度该作业(某些 cron 实现这样做,其他则不)。如果作业落后于计划(即花费太长时间完成),Supercronic 会警告您。
这是一个示例:
$ cat ./my-crontab # 每秒睡眠2秒。这将花费太长时间。 * * * * * * * sleep 2 $ ./supercronic ./my-crontab INFO[2017-07-11T12:24:25+02:00] read crontab: ./my-crontab INFO[2017-07-11T12:24:27+02:00] starting iteration=0 job.command="sleep 2" job.position=0 job.schedule="* * * * * * *" INFO[2017-07-11T12:24:29+02:00] job succeeded iteration=0 job.command="sleep 2" job.position=0 job.schedule="* * * * * * *" WARN[2017-07-11T12:24:29+02:00] job took too long to run: it should have started 1.009438854s ago job.command="sleep 2" job.position=0 job.schedule="* * * * * * *" INFO[2017-07-11T12:24:30+02:00] starting iteration=1 job.command="sleep 2" job.position=0 job.schedule="* * * * * * *" INFO[2017-07-11T12:24:32+02:00] job succeeded iteration=1 job.command="sleep 2" job.position=0 job.schedule="* * * * * * *" WARN[2017-07-11T12:24:32+02:00] job took too long to run: it should have started 1.014474099s ago job.command="sleep 2" job.position=0 job.schedule="* * * * * * *"
您可以选择性地禁用此行为,并通过向 Supercronic 传递 -overlapping 标志来允许作业的重叠实例。Supercronic 仍会警告作业落后,但会运行它们的重复实例。
向 Supercronic 发送 SIGUSR2 以重新加载 crontab:
# docker 环境(Supercronic 需要是容器中的 PID 1)
docker kill --signal=USR2 <container id>
# shell
kill -USR2 <pid>
如果您在发送 SIGUSR2 比较麻烦的环境中运行 Supercronic,或者您期望频繁更新您的 crontab 文件,您可以选择使用 -inotify 标志运行 Supercronic。这将在 crontab 文件上启动监视,在更改时重新加载它。一个示例用例是一个运行 Supercronic 的 kubernetes pod,它从 configMap 挂载其 crontab 文件。使用 -inotify 标志,对此 configmap 的任何更新(前提是它不是不可变的)都将触发 Supercronic 中的重新加载,而无需您找出向 pod 发送 SIGUSR2 信号的机制。对 crontab 文件的监视在 Write 和 Remove 事件上触发,后者确保检测 kubernetes 的原子写入。
$ ./supercronic -inotify ./my-crontab ... time="2024-09-11T09:23:18+02:00" level=debug msg="event: CHMOD \"./my-crontab\", watch-list: []" time="2024-09-11T09:23:18+02:00" level=debug msg="event: REMOVE \"./my-crontab\", watch-list: []" time="2024-09-11T09:23:18+02:00" level=debug msg="watched file changed" time="2024-09-11T09:23:18+02:00" level=info msg="received user defined signal 2, reloading crontab" time="2024-09-11T09:23:18+02:00" level=info msg="waiting for jobs to finish" time="2024-09-11T09:23:18+02:00" level=debug msg="shutting down" job.command="sleep 2" job.position=0 job.schedule="* * * * *" time="2024-09-11T09:23:18+02:00" level=info msg="read crontab: ./my-crontab" time="2024-09-11T09:23:18+02:00" level=debug msg="try parse (7 fields): '* * * * * sleep 5'" time="2024-09-11T09:23:18+02:00" level=debug msg="failed to parse (7 fields): '* * * * * sleep 5': failed: syntax error in day-of-week field: 'sleep'" time="2024-09-11T09:23:18+02:00" level=debug msg="try parse (6 fields): '* * * * * sleep'" time="2024-09-11T09:23:18+02:00" level=debug msg="failed to parse (6 fields): '* * * * * sleep': failed: syntax error in year field: 'sleep'" time="2024-09-11T09:23:18+02:00" level=debug msg="try parse (5 fields): '* * * * *'" time="2024-09-11T09:23:18+02:00" level=debug msg="job will run next at 2024-09-11 09:24:00 +0200 CEST" job.command="sleep 5" job.position=0 job.schedule="* * * * *"
使用 -test 标志提示 Supercronic 验证您的 crontab,但不执行它。这对于作为构建过程的一部分来验证 crontab 的语法很有用。
默认情况下,Supersonic 将所有日志路由到 stderr。如果您希望将此行为更改为基于级别的日志记录,请传递 -split-logs 标志以将调试和信息级别日志路由到 stdout:
$ ./supercronic -split-logs ./my-crontab 1>./stdout.log $ cat ./stdout.log time="2019-01-12T19:34:57+09:00" level=info msg="read crontab: ./my-crontab" time="2019-01-12T19:35:00+09:00" level=info msg=starting iteration=0 job.command="echo \"hello from Supercronic\"" job.position=0 job.schedule="*/5 * * * * * *" time="2019-01-12T19:35:00+09:00" level=info msg="hello from Supercronic" channel=stdout iteration=0 job.command="echo \"hello from Supercronic\"" job.position=0 job.schedule="*/5 * * * * * *" time="2019-01-12T19:35:00+09:00" level=info msg="job succeeded" iteration=0 job.command="echo \"hello from Supercronic\"" job.position=0 job.schedule="*/5 * * * * * *"
Supercronic 提供与 Sentry 的集成,用于实时错误跟踪和报告。此功能有助于识别、分类和修复 cron 作业中的崩溃。
要启用 Sentry 报告,请配置 Sentry 数据源名称 (DSN),例如在启动 Supercronic 时使用 -sentry-dsn 参数
$ ./supercronic -sentry-dsn DSN
您也可以通过 SENTRY_DSN 环境变量指定 DSN。
当同时通过环境变量和命令行参数指定 DSN 时,
参数的 DSN 具有优先权。
您还可以为 Sentry 指定环境和发布版本,以便为错误报告提供更多上下文:
环境:使用 -sentry-environment 标志或 SENTRY_ENVIRONMENT 环境变量在 Sentry 中设置环境标签。
$ ./supercronic -sentry-dsn YOUR_SENTRY_DSN -sentry-environment YOUR_ENVIRONMENT
发布版本:使用 -sentry-release 标志或 SENTRY_RELEASE 环境变量在 Sentry 中设置发布版本标签。
$ ./supercronic -sentry-dsn YOUR_SENTRY_DSN -sentry-release YOUR_RELEASE
如果您对 Supercronic 有任何问题,请随时在此存储库中打开 issue!
请注意,如果您尝试在 Aptible App 上使用 Supercronic,我们有专门的支持文章。
PR 总是受欢迎的!在进行重大更改之前,考虑打开一个 issue 进行一些讨论。
参见 LICENSE.md。
版权所有 (c) 2019 Aptible。保留所有权利。