跳转到内容
类型解锁
阶段船队
难度中等
工作多租户 SaaS
用于开发团队
用于后端开发者
服务Cron
服务容器
为何选 Hoody容器经济学
为何选 Hoody裸金属隔离
为何选 HoodyHTTP 原生
行业SaaS
类型解锁
阶段船队
难度中等
工作多租户 SaaS
用于开发团队
用于后端开发者
服务Cron
服务容器
为何选 Hoody容器经济学
为何选 Hoody裸金属隔离
为何选 HoodyHTTP 原生
行业SaaS
类型解锁
阶段船队
难度中等
工作多租户 SaaS
用于开发团队
用于后端开发者
服务Cron
服务容器
为何选 Hoody容器经济学
为何选 Hoody裸金属隔离
为何选 HoodyHTTP 原生
行业SaaS
类型解锁
阶段船队
难度中等
工作多租户 SaaS
用于开发团队
用于后端开发者
服务Cron
服务容器
为何选 Hoody容器经济学
为何选 Hoody裸金属隔离
为何选 HoodyHTTP 原生
行业SaaS
多租户 SaaS / 按客户调度

每个客户自动获得独立的 crontab

你的 SaaS 让每个客户自定义自己的报表生成。朴素做法是一个共享调度器,客户 ID 塞在任务负载里,然后祈祷谁也别饿死谁。Hoody 的做法给每个租户独立的容器和独立的 hoody-cron 服务。

阅读文档

三个生命周期状态,一个 HTTP API。PROVISION 添加条目、cron 定时执行、DELETE 挂起。每个租户的 cron 都在自己的容器里——没有共享队列、没有吵闹邻居的风险。

一次 PUT 调用就能为整个租户的 crontab 配齐

每个客户容器都暴露 hoody-cron 的 HTTP API。要设置他们的调度,只需用一次 PUT 替换整张 crontab。无共享队列,无优先级车道,无需重新部署调度器配置。

POST 一条受管条目——创建一个带 UUID、enabled 状态和人类可读 schedule_human 字段的 cron 任务POST /开通
request
# POST managed entry for acme-corp tenant
POST acme-cron.hoody.com/users/root/entries
Content-Type: application/json

{
  "schedule": "0 9 * * *",
  "command": "/usr/local/bin/digest.sh",
  "comment": "daily digest",
  "enabled": true
}
response
HTTP/1.1 201 Created
Content-Type: application/json

{
  "id": "7d3f2a1b-8c4e-4f9a-b2d5",
  "schedule": "0 9 * * *",
  "schedule_human": "At 09:00",
  "enabled": true,
  "user": "root"
}
201 Created。返回 Entry ID 供后续 PATCH 或 DELETE 使用。schedule_human 确认表达式已正确解析。

该端点原子地替换整张 crontab。被移除的条目会回报给你,以便控制平面审计漂移。每个租户的 cron 都活在自己的容器里;acme-corp 的调度对 globex-saas 完全不可见,一个容器里跑飞的任务也无法饿死另一个容器里的调度器。

容器绑定的 crontab 解锁了什么

三项特性是从这种设计里白送的——因为隔离是底质,而不是你写出来的功能。

集群计费拆解单租户成本 = 服务器费用 ÷ 租户数
你的租户
acme-corpSM
globex-saasMD
initech-incLG
+ 还有 57 个
包月服务器$29一个裸金属节点。60 个容器。账单始终不变。
÷租户
单租户成本<$0.49租户越多,单价越低

再不用担心吵闹的邻居

initech-inc 的 scrape.js 卡住了,acme-corp 9 点的摘要依然准点发出。不同 crontab、不同进程树、不同文件系统。

调度变更即时生效

POST 一条新条目,租户的 hoody-cron 服务马上就接住。不用 reload 中央调度器、不用广播。

一个容器,一份租户日志

globex-saas 问为什么 6 点的 rollup 跑了两次时,你只需要看一个容器的日志——而不是 grep 九台机器上的共享调度器。

共享调度器 vs 容器绑定的 crontab

三个轴线上,旧设计向你的团队收税,而 Hoody 的设计根本不收。

维度共享调度器容器绑定
隔离
job payload 中的 tenant_id一行坏数据,所有租户的队列都阻塞
Separate /etc/crontab per container卡死永远是局部的
配给
INSERT INTO scheduled_jobs迁移耦合,schema 锁
PUT /users/root/crontab一次 HTTP 调用,原子替换
审计
grep tenant_id=42 logs/*9 台机器,每台一个日志文件
GET ctr_8a3f1c/cron/log一个容器,一份日志,一个真相

旧栏是每个团队第一次做多租户调度时都会写的东西。新栏是当平台默认给每个租户独立容器时,你顺手就发出来的东西。

边界处的容量

当每个客户都有自己的 crontab 时,一台裸金属 Hoody 服务器能做到什么。

  1. 每台机器的租户数60

    一台裸金属节点上跑六十个客户容器,每个都有自己的 hoody-cron 服务。无共享调度器形成瓶颈。

  2. 调度传播<1s

    从 PUT 请求到新调度的第一次 tick,在典型 64 核节点的 60 个容器上观察到。

  3. 跨租户队列0

    字面意义上没有共享队列、优先级车道或调度器线程让两个租户去抢。隔离就是底质。

容量数字为 64 核 / 256GB 裸金属节点上、采用标准 Hoody 容器密度时的典型观测值。实际容量取决于每租户的 CPU / 内存预算以及每个 cron 任务的工作量。跨租户队列的零是结构性的,不是基准测试。

一个客户的 cron 饿不死另一个,因为它们根本不在同一份 crontab 上。

之前 / 共享调度器之后 / 容器绑定
共享scheduled_jobs WHERE tenant_id = 42一张人人都读的表里的一行
按租户PUT acme-cron.hoody.com/users/root/crontab一次 HTTP 调用,一个容器,一份 crontab
阅读 cron API

它替代了什么

团队为了在多个租户间共享一份 crontab 而搭建的种种架构。Hoody 把每个租户放进自己的 crontab——没有路由,没有公平队列,没有吵闹邻居。

  • 共享多租户 crontab一条糟糕的正则,饿死 400 个客户
  • 自定义租户隔离每行都带 tenant_id 的调度器
  • Postgres pg_cron数据库绑定;一次升级所有人崩
  • 带过滤器的 Quartz scheduler每个区域一个 JVM 加一个分片队列
  • Sidekiq 租户队列12 个队列,12 份配置文件
  • Kubernetes 按租户 CronJob一个 namespace、一个 RBAC role、一份 YAML、一个传呼

别再到处写 tenant_id 了。给每个客户自己的容器,让 cron 在隔离里做它一直在做的事。

阅读文档

阅读其他内容