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,各环境的差异只有配置,不含产物差异

三条铁律:

  1. 同一个产物必须能进所有环境——"测试环境重新打包一份"是经典的作死行为,测试通过的那份和上线那份不是同一个字节。
  2. 版本一旦发布不可变更——正因为不可变,发布流程才可能幂等重跑(第 01 课 02 章会反复用到这条)。
  3. 配置与产物分离——环境差异进配置,不进代码,也不进镜像。

四、流水线的三条边界(讲不清楚会翻车)

面试时常被追问"有了 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 是否一致 不一致 → 构建非确定性,正在埋雷

自测题

  1. 流水线的四个作用中,哪一个最容易被"只是跑个脚本"的方案假装实现?
  2. 为什么"测试环境重新构建一份"会破坏交付链条里的哪条铁律?
  3. 演进四阶梯里,第 2 级和第 3 级的本质区别是什么?(提示:对环境状态的描述方式)
  4. 面试官问"有 CI 了还要不要 Code Review",你的一句话回答是什么?
  5. 你的三个项目分别停在第几级?各自能说、不能说的是什么?

进入 keel 阅读