
一台服务器上运行 60 个容器
一个裸金属服务器运行数十到数百个 Hoody 容器。KSM 和 BTRFS 去重使边际成本接近零。
在清理开始那天 provision 一个托管 cron 条目。把 expires_at 设在最后一次预期运行之后几天。脚本每晚一片片地切完工作,然后在没东西可清时把自己 DELETE 掉。无需日历提醒、无僵尸 crontab、无年度「清理清理者」复盘。
# tail of cleanup-stale-uploads.sh removed=$(find /uploads -mtime +90 -delete -print | wc -l) echo "$removed files removed" # self-aware tail: nothing left → retire if [ -z "$(ls /uploads)" ]; then curl -fsS -X DELETE \ "$CRON_URL/entries/$ENTRY_ID" fi
会自我察觉的尾巴。cron 条目就是它自己脚本的最后一行。
Day 1 清掉 247 个过期文件。Day 7 一个都没清——并对自己的条目触发了 DELETE。
# tail of cleanup-stale-uploads.sh removed=$(find /uploads -mtime +90 -delete -print | wc -l) echo "$removed files removed" # self-aware tail: nothing left → retire if [ -z "$(ls /uploads)" ]; then curl -fsS -X DELETE \ "$CRON_URL/entries/$ENTRY_ID" fi
会自我察觉的尾巴。cron 条目就是它自己脚本的最后一行。
三个离散阶段。每次过渡都是机械的,没有一个需要人来记。条目自己知道何时活儿干完、何时日历位也该结束。
schedule @daily,command 指向切片脚本,expires_at 设在最后一次预期运行之后一点。截止日在条目里——不在 Notion 文档里、不在 Slack 串里。
每次运行删一片过期数据,这样数据库不会被锤。Day 1 可能清 247 个文件,Day 6 只剩 1 个。节奏受真正剩下多少东西约束。
脚本最后的一段检查目标是否已空。如果是,触发 DELETE /entries/[self]。如果出于某种原因没触发,几天后 expires_at 兜底。
两个独立的触发器——脚本自己的检查和 API 的 expires_at——汇合到同一个结果:一行不会活过自身用途的 crontab。
Hoody Cron 是系统 crontab 之上的一个 JSON-CRUD 包装。POST 创建条目,DELETE 移除条目,expires_at 是兜底。每晚跑的那个脚本知道自己什么时候做完——所以由它来调用 DELETE。
# day 0 — provision the cleanup curl -X POST \ https://cron.containers.hoody.com/users/me/entries \ -H "Content-Type: application/json" \ -d '["schedule":"@daily","command":"/srv/jobs/cleanup-stale-uploads.sh","expires_at":"2026-05-05T00:00:00Z"]' # response HTTP/1.1 201 Created { "id":"f3a1", "expires_at":"2026-05-05T00:00:00Z", "enabled":true }
# inside the cron command itself if [ -z "$(ls /uploads)" ]; then curl -X DELETE \ "$CRON_URL/entries/$ENTRY_ID" fi # response HTTP/1.1 204 No Content # entry f3a1 was here. f3a1 deleted itself.
$ENTRY_ID 是 POST 返回的 UUID——脚本可以从条目命令行传入的文件里读到,也可以在运行时从 $HOODY_ENTRY_ID 读。无论哪种,cron 条目都在删除 cron 条目自己。
重要的不是删除本身。重要的是三个月后没人需要记得这些东西的存在。
@daily 每 24 小时一次。脚本删一片过期数据——几千个文件、几千行——然后退出。数据库保持平静;负载曲线看上去像什么都没发生。
expires_at 作为 JSON 在条目里。它一触发,这一行就从系统 crontab 里被移除。三任工程师之后,没人会翻 200 行 crontab 想 cleanup-stale-uploads-v3 还在干啥。
活儿干完那晚脚本 DELETE 掉自己。如果有 bug 跳过了那条路径,几天后 expires_at 让条目退役。两个独立机制;其中一个一定会触发。
每个托管条目就是 API 注入到系统 crontab 里的一行 JSON。扩展受限于 cron 自己能容纳什么,而不是 Hoody。
@daily 是清理的标准节奏。如果你需要更频繁,可以一路用 5 字段表达式直到 * * * * *——分钟级精度。
条目上的一个 ISO-8601 时间戳。一旦过期,API 会在下一次扫描时把这一行移除。清理永远不会赖在自己的截止日之后。
在运行中的 command 内部对 DELETE /users/[user]/entries/[id] 调用之所以可行,是因为 cron 守护进程不锁自己的 crontab——API 安全地把变更扫进来。
标准 5 字段 cron 表达式加上宏(@hourly, @daily, @weekly, @monthly, @yearly)。按用户隔离;每个系统用户拥有自己的 crontab。Hoody Kit 的 Cron 页面同时记录了托管条目和原生 crontab 访问,如果你需要更老的形态。
清理每晚跑,直到要清的东西消失。
凡是清理任务理应自我消失的地方——它替代的是这些模式:
Provision 这次清理。设好它的退役日。然后走开。