KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
01 · 交付流水线的第一性原理 — keel 龙骨
这一章回答一个被问过很多次但很少答到点上的问题:为什么要有一条流水线?答案不是"自动化",而是"把交付这件事的真相,固定成一个每次都不打折的反馈回路"。
这一章回答一个被问过很多次但很少答到点上的问题:为什么要有一条流水线?答案不是"自动化",而是"把交付这件事的真相,固定成一个每次都不打折的反馈回路"。
人肉发布的真正代价不是"慢",而是每次发布的路径都不一样。周三上线是小王手动 scp 的,周五上线是小李用另一个脚本跑的,两次差距没人知道。当没有人能回答"这次上线到底改了什么、跑了哪些检查、产物是从哪一份代码来的",故障复盘就只能停在"下次注意"。
一、流水线到底替你解决了四件事
| 作用 | 没有流水线时的样子 | 有流水线之后 |
|---|---|---|
| 分批集成 | 代码在本地攒一周,合并时一次冲突炸一片 | 每个 PR 都是一次小集成,冲突跟着变小 |
| 稳定的反馈回路 | "在我机器上是好的",这句话无法验证也无法复现 | 同一份输入在同一环境下被同样地处理,结果可复现 |
| 唯一可信产物 | 谁也说不清生产上跑的是哪次构建出来的包 | 产物有版本号、有来源提交、有构建记录,可校验 |
| 把规范变成可执行的东西 | Code Review 里反复强调"记得跑类型检查" | 不跑类型检查这个 PR 就是红的,没有商量余地 |
第四条是容易被低估的:团队规范的载体应该是流水线,不是文档。 文档会被绕过,流水线不会。所谓"工程文化",落到地面上就是"哪些红线是一跑 CI 就不能过"。
二、四条演进阶梯:知道自己站在哪一级
第 0 级 · 人肉 本地打包 → scp/上传 → 手动改配置 → 祈祷
痛点:每次路径不同,回滚靠记忆
▼
第 1 级 · 脚本化 build.sh / deploy.sh 固化步骤
痛点:脚本在谁机器上跑就是谁的环境,依赖版本不受控
▼
第 2 级 · 流水线 每次 push/PR 自动跑同样的检查,产物由流水线生成
痛点:部署还是流水线里的命令式步骤,环境与声明状态容易漂移
▼
第 3 级 · 声明式交付 环境状态写在版本库里,控制器持续对齐(GitOps / IaC)
痛点:多了套需要维护的东西,小规模团队未必划算
诚实原则:级别不是越高越好,而是跟团队规模和故障代价匹配。 三个人的小服务做到第 2 级就够;二十个服务、四个团队共享两套环境时,第 3 级的账才算得过来。
三、一条交付链上的五个对象,别混为一谈
源码 ← Git commit / tag,一切的唯一事实起点
↓ 构建(build):确定性地把源码变成产物
产物 ← wheel / jar / tar 包 / 容器镜像,**不可变**,有版本号
↓ 发布(release):给不可变产物一个可寻址的名字,推到制品库/镜像仓库
发布记录 ← 版本 → 来源 commit → 构建号 → 谁批准的 → 校验值(sha256/digest)
↓ 部署(deploy):把某个具体产物放到某个环境里跑起来
运行环境 ← dev / staging / prod,各环境的差异只有配置,不含产物差异
三条铁律:
- 同一个产物必须能进所有环境——"测试环境重新打包一份"是经典的作死行为,测试通过的那份和上线那份不是同一个字节。
- 版本一旦发布不可变更——正因为不可变,发布流程才可能幂等重跑(第 01 课 02 章会反复用到这条)。
- 配置与产物分离——环境差异进配置,不进代码,也不进镜像。
四、流水线的三条边界(讲不清楚会翻车)
- 流水线不能替你补上测试:它能保证测试被跑过,但不能保证测试有效。覆盖率 80% 全是断言
assert True的用例,比 0% 更有欺骗性。 - 流水线不能证明业务正确:它通过所有门禁,只说明"没有触发我们已知的那些错误"。
- 流水线不能消灭沟通成本:它让"代码能不能合"不需要讨论,但"这个变更值不值得做"依然只能人来判断。
面试时常被追问"有了 CI 是不是就不需要 Code Review 了"——标准回答是:CI 负责消灭"能不能合并"的不确定性,Review 负责判断"该不该这样写"。前者可以被自动化,后者恰恰因为不能被自动化才值钱。
五、你的实践坐标(先把位置摆正)
| 场景 | 实际到达的级别 | 可以说的力度 |
|---|---|---|
| agent-feed(个人开源) | 第 2 级,且发布链做到幂等可重跑 | ✅ 可公开验证的完整实绩(GitHub Actions / PyPI / npm 全公开) |
| E 平台(公司项目) | 第 1 级:Docker compose + Nginx + 脚本化人工窗口 | ⚠️ 只能说"参与过的部署方式",不能说"我搭了 CI" |
| 指挥平台(公司项目) | 第 1 级,脚本 + 日志轮转 + 值班窗口 | ⚠️ 同上 |
这个坐标决定了你的讲述策略:开源侧讲完整闭环(尤其幂等、最小权限、产物冒烟三件事),公司侧讲"我清楚下一级长什么样,我有能力把它落地"。 硬把公司项目说成有 CI/CD,追到"你们用的是 Jenkins 还是 GitLab CI、runner 装在哪"就穿帮。
动手:可观察结果
| 动作 | 产出物 | 判断"做完了"的标准 |
|---|---|---|
| 给自己的任意项目补一条最小 CI | 一个 workflow 文件,push 后自动跑 lint + test | 故意写错一行代码,push 后看到红色 run;改回来再 push,看到绿色 |
| 画出这条链路的五个对象 | 一张图或一段文字:源码→产物→发布记录→环境 | 能回答"生产上现在跑的是哪个 commit 构建的" |
| 找一份人肉发布的旧记录 | 描述当时上线做了哪几步 | 能列出至少 3 个当时没人检查、现在应该进流水线的点 |
故障注入
| 注入方式 | 观察什么 | 应该看到的现象 |
|---|---|---|
| 本地删掉一个被 import 的模块再 push | CI 是否挡住 | 红的 run,出现在测试阶段而不是部署之后 |
| 手动装包跳过 CI 直接发布 | 版本号与来源 commit 是否还能对应 | 对应不上——这就是"没有发布记录"的代价 |
| 把 CI 里的测试步骤注释掉 | PR 是否还能被合并 | 能 → 说明缺分支保护;这是多数团队真实的样子 |
| 在两个环境分别构建 | 产物 hash 是否一致 | 不一致 → 构建非确定性,正在埋雷 |
自测题
- 流水线的四个作用中,哪一个最容易被"只是跑个脚本"的方案假装实现?
- 为什么"测试环境重新构建一份"会破坏交付链条里的哪条铁律?
- 演进四阶梯里,第 2 级和第 3 级的本质区别是什么?(提示:对环境状态的描述方式)
- 面试官问"有 CI 了还要不要 Code Review",你的一句话回答是什么?
- 你的三个项目分别停在第几级?各自能说、不能说的是什么?