KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
课程设计说明 · 私有化交付工程 — keel 龙骨
私有化交付工程:把一套系统送进客户内网 的参考信息:课程设计说明 · 私有化交付工程
为什么主线是「一次交付的落地」而不是「一堆运维命令」
交付这一层的知识,天然长成两张网:一张是命令网(save / load / up / stop / exec / ps),
一张是约束网(什么必须在现场之前准备好、什么必须在现场才能产生、失败时能退回哪里)。
按命令开课的结果,读者会敲完一整套命令却答不出一个判断:「为什么这里的失败必须停下而不许继续」、
「为什么回滚的时候不能用旧镜像」。这正是本课要解决的读者卡点——命令都会,出事时不知道该先看什么。
所以本课把主线定成一次交付的落地:从我们这边打出包开始,到客户机房跑起来、升级、回滚、报障为止,
每章在这条链路上加一段约束,加完就破坏一次、看它从哪里断。
01 拆链路:这条链路和普通部署差在哪一段 (站在链路外面看)
02 看制品:要搬的到底是什么,为什么这么重
03 搬运:跨过一段没有网络的路
04 落地:现场五步,每一步失败时已经发生了什么
05 并存:同一份镜像怎么给多个客户各起一套
06 演进:升级与回滚,怎么让结局只有两种
07 收口:把能自己查的交出去,并说清不做什么
判断力的落点在 04、06、07 三章:落地顺序、可逆性、排障与边界。02、03、05 是它们的证据基础,不是终点。
目标能力(读者学完能做什么)
| 能力 | 判断依据 |
|---|---|
| 拆链路 | 拿到一个「要送进内网」的需求,能说出这条链路上哪几段与普通部署不同,以及不同的原因 |
| 读制品 | 给出一个镜像,能读出它的构成、哪几层是必须搬的、以及哪些东西是「故意不写进去」的 |
| 定搬运 | 能说清「现场拉镜像」为什么走不通,并给出正确的搬运动作与它的三条命名约定 |
| 排顺序 | 能列出一次安装的步骤,并对每一步说出「它失败时现场已经发生了什么」 |
| 做隔离 | 能给出一套代码装多个客户的最小隔离集(哪几个变量必须不同、哪些名字会撞车) |
| 保证可逆 | 能设计一次升级,使它的结局只有「升上去」与「原样退回」;能说出回滚时执行迁移的镜像该取哪一版 |
| 现场排障 | 客户报障时按「哪一层断了」的顺序定位,而不是逐个配置项试 |
| 定边界 | 能说出这套形态明确不解决什么,以及不解决的部分该交给哪里 |
评估证据(每章靠什么证明学会)
本章的「证据」都要求可观察,不要求读者同意课程的判断:
| 章 | 可观察的验收物 |
|---|---|
| 01 | 解开交付包,指着清单说出哪两样丢了要重来、哪几样随时能再生;能说出五步里哪一步失败会留下半成品 |
| 02 | 读出镜像的 User / Cmd / Healthcheck 三个值,并说出每一个「为什么是这样」;能说出「压到 180MB 以下该动哪一层」 |
| 03 | 复现一次 No such image,对比加与不加 --pull never 两种输出,说出它们为什么指向不同的下一步 |
| 04 | 分别制造「缺 .env」与「端口被占」两种失败,说出这两次现场的清理成本差在哪里 |
| 05 | 让实例把配置读出来,指出来源列里哪些是 env;说出「实例」由哪三样构成 |
| 06 | 用 BOARD_JOB_SECONDS 与 BOARD_WORKER_STOP_GRACE 做一次正反对照,说出任务为什么停在 running;说出回滚时该用哪一版镜像跑下行迁移 |
| 07 | 停掉数据库,观察 healthz 是 200 而 readyz 是 503,并说出这两个端点为什么必须分开 |
课程内的自测题只考察因果(为什么、漏了会怎样),不考察命令记忆。
教学替身与生产替换点
| 教学替身(本课/配套项目) | 真实依赖 | 不换会出什么事 |
|---|---|---|
| 交付包靠 U 盘搬运 | 现场选定的搬运通道(摆渡机 / 内网文件服务 / 刻录介质)+ 固定校验动作 | 160M 的包在多次中转里被截断,而中途没人校验 |
pg_dump 备份落在本地目录 |
备份落到独立存储,并做恢复演练 | 备份和数据库同盘,盘一坏两样一起没 |
| 单机单实例 | 按现场评估高可用需求(形态随之改变) | 机器一坏整套停摆,没有接管路径 |
密码用 /dev/urandom 现场生成 |
接密钥管理系统或按流程登记 | 密码只存在于那台机器上,交接与轮换无记录可查 |
| 自检结论打印在屏幕上 | 退出码接进监控,按周期跑 | 故障只在有人手动跑脚本时被发现 |
手工改 .env 分配实例名与端口 |
实例登记表 / 分配系统,装机前查一次冲突 | 第二个客户装进来时接管第一个客户的数据卷 |
| 单架构镜像 | 按客户机器 CPU 架构分别构建 | load 成功、一跑就 exec format error |
| 时区靠容器内 UTC | 统一时区口径,或在文档里写明日志是 UTC | 「时间对不上」造成的假因果反复消耗排障时间 |
docker compose stop 的宽限期 |
现场停机窗口与业务排期一起定 | 窗口小于任务耗时,出现永远停在处理中的任务 |
与相邻课程的分工
| 相邻课程 | 本课只负责 |
|---|---|
| 《构建打包与部署上线》 | 那边负责制品是什么、部署策略、发布回路、交付形态怎么选;本课从「已经选定私有化」之后开始,只讲执行 |
| 《Docker 应用课》 | 那边负责 Dockerfile 语法、层与缓存、compose 基础、信号与停机机制;本课只讲面向交付的那部分约束与后果 |
| 《CI 流水线与质量门禁》 | 那边负责流水线、门禁、供应链;本课从「制品已经打好了」开始 |
| 《Kubernetes 应用课》 | 那边负责声明式、探针、资源预算、滚动更新;本课只用 compose 这一层的原语,因为私有化现场多数不装 K8s |
防止课程变成玩具的标准
每条结论都要能被一次故意破坏验证,做不到的结论不写进正文:
- 「解析期失败不产生副作用」——把
.env移走,容器数必须是 0; - 「制品校验拦在副作用之前」——改包里任意一个文件,中止必须发生在任何安装动作之前;
- 「停机窗口必须大于任务耗时」——把窗口改到小于任务耗时,任务必须停在
running且重启不回收; - 「孤儿回收能救回被砍的任务」——把判定时间调小后重启,必须出现
orphans.reaped并最终done; - 「回滚必须用认识新结构的镜像」——用旧镜像执行
--to N,必须复现「退出码 0、什么都没做」; - 「迁移失败是停在中间而不是什么都没发生」——坏迁移必须留下
migrate.done from 3 to 3; - 「
internal网络真的没有出口」——在只有data网络的容器里出网,必须是内核拒绝而不是超时; - 「
healthz不受数据库影响」——停掉数据库后healthz必须仍是 200,且容器不被重启。
这些断言各自对应 course/project/ 里的一段真实代码或一个脚本分支;正文引用它们时给的是读数的形状,
不是要求读者抄下某个具体数字。