跳转到内容
use-cases / crontab-per-branch / hero
CRON · GIT · 每分支调度

每个分支一份 crontab,与代码一起部署

把 `.hoody/crontab` 签入仓库,与任务脚本放在一起。当部署脚本为 `main`、`feature/billing-v2` 或任何预览分支拉起容器时,它会把这份文件 PUT 到新容器的 Cron API。调度随分支一起发布——分支消失时,调度也跟着消失。

阅读 Cron API
use-cases / crontab-per-branch / mechanism

`.hoody/crontab` 如何变成真正的调度

每个分支容器都运行 Hoody Cron。部署脚本读取签入的 crontab,把它 PUT 到新容器的 raw-crontab 端点。容器按文件描述运行调度——多一行没有,少一行也没有。

deploy.sh · 由 CI 推送
shell · 客户端
#!/bin/sh
# 为这个分支创建一个全新容器。
BRANCH=$(git branch --show-current)
CTR=$(hoody containers create --from main-snapshot)

# 用仓库里的 crontab 替换容器的 crontab。
curl -X PUT --data-binary @.hoody/crontab \
  -H "Content-Type: text/plain" \
  https://$CTR-cron-1.hoody.com/users/root/crontab

# 完成。分支的调度存活在它自己的容器里。
echo "deployed $BRANCH → $CTR"
PUT /users/root/crontab
cron · 服务端
# Hoody Cron raw-crontab 端点——原子地替换整个文件。
PUT /users/root/crontab HTTP/1.1
Host: ctr_4d72b9-cron-1.hoody.com
Content-Type: text/plain

0 2 * * * /srv/jobs/billing-rollup-v2.sh
*/15 * * * * /srv/jobs/sync-stripe.py
@hourly curl -fsS http://localhost/healthz
*/5 * * * * /srv/jobs/diff-v1-v2.sh

HTTP/1.1 200 OK
# 200 OK:cron 守护进程重新加载,不到一秒调度生效。

crontab 是分支随身携带的数据,而不是 cron 服务器需要记住的状态。删掉容器,没有任何条目要清理——文件随磁盘一起消失。

use-cases / crontab-per-branch / powers

三件你不再需要做的事

调度变成仓库里的一个文件后,三类工作就消失了。

版本管理

调度和代码走同一份 diff

当你把 `billing-rollup.sh` 改成 v2,新的调度落在同一个 PR 里。审核者直接在脚本旁边看到 cron 行。回滚一个 commit,调度也跟着回滚。

拆除

删除分支,cron 一并消失

分支容器是临时的。当你合并或关闭分支时,容器被拆除。crontab 就在容器内,所以调度无需清理工——没有共享 cron 服务器留着过期条目。

隔离

实验任务无法在 staging 里触发

`experiment/llm-rollups` 上每小时跑一次的实验任务跑在自己的容器、自己的文件系统里。staging 的 cron 守护进程看不到它;生产的 cron 守护进程也看不到它。任务脚本里不再需要 `if BRANCH_ENV` 分支。

use-cases / crontab-per-branch / compare

共享 crontab vs 分支 crontab

标准的「一份运维管理的 crontab」模式和分支绑定模式在两个方向上失败。同样一份任务,影响半径完全不同。

维度共享 CRONTAB分支 CRONTAB
唯一来源
ops 文档 + ansible 角色调度和脚本不在同一个仓库
仓库里的 .hoody/crontab就在被 cron 行调用的脚本旁边
新增任务
合并代码 → 联系 ops → SSH 到 cron 主机两个系统必须人工同步
改文件、推送、部署一次 diff、一次合并、一次部署
分支隔离
if [ "$ENV" = staging ]; then …每个任务都要懂所有环境
每个分支一个容器脚本里不再有环境判断
清理
记得删掉那一行过期条目能堆积好几年
分支删除 = cron 删除文件系统没了,调度也没了
实验
只有一份生产 crontab任何测试都可能在生产环境触发
试验分支 = 试验 crontab只在自己的容器里触发

差别不是某个功能——而是调度存放在哪里。是分支随身携带的一个文件,还是分支借用的共享表里的一行。

use-cases / crontab-per-branch / capacity

Hoody Cron 实际给你的能力

每容器的 Cron 是一个真正的 REST 接口——三组端点家族,标准 cron 语法,完整的按用户隔离。数字来自 Cron API 规范,不是凭空的基准。

  1. 每容器一份 CRONTAB1

    每个分支容器都有自己按用户的 crontab。整文件 PUT、整文件 GET、原子替换。背后没有共享的调度表。

  2. 端点家族3

    原始 crontab(GET/PUT)、托管条目(POST/PATCH/DELETE,带 UUID 和 `expires_at`)、按用户列表。部署脚本想用哪个就用哪个。

  3. 字段 cron 表达式5

    标准 `min hour day month dow`,再加上宏:`@hourly`、`@daily`、`@weekly`、`@monthly`、`@yearly`。和 `.hoody/crontab` 现在用的语法一样。

依据 Hoody Cron API:每个容器的 cron 服务 URL 上提供 GET/PUT /users/[user]/crontab 与 POST/PATCH/DELETE /users/[user]/entries。

use-cases / crontab-per-branch / punchline

调度就活在它要执行的代码旁边,在同一个容器、同一个分支里。

之前 · 两个系统要记住之后 · 仓库里一份文件
你过去拥有的调度在 ops/ansible · 代码在 app/ · 两边永远对不上合并 PR 之后,再开个工单去更新 cron 主机
你现在拥有的PUT @.hoody/crontab → cron-1.[branch].hoody.com一次 PUT、一个容器、一份调度、一个分支
阅读 Cron API
use-cases / crontab-per-branch / replaces

它替代了什么

cron 调度过去藏过的六个地方,没有一个挨着代码。分支绑定的 crontab 让它们全部多余。

  • 共享生产 crontab每个团队都得围绕一台 cron 主机上的一份文件协调
  • 手动同步 cron 配置Ansible 角色 / Puppet manifest 在你的代码合并之外被单独应用
  • 钉死在 main 上的 GitHub Actions 调度调度绑死在默认分支,对功能开发和预览分支不可见
  • 「合并时记得改 cron」靠人盯的 checklist——这是你与过期条目之间唯一的屏障
  • 独立的 cron 配置仓库另一个仓库,唯一的工作就是落后于真正放代码的那个
  • Atlas/Liquibase 计划迁移迁移工具被迫做调度的活,因为没有更好的地方放它
use-cases / crontab-per-branch / cta

别再跨系统同步调度了。把 crontab 签进仓库。让分支带着它走。

阅读 Cron 文档
use-cases / crontab-per-branch / related

阅读其他内容