周五下午的红色叉:持续集成技术到底在替谁背锅?
周五下午四点十七分。我点了merge。三分钟后,CI流水线红了一片。群里瞬间安静,然后有人甩了张截图:“谁又把测试环境数据库改了?”——你看,持续集成技术这东西,平时没人夸它,出事的时候它永远是第一个被拉出来审的。
持续集成流水线构建失败红色日志截图
说实话,我第一次看到流水线里跑起完整构建的时候,感觉像给代码请了个全天候门卫。但门卫也有打盹的时候。
持续集成服务器并行构建任务监控面板
还有缓存。Docker层缓存、依赖缓存、编译产物缓存,配好了能省一半时间。但缓存键设计不好,会变成“缓存命中但内容过期”的隐形炸弹。比如你缓存了node_modules,锁文件变了却没失效,构建绿了,跑起来报错。所以缓存要跟着锁文件走,别偷懒。
CI不是质量保险箱
这里得泼盆冷水。持续集成技术能告诉你“这次合并有没有破坏已有测试”,但它不保证“你的系统设计是对的”。测试覆盖率百分之九十,可能全是断言getter/setter。流水线全绿,可能生产环境一上线就雪崩——因为CI环境没模拟真实流量、没模拟网络分区、没模拟数据库慢查询。
我见过最典型的误解:把CI当CD用。CI只管集成和构建,CD(持续交付/部署)管的是发布策略、灰度、回滚。混在一起,流水线会臃肿到没人敢改。还有团队把所有检查塞进一个job,一个lint失败全红,看不出是代码问题还是环境问题。拆开,让失败原因可读,比追求“一条流水线搞定所有”重要得多。
风险也说两句。CI流水线本身是代码,会腐化。脚本里硬编码密码、镜像用latest、构建机权限过大,都是安全债。更隐蔽的是环境漂移:CI用的基础镜像半年没更新,本地开发已经换了两代运行时,某天一个依赖升级,CI上编译不过,本地却好好的——这时候别怀疑人生,先对版本。
先别急着点重新运行
很多人对持续集成(Continuous Integration,简称CI)的理解就一句话:提交代码后自动跑测试。对,但太薄了。它真正干的事,是把“合并”这个动作从手工赌博变成了可重复的流水线。以前三个人各自在本地分支上闷头写两周,最后合并那天像拆盲盒——冲突、编译不过、依赖版本打架,全挤在一起爆。CI的做法很土但有效:你每推一次代码,它就拉一份干净环境,编译、跑测试、打包,告诉你“这次合并会不会炸”。
持续集成流水线构建失败红色日志截图
说实话,我第一次看到流水线里跑起完整构建的时候,感觉像给代码请了个全天候门卫。但门卫也有打盹的时候。
它到底在检查什么
一条典型的CI流水线,粗粒度分四段:拉代码、装依赖、跑测试、出产物。听起来简单,坑全在细节里。比如“装依赖”这一步,你本地有缓存,CI环境是全新的,npm install或者maven clean install一跑,网络抖一下、镜像源抽个风,流水线就挂了。这时候你点“重新运行”,它又绿了——这种随机红绿,比稳定失败更让人抓狂。 再比如测试。单元测试跑得快,但覆盖不了集成路径;端到端测试覆盖全,但慢且脆。我见过一个团队,CI里塞了四百多个Selenium用例,每次构建四十分钟,失败率百分之三十,大家已经麻了,红了先重跑,重跑还红就找借口跳过。持续集成技术最怕的不是失败,是失败变得不可信。一旦“红了不一定有问题”成为共识,整条流水线就退化成摆设。 原理层面,CI的核心是每次提交都触发一次可重复的构建,并且构建环境要尽量接近生产。类比一下:你家的烟雾报警器。它不需要每天真着火才有用,它要的是“有烟就响,没烟不响”。如果它天天误报,你第一反应是拆电池——CI被绕过,也是同一个心理路径。那些让流水线变慢的“隐形税”
CI慢,是另一种慢性病。有人统计过,开发者等构建结果超过十分钟,注意力就开始漂移,切去刷消息、改别的代码,上下文一断,回来又得重新加载。这不是矫情,是认知成本。 常见的慢因:串行跑所有测试、每次全量拉依赖、构建机资源抠门、测试之间抢端口/抢数据库。我经历过一个项目,CI里所有测试共用一个MySQL实例,A测试删了表,B测试刚好在查,随机挂。排查了两天才发现是测试隔离没做好。后来改成每个job起独立容器,构建时间涨了四分钟,但失败率从百分之二十掉到百分之二——用一点时间换确定性,这买卖划算。
持续集成服务器并行构建任务监控面板
还有缓存。Docker层缓存、依赖缓存、编译产物缓存,配好了能省一半时间。但缓存键设计不好,会变成“缓存命中但内容过期”的隐形炸弹。比如你缓存了node_modules,锁文件变了却没失效,构建绿了,跑起来报错。所以缓存要跟着锁文件走,别偷懒。
CI不是质量保险箱
CI不是质量保险箱
这里得泼盆冷水。持续集成技术能告诉你“这次合并有没有破坏已有测试”,但它不保证“你的系统设计是对的”。测试覆盖率百分之九十,可能全是断言getter/setter。流水线全绿,可能生产环境一上线就雪崩——因为CI环境没模拟真实流量、没模拟网络分区、没模拟数据库慢查询。
我见过最典型的误解:把CI当CD用。CI只管集成和构建,CD(持续交付/部署)管的是发布策略、灰度、回滚。混在一起,流水线会臃肿到没人敢改。还有团队把所有检查塞进一个job,一个lint失败全红,看不出是代码问题还是环境问题。拆开,让失败原因可读,比追求“一条流水线搞定所有”重要得多。
风险也说两句。CI流水线本身是代码,会腐化。脚本里硬编码密码、镜像用latest、构建机权限过大,都是安全债。更隐蔽的是环境漂移:CI用的基础镜像半年没更新,本地开发已经换了两代运行时,某天一个依赖升级,CI上编译不过,本地却好好的——这时候别怀疑人生,先对版本。