TT Lab
开始
学习 学习路径 课程

CI/CD 流水线

红灯并不都是同一种红灯

在 TT Lab 中继续学习

一句话总结

流水线的失败分成基础设施问题、不稳定测试、真正的缺陷三类。不分类的话,人们就会一律用“再跑一遍”来应对,而真正的缺陷就藏在里面。

为什么需要它

失败几天才出现一次,人会去读日志;一天出现十次,就不读了。重试按钮先被按下,通过之后就忘了。在养成这种习惯的团队里,真正的缺陷也会被重试两三次,碰巧通过后就原样合并了。 这就是红灯不再是信号的那一刻。

所以需要的不是更好的日志,而是分类。先确定这次失败属于哪一类,再对每一类采取不同的应对。

区分的方法出乎意料地简单:同一个提交再跑一遍,结果是否有分歧。 有分歧就是不稳定或基础设施问题,始终一样就是真正的缺陷。所以流水线必须在结果中留下“这次运行对应哪个提交”。

把它变成可复现的失败

要区分不稳定和真正的缺陷,就必须能重新构造出同样的条件。所以每次运行都要把下列内容记录在结果里。

仅仅留下这三样,“复现不了”这类报告就会大幅减少。

找出什么时候坏的

查找失败的原因提交时,倒着读日志的方法,耗时与提交数成正比。二分查找则是对数乘以常数。git bisect 文档写道,675 个修订大约经过 10 步、337 个大约经过 9 步就能缩小范围。也就是说,1000 个提交经过十次就能缩小范围。

要自动化,有一个条件:判定脚本必须是确定性的。 如果同一个提交不能给出同样的答案,二分查找就会把毫不相干的提交指认为罪魁。所以不能用不稳定的测试来跑二分查找。

git bisect run 依据脚本的退出码做出判定。约定是明确规定好的。

0          이 커밋은 정상(good/old)
1..127     이 커밋은 문제 있음(bad/new)   단, 125 는 제외
125        판정할 수 없음 — 이 커밋은 건너뛴다(skip)
128..255   이분 탐색 자체를 중단한다

该代码块中的韩文说明依次表示:0 表示该提交正常(good/old);1 到 127(125 除外)表示该提交有问题(bad/new);125 表示无法判定,跳过该提交(skip);128 到 255 表示中止二分查找本身。

125 单独存在的原因很重要。如果把构建根本通不过这类无法判定的提交报告为“有问题”,罪魁就会被错误地引向那一边。而 126 和 127 是 POSIX shell 用来表示“无法执行”和“找不到命令”的值,所以 125 被选为这一用途所能使用的最大值。编写判定脚本时,要当心不要使用 exit -1 之类的写法。那个值会变成 255,使整个查找被中止。

反馈时间预算

如果不给反馈时间设一个数字上限,流水线就会悄悄变慢。一点一点增加的时间没有人会察觉,终有一天就变成了“它本来就要跑很久”。

超过上限之后该做什么,最好事先定好。比如把阻止合并的步骤和不阻止合并的步骤分开,把慢的挪到后面;或者拆分测试;或者根据变更范围,跳过那些不常出问题的领域的测试。无论哪种做法,都必须记录放弃了什么。如果不记下放弃的东西,几个月后那个领域出了事故,没有人能解释当初为什么没有抓到。

在现场相遇的样子

参考

下一项实验要做什么

创建一个 git 仓库,堆叠提交,让它在中间某处被弄坏,然后用二分查找找出罪魁。先手动运行 git bisect start / good / bad,数一数经过几步就能缩小范围,再用判定脚本把同样的事情自动化。确认脚本遵守退出码约定,故意插入一个无法构建的提交,亲眼看看如果不用 125 跳过,罪魁会被错误指认成什么样子。然后放入结果有分歧的判定脚本,确认二分查找会崩塌;最后编写一个按类别统计失败日志并报告的分类脚本。