KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

私有化交付工程 · 课程导读 — keel 龙骨

私有化交付工程:把一套系统送进客户内网 的参考信息:私有化交付工程 · 课程导读

这门课不站在「把一套系统跑起来」的起点上,而是站在**「它要跑在一个你进不去、也不该进去的地方」**的起点上。

私有化交付有个特点:顺的时候看不出它和普通部署有什么区别。 安装脚本跑完、屏幕上打出一行「全部通过」,
你可能觉得「不就是多写了个脚本」。只有它不顺的时候,那些刻意的设计才一条一条显形——
包里的制品少了一个、密码生成在交接里丢了、停机的时候有个任务永远停在处理中、回滚之后结构和代码对不上。

七章的顺序是刻意排的:

先看这条链路和普通部署差在哪(01 全景)
  → 看清楚要搬的到底是什么(02 一个自足的镜像)
  → 把它搬到没有出口的地方(03 离线搬运)
  → 在现场按固定顺序落地(04 从压缩包到跑起来)
  → 一台机器上并存多个客户(05 配置注入与多实例)
  → 让升级只有两种结局(06 升级与回滚)
  → 把能自己查的那部分交出去,并说清边界(07 现场排障)

你现在的起点

先花一分钟对一下。下面五条你都能说清,这门课的前两章可以直接跳读:

其中任何一条答不上来,都属于这门课的读者面。

主案例:一个被交付的最小看板

全课用一个系统反复演进,不换例子。它叫 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 现场排障与边界 客户说「不对」时,谁先能自己查出答案? 一棵排障决策树 + 明确不解决的四件事

你会反复用到的三种「看」

  1. 看制品:docker image inspect / docker save / docker load。
    「这个镜像里到底有什么」是一个必须能读出来的问题,而不是一句「应该没问题吧」。
  2. 看解析:docker compose config --variables / --services / --format json。
    它只做客户端解析、不需要 daemon,所以它是现场唯一能「不碰生产就先验一遍」的动作。
    而且归一化会暴露 YAML 里看不出的东西——60s 变成 1m0s,512M 变成 536870912。
  3. 看状态:docker compose ps / board doctor / healthz 与 readyz。
    这三个各自回答一个不同的问题:「容器在不在」「这个镜像在当前现场能不能干活」「进程活着吗 / 它真能干活吗」。

三个必须先纠偏的认知

  1. 私有化交付不是「部署到更远的一台机器」。 部署的前提是那张网还在,交付的前提是它不在了。
    这条差别决定了镜像里放什么、包里有什么、脚本为什么不能问问题。
  2. Exited 0 常常是好消息。 migrate 是一次性进程,它的成功形态就是「退出码 0 且不在运行」。
    把「有容器 exited」当成故障,是现场最常见的一次误判。
  3. 「命令成功」不等于「它做了那件事」。 回滚时如果用了不认识新结构的旧镜像,
    --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 章那组正反对照去制造一次「被砍在中间的任务」
。

读完之后

同一主题下还有面向「第一次跑起来」的入门线与「名词从哪来」的概念页;本课不重复它们的内容,
只在概念第一次出现时给一句够做判断的定义。

前置要求

进入 keel 阅读