KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
04 · 停机窗口 — keel 龙骨
类型:主题概念 · 应用课对应:06 章 · 升级与回滚:要么升上去,要么原样退回来
类型:主题概念 · 应用课对应:06 章 · 升级与回滚:要么升上去,要么原样退回来
这一页只回答四件事:它为什么会出现、它是什么、它长什么样、它怎么工作。它不讲信号在内核里是怎么传递的。
一、它为什么会出现
worker 手上有一个要跑 20 秒的任务。这时一条停止命令进来了。
两种结局差在一个参数上:
窗口 > 任务耗时:任务跑完 → done
窗口 < 任务耗时:窗口用完 → 进程被杀 → 那一行永远停在 running
第二种结局最难受的地方在于它不报错。客户看到的现象是"有个任务卡住了",而重启 worker 也救不回来——新 worker 只领 pending 的任务。
需求一句话:从"进程随时可以被杀死"到"手上的活不能丢",中间需要一个明确的时长。
二、它是什么
一句话:停机窗口(stop_grace_period)是收到 SIGTERM 之后、被 SIGKILL 之前,留给进程的那段时长。
它不是下面这三样东西:
- 不是一个"礼貌等待"。 它是一条硬边界:窗口一用完就强杀,进程没有写日志、改数据库、留下遗言的机会。
- 不是一个经验数字。 它必须由手上那个任务的最长耗时决定,而不是由"一般设 30 秒"这类经验决定。
- 不是一个制品的属性。 同一个镜像要扮几种角色,而几种"停得干净"的定义不一样,所以它住在编排文件里,一个服务一套。
三、它长什么样
它写在编排文件里,三个服务三个值:
db: stop_grace_period: 30s
web: stop_grace_period: 15s
worker: stop_grace_period: ${BOARD_WORKER_STOP_GRACE:-60s}
worker 那一行是环境变量,因为它必须和现场的任务耗时对齐:
BOARD_JOB_SECONDS=20 # 一个任务要跑多久
BOARD_WORKER_STOP_GRACE=60s # 停它之前愿意等多久
窗口够用时,worker 的日志是完整的四行:
{"event": "job.started", "id": 1, "title": "停机窗口实验"}
{"event": "shutdown.requested", "signal": "SIGTERM", "in_flight": 1}
{"event": "job.done", "id": 1, "seconds": 20.03}
{"event": "worker.stopped", "in_flight": null}
把窗口改到小于任务耗时,日志就断在第二行:没有 job.done,也没有 worker.stopped。
四、它怎么工作
一次干净停机,四步:
① 送到 停止命令给容器里的 1 号进程发 SIGTERM,窗口开始计时
↓
② 接到 进程把信号翻译成"不再领新任务",手上那一个继续跑
↓
③ 收尾 手上的活干完、写回结果,然后自己退出
↓
④ 兜底 到这一步还没退,窗口用完就强杀 —— 结果只能靠"孤儿回收"事后收拾
第 ④ 步解释了为什么"窗口调对"不是全部:容器被 OOM 杀、机器断电,这些都不看窗口。所以这套系统还有第二道防线——重启时把"多久没心跳"的 running 任务放回 pending。第一道防住常规停机,第二道兜住异常终止。
五、它会带出哪些概念
- 迁移与结构版本:那道"孤儿回收"的兜底靠的正是结构里新增的一列
claimed_at。→ 03 页 - 实例:同一个参数在两套实例上取不同的值,就能造出两种不同行为的系统。→ 02 页
- 交付包:这个值是现场参数、不是制品参数,所以它出现在包外的配置里。→ 00 页
六、回到应用课
| 应用课里的那句话 | 现在你知道它为什么成立 |
|---|---|
"stop_grace_period 的语义只有一句:SIGTERM 之后等多久才 SIGKILL" |
所以它只有一个正确的取值方向:不小于手上那个任务的最长耗时 |
| "设得比任务短:手上的活去哪了?没人知道" | 强杀不给进程任何机会,悬空状态只能由"孤儿回收"事后收拾 |
| "它是现场参数,不是制品参数" | 任务耗时是现场的,所以这个窗口也必须是现场的——同一个镜像几种角色用几个值 |
| "「窗口对齐」和「孤儿回收」是同一件事的两道防线" | 前者管常规停机,后者管异常终止;只看其中一道,就会在另一道上翻车 |
这一页改变了你的哪个判断:定这个数的时候,你不再问"一般设多少",而是先问**"这个角色手上最长的一个活要跑多久"**——先量活,再定窗口,不是反过来。
→ 这一页的展开在应用课 06 章 · 升级与回滚:要么升上去,要么原样退回来。
自测题
- 停机窗口设得比任务耗时短,那行数据会停在什么状态?为什么重启也回收不了它?
- 为什么同一个镜像的三个角色要配三个不同的停机窗口?