LabHub
学习 学习路径 课程

CI/CD 流水线

可复现的构建要放在最前面

在 LabHub 中继续学习

一句话总结

CI不是代替点击构建的机器人,而是强制保证即使输入相同的内容,无论何时何地都产生相同的输出结果的再现性装置。

##为什么需要这个

手工构建时的错误总是同样的样子。在我的笔记本电脑上可以做,但在服务器上做不到,昨天制作的图像和今天制作的图像不一样,但没有人能解释有什么不同。原因通常在于“不固定的东西”。依赖性版本没有固定,或者只用标签来引用动作和基准图像,或者生成物名称上没有留下制作的什么。

标签是人可以移动的名字标签。在tj-actions/changed-files供应链攻击中,攻击者将动作的标签重新指定为恶意提交。不是突破了存储库,只是移动了名字标签,而引用该标签的无数管道就直接执行了恶意代码。所以规则很简单。标签可以恶意更改,但提交SHA无法更改。动作和图像不是通过标签固定,而是通过提交SHA或摘要固定。

在生产中使用latest的话,同时会失去两种。无法追踪哪个版本被发布了,发生事故时也没有可以恢复的目标。所以发布时使用的标签必须是不可变的。像commit SHA或语义版本一样,一旦确定,就必须是不会指向其他值的值。

概念图: 如果不固定版本,昨天的成功不能保证今天的成功。 · 底层图像的标签也是版本。 · 将 · 消化器中,并自动更新

怎么操作

可重现的结构由四个轴组成。

  1. 输入固定。锁文件必须提交,CI使用npm ci、--frozen-lockfile、-lockfile=readonly等命令,不更新锁文件,直接安装。普通的install系列会悄悄更新锁文件,每次运行都会创建不同的依赖关系树。

  2. 输出识别。在输出名称和图像标签中,在每个提交中插入一个变不动的值。latest只是上面附加的别名,不是识别符。

  3. 缓存。当将锁文件哈希放入缓存键时,依赖性发生变化时,键会自动改变,自动失效。${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}添加restore-keys前缀链的形式是标准。有效的快取键策略可以将构建时间缩短50~70%。一个团队的实际测定显示,平均CI构建从28分钟缩短到8分钟,减少了71%。但是,每个快取存储器的限制是10GB,超过了的话,会从旧的开始排除,所以如果将范围分成提交单位,快取之间相互推挤,命中率反而会降低。快取也是信任界限。如果将Fork PR注入快取中,则后续构建会形成将该快取直接使用的快取配置。

  4. 失败处理。矩阵构建的fail-fast默认值是true,所以如果一个失败,其余的就会被取消。如果快速反馈很重要,就改为true,如果全面兼容性确认很重要,就改为false。如果不设置同步控制,连续Push时多个部署Job会同时运行,发生回滚复杂化的事故。

在现场相遇的样子

在障碍回顾中最常出现的句子是“当时分发的确是什么”。如果只有一个latest的图像标签,没有人能回答这个问题。相反,如果标签中嵌入了commit hash,那么仅凭注册表和Git日志就能在5分钟内得到答案。

以数字管理的团队这样设定目标。Lead Time 1小时以下,部署频率一天10次以上,变更失败率5%以下,MTTR 30分钟以下,管道执行15分钟以下,覆盖率80%以上。其中,如果管道执行时间开始超过15分钟,人们就不等CI结果,而是去做其他事情,反馈循环就会完全崩溃。

##如果输入相同的话,会得到相同结果的构建

第一次加上CI的话,只会从“在我的电脑上可以”变成“在CI上也可以”。

消除那个位置是构建自动化的真正目标。

如果不固定版本,昨天的成功不能保证今天的成功。npm installnpm ci的区别就在这里。前面的是package.json在范围之内最新的

因为要带过来,所以会更新锁定文件。后面的是**如果和锁定文件不一样的,就完全

失败。** 在CI中,失败的一方是正确的。Python的pip install -r也把哈希码

少的要求文件和--require-hashes只有一起使用时才能获得相同的性质。

底层图像的标签也是版本。FROM python:3.12明天指的是别的东西

可以。如果用摘要打不进去的话,会重现,但无法获得安全更新。

所以固定在消化器中,并自动更新是必须一起进行的。

只进行固定的话,不进行更新的话,几个月后漏洞列表会变长。

**缓存只有在准确时才有好处。将锁定文件的哈希码放入钥匙中。```yaml key: deps-${{ runner.os }}-${{ hashFiles('/package-lock.json') }} restore-keys: deps-${{ runner.os }}-


比没有还糟糕:因为必须失败才能成功。

**构建成果只制作一次。** 开发界分发时只制作一次,运营界

分发的时候再构建的话,测试过的和分发的会变成不同的东西。做一个就好了。

上传到存储库,之后的步骤是**升格相同的摘要**。标签由人

只是为了阅读的姓名标签,保证是同一个东西的是摘要。

**留下失败的构建的证据。**只要留下日志,就可以重新尝试重现相同的构建。

会旋转。将考试报告、覆盖范围、生成的设置文件作为产出物上传的话

可以直接看到**失败的那一刻的状态**。再转过去就会消失的东西

一般是原因。

##下次实习要做的事情

这个平台上没有Jenkins也没有GitHub Actions Runner。所以用shell脚本直接做build→test→package3个步骤。因为重要的是机制,而不是供应商。只制作一个以源hash命名的事件,用同样的输入再运行的话可以重复使用,最后用hash标签和latest粘贴到同一张图片上,用眼睛确认什么是不可变标识符,什么是移动的别名。