KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
私有化交付工程 · 课程导读 — keel 龙骨
私有化交付工程:把一套系统送进客户内网 的参考信息:私有化交付工程 · 课程导读
这门课不站在「把一套系统跑起来」的起点上,而是站在**「它要跑在一个你进不去、也不该进去的地方」**的起点上。
私有化交付有个特点:顺的时候看不出它和普通部署有什么区别。 安装脚本跑完、屏幕上打出一行「全部通过」,
你可能觉得「不就是多写了个脚本」。只有它不顺的时候,那些刻意的设计才一条一条显形——
包里的制品少了一个、密码生成在交接里丢了、停机的时候有个任务永远停在处理中、回滚之后结构和代码对不上。
七章的顺序是刻意排的:
先看这条链路和普通部署差在哪(01 全景)
→ 看清楚要搬的到底是什么(02 一个自足的镜像)
→ 把它搬到没有出口的地方(03 离线搬运)
→ 在现场按固定顺序落地(04 从压缩包到跑起来)
→ 一台机器上并存多个客户(05 配置注入与多实例)
→ 让升级只有两种结局(06 升级与回滚)
→ 把能自己查的那部分交出去,并说清边界(07 现场排障)
你现在的起点
先花一分钟对一下。下面五条你都能说清,这门课的前两章可以直接跳读:
- 会读 Dockerfile 与 compose 文件,能说出一次
docker build大致做了什么; - 手里做过发布,知道「制品」「环境」「部署」大概是三件事;
- 见过「本地是好的、现场不对」这类问题,但排障靠试,没有固定的查看顺序;
- 知道镜像可以
save成文件、load回来,但没想过为什么要在没有外网的机器上做这件事; - 大致知道升级要备份,但说不清「备份失败」和「迁移失败」该退回到哪里。
其中任何一条答不上来,都属于这门课的读者面。
主案例:一个被交付的最小看板
全课用一个系统反复演进,不换例子。它叫 board,是一个最小任务看板:
| 事实 | 值 |
|---|---|
| 一个镜像,五个角色 | web / worker / migrate / config / doctor,靠启动命令区分 |
| 依赖 | 纯标准库 + psycopg[binary]==3.2.9 |
| 两个版本 | 1.0.0 有一个真实缺陷(被 SIGKILL 的任务永远停在 running),1.1.0 修掉它 |
| 库结构 | 三个版本(tasks → priority → claimed_at),每一版都带下行语句 |
1.0.0 与 1.1.0 之间的这条链不是编出来的演示:它正好构成一次真实的升级、一次真实的回滚、
以及一次「回滚时用错了镜像」的坑。06 章的整章都建在它上面。
course/project/ 里这套东西是可运行的:一份 Dockerfile、一份客户内网版 compose.yml、
四个交付脚本、一个打包脚本。
章节地图
| 章节 | 关键问题 | 产物落点 |
|---|---|---|
| 01 为什么私有化交付不一样 | 为什么它不是「多买一台服务器」?一次交付要经过哪几道环节? | 一份交付包的最小形态 + 五步现场流程 |
| 02 一个自足的镜像 | 能送进内网的镜像里装了什么?为什么代码 61.4kB、镜像 215MB? | 一份镜像的重量分解 + 两处「故意不写」的设计 |
| 03 没有外网怎么把制品搬进去 | 「能拉」为什么不能假设?失败为什么是一段等待而不是一句报错? | 一张搬运链路图 + 三条命名约定 |
| 04 从压缩包到跑起来 | 中间隔着几步?顺序错了会留下什么? | 五步顺序表 + 每一步失败时的现场状态 |
| 05 配置注入与多实例 | 一套代码怎么给多个客户各起一套?配置凭什么不漂? | 「实例」的判据 + 一张配置自证输出 |
| 06 升级与回滚 | 怎么让升级只有两种结局?回滚为什么最容易写错? | 停机窗口的正反对照 + 回滚通则 |
| 07 现场排障与边界 | 客户说「不对」时,谁先能自己查出答案? | 一棵排障决策树 + 明确不解决的四件事 |
你会反复用到的三种「看」
- 看制品:
docker image inspect/docker save/docker load。
「这个镜像里到底有什么」是一个必须能读出来的问题,而不是一句「应该没问题吧」。 - 看解析:
docker compose config --variables/--services/--format json。
它只做客户端解析、不需要 daemon,所以它是现场唯一能「不碰生产就先验一遍」的动作。
而且归一化会暴露 YAML 里看不出的东西——60s变成1m0s,512M变成536870912。 - 看状态:
docker compose ps/board doctor/healthz与readyz。
这三个各自回答一个不同的问题:「容器在不在」「这个镜像在当前现场能不能干活」「进程活着吗 / 它真能干活吗」。
三个必须先纠偏的认知
- 私有化交付不是「部署到更远的一台机器」。 部署的前提是那张网还在,交付的前提是它不在了。
这条差别决定了镜像里放什么、包里有什么、脚本为什么不能问问题。 Exited 0常常是好消息。migrate是一次性进程,它的成功形态就是「退出码 0 且不在运行」。
把「有容器 exited」当成故障,是现场最常见的一次误判。- 「命令成功」不等于「它做了那件事」。 回滚时如果用了不认识新结构的旧镜像,
--to N会变成一次退出码为 0 的静默空操作。任何时候一个「应该改了点什么」的命令返回成功、
而现场状态没变,第一件要问的是**「它有没有能力做这件事」**。
配套项目与本课的关系
course/project/ 是一个只用标准库的最小系统,加一整套交付脚本。正文里每一处「这个读数是怎么来的」
都能在它里面找到对应的文件行:
app/board/ 七个源文件:config / log / db / web / worker / __main__
Dockerfile 多阶段,非 root,刻意不带 HEALTHCHECK
deploy/self-hosted/ compose.yml + env.example + install/upgrade/rollback/selfcheck 四个脚本
deploy/build-bundle.sh 打离线包(跑在有外网的那一侧)
deploy/variants/ 初版 1.0.0 的差异文件(让 06 章的升级演练有真实的起点)
学习顺序建议:先读正文建立判断,再按 04 章的顺序真的把包跑起来,
最后用 06 章那组正反对照去制造一次「被砍在中间的任务」。
读完之后
- 「制品是什么、部署策略怎么选、发布回路怎么走」在《构建打包与部署上线》;
- Dockerfile 语法、层与缓存、compose 基础、信号与停机在《Docker 应用课》;
- 「制品怎么被构建、怎么被门禁放行」在《CI 流水线与质量门禁》;
- 如果现场真的装了 K8s,探针、资源预算与滚动更新在《Kubernetes 应用课》
——但记住本课的判断:私有化现场多数不装 K8s。
同一主题下还有面向「第一次跑起来」的入门线与「名词从哪来」的概念页;本课不重复它们的内容,
只在概念第一次出现时给一句够做判断的定义。
前置要求
- 能读懂一份
compose.yml:知道services/depends_on/volumes/networks各自在说什么; - 用过
docker image与docker container这两组命令,能看懂docker ps的输出; - 知道数据库迁移是什么(哪怕只是「改表结构要有顺序」这一层);
- 手边有一个能跑 Docker 与 Docker Compose v2 的环境——本课的命令都能真跑,
但读形状比抄数值重要。