先说我们那条 38 分钟的流水线
2021 年我接手的第一个项目,Jenkins 上跑着一个 620 行的 Groovy 脚本,一次完整流水线 38 分钟。我拿 Stage View 掐过表:npm ci 占 6 分半,单元测试 9 分钟,还有 12 分钟是卡在"等待测试环境释放"——一共 3 台测试机,11 个开发共用一个队列。
那时候团队管这个叫持续交付。但真实情况是,每次上线前三天产品经理会在群里发一个 Excel,让大家填"本周要发的需求",然后挑一个周三晚上 8 点,集体待命,一个人点构建按钮,其他人在群里刷"绿了没"。
我们花了一个季度把它优化到 9 分钟。第一周大家都很爽。第三周出了个事:一个改了缓存 key 前缀的提交在 9 分钟里跑完全部测试进了生产,把购物车数据全打到了旧 key 上,线上撑了 47 分钟才有人发现。回滚花了 22 分钟,因为那个版本还带了一个数据库字段变更,回滚脚本没写。
这段我想说的是:大多数团队缺的不是自动化,是回滚能力。你把流水线从 38 分钟压到 8 分钟,事故率不会下降,反而可能上升,因为你只是把"引入故障"的速度提高了 4 倍。
CI 的绿灯和 CD 的绿灯根本不是一回事
CI 的绿灯判定条件很明确:代码能编译,单元测试全过,lint 不报错。它衡量的是代码在受控环境里的自洽性。
CD 的绿灯是另一套东西:这次发布进生产之后,下单成功率、支付回调延迟、首页首屏时间,有没有掉出基线。这两个判定条件几乎不重叠。拿 CI 的绿灯去保证 CD,相当于拿科目一的分数去估科目三。
DORA 从 2014 年发第一份报告开始,到 2018 年那版把指标收敛成四个:部署频率、变更前置时间、变更失败率、服务恢复时间。前两个是速度,后两个是稳定性。2018 年报告里精英表现者是按需部署(一天多次)、前置时间小于 1 小时、变更失败率 0-15%、服务恢复小于 1 小时。
这里有个设计意图经常被忽略:DORA 从来没把部署频率当成目标,它只是个观测值。但不少团队(包括我待过的两家)直接拿它当 KPI 考核,后果就是周五下午也要发版,出问题了不敢回滚,因为回滚等于本周数据难看。我见过一个团队甚至规定回滚也要走一次完整流水线,回滚耗时比发布还长。这就本末倒置了。
真正决定能不能持续交付的是"服务恢复时间"。逻辑不复杂:高频部署这个策略成立的前提,是任何一次变更出问题都能在几分钟内退回去。如果回滚要 30 分钟起步,高频部署只是把风险敞口拉大了而已。
回滚卡住的三个地方
第一个是数据库。我踩过最典型的一次是给用户表加 status 字段,alter table 在 2000 万行的表上跑了 6 分多钟,期间应用还在写。正确做法是线上应用不下线,用扩展—收缩模式:先加列(允许 null),代码双写新旧字段,用后台任务回填历史数据,确认读取逻辑切过去之后再删旧列。一个字段走完大概要跨 3 次发布,听起来慢,但它让每一次发布都独立可回滚。
第二个是配置。配置漂移这东西平时看不出来,出事了才知道要命。有个项目的生产环境有两台机器,nginx 配置是 2020 年某次紧急修复时手工改的,没进仓库。后来做蓝绿部署,新环境照着仓库里的配置起,流量切过去之后有一批接口开始 502。那次事后我们把所有机器配置收进了 Ansible,跑了一个月的 diff 巡检,才把 17 处漂移清干净。
第三个是 feature flag。flag 本身是好事,它让你把"发布"和"放量"拆开,这是持续交付里最值钱的一个概念。但 flag 的生命周期没人管就是灾难。我见过一个后台存了 400 多个 flag,其中三分之一没人知道是干什么的。后来清理时关掉一个 2019 年的老 flag,直接导致结账流程掉了一步——那步逻辑被之前另一版代码接手了,没人记得关联关系。所以 flag 必须带 owner、创建时间、预期下线时间,上线一个月之后没转成配置的一律删。
两个我常拿来给别人讲的例子
一个是 Knight Capital。2012 年 8 月 1 日,他们上线了新的做市代码,8 台服务器里 7 台部署成功,第 8 台还是旧版本。结果那段本该废弃的死代码被重新激活,45 分钟亏了 4.4 亿美元,公司后来被收购。这个例子的重点不是代码写错了,是部署一致性没保证——只要有一台机器没跟上,整个集群的假设就崩了。
另一个是 GitLab 2017 年 1 月 31 日的数据库事件。运维在疲惫状态下误删了主库数据目录,5 个备份机制里 4 个没起作用,最后从 6 小时前的 LVM 快照恢复,丢了大概 6 小时的写入数据。事后他们直播复盘,把整个流程贴到了 YouTube 上。这件事让我改了一个习惯:备份验证不能只写脚本,得定期真的恢复一次。我们现在每季度做一次恢复演练,从备份把生产数据恢复到隔离环境,跑一遍冒烟测试,记录耗时。上一次的记录是 4 小时 12 分钟,离"几分钟退回去"还差得远,但至少心里有数了。
所以我现在怎么判断一个团队的 CD 成熟度
不看流水线有多少 stage,不看用了 Argo CD 还是 Flux,我就问三个问题。
回滚一个版本需要多久?如果回答超过 10 分钟,或者需要"申请"或者"走流程",那这团队的持续交付还没入门。
数据库变更能不能独立于代码发布回滚?如果每次回滚都要跟着跑一个逆向 SQL,那发布频率越高越危险。
生产环境的实例,是不是保证跑同一个版本?如果有一台机器是"上次没重启所以还是老版本",那就等于埋了一颗雷。
这三个问题答完,基本能判断这个团队是在做持续交付,还是在做"不持续的交付"。我个人不太信一套流水线能解决所有问题,工具永远只是把人本来就该想清楚的事情变得不容易忘掉。