开发者学堂课程【ALPD 云架构师系列:云原生 DevOps 36计-阿里云云效出品:不可变构建及如何提升构建效率】学习笔记,与课程紧密连接,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/772/detail/13498
不可变构建如何提升构建效率
主要内容
一.终态:提供稳定、可预期的系统
二.可预期的系统,始于预期的制品产物
三.几个常见的构建问题
四.不可变构建
五.构建的效率问题
六.提高构建效率的实践建议
一.终态:提供稳定、可预期的系统
软件交互是面向终态的,终态提供稳定、可预期的系统。服务提供了相应的请求,给相应预期的返回。可预期的系统,需要确定的环境软件制品,比如容器镜像。在市场环境部署,环境是确定的,部署的东西是确定的,否则系统不知道是怎样的。
东西分发出去需要确保版本的一致性,否则影响整个软件交付。
二、可预期的系统,始于预期的制品产物
代码、编出来的包、镜像都属于制品,软件制品构建的容器镜像,将软件制品当作容器镜像。传统的构建过程里代码及其代码依赖,依赖 so 文件。在确定的环境里确定构建脚本,自己构建动作伸展出软件制品,是非常常见的操作。本地结合构建产生二进制文件,二进制文件是软件制品。
软件制品必须包含:
有确定的格式,容器镜像或 tar 包能够确定格式。
有唯一的版本,构建出来的版本都不一样,版本与代码的版本或依赖的版本、环境版本、构建脚本的版本是强相关的,软件制品有版本。
能够追溯到源码,可以知道从哪制出,如果制品很难溯源产生很多制品管理问题。比如在某台生产环境里,二进制文件出现问题,不知道什么原因导致。本身构建很乱,谁都可以直接替代生产环境。
能够追溯到生产和消费过程,制品通过哪些阶段有哪些活动最后产生出来可以溯源。可以知道制品被用在哪些环境,被谁用,制品有标签,知道是什么有什么样的特性。
构建并不陌生,每天在电脑里执行很多次。
三.几个常见的构建问题
1.应用的代码仓库没有Makefile/package.json/…,怎样构建,是初学者拿到代码第一个问题,拿到代码构建不知道怎样构建出来。
2.为什么make build执行成功,但制品缺少了几个依赖的组件,成功了但少了一些东西,怎么来的、在哪定义的不知道。
3.同一份代码,上周可以编过,但今天报编译错误。
电脑上可以编译成功,但服务器上失败。换个环境编辑失败,不知道为什么失败。
4.包在测试环境上运行是正常的,但到生产环境出错。编出来,运行的环境不同也不一样。由于运行环境、构建环境不同依赖的版本导致遇到类似问题。
问题的根源,构建是可变的。当构建可变时带来一系列问题。
四.不可变构建
1.不可变构建含义
相同代码相同依赖在同样构建环境、同样的构建脚本的描述下出来的软件制品是相同的。所有东西保证一致性,前三者一样,产出的制品一样,不同时间构建也是一样的。不可变构建基础理解。有的人做不到不可变构建,代码是一样的但构建环境不同,依赖看上去是一样的不同时间点拿到是不一样的。用 snapshot 的 Java 程序,snapshot 永远的资源版本,不知道今天与明天是否为一个版本。不可变构建并不是绿色软件制品相同,而是如何确保蓝色条件相同。
2.相同代码、相同依赖
依赖分为两种源码的依赖、组件制品的依赖。go 依赖源码Java 依赖 class 架包,每一个依赖有明确版本要求,依赖的什么,什么版本。源码加制品版本能够唯一描述此依赖,如果源码加依赖的版本可变,依赖可变,做不到依赖相同的概念。
常见依赖
·go.mod
.package.json &
package-lock.json
.pom.xml
.requirements.txt
package.json本身定义可变,里面版本比此版本新,但一般有 package-lock,固定起来。两个加起来为不可变的。
pom 里有xml版本认为是确定版本。Requirements 在 Demo 示例代码里 requirements.txt 没有,依赖是可变的,依赖没有做到依赖项 requirements+特定版本,没有版本,可能今天没问题,过一段时间有问题。隔一周时间构建发现有内存的 bug,在构建前一天提交 bug,看源码提交发现问题。有时 library 兼容性出现问题,可对照构建的依赖文件,依赖文件首先确定依赖的哪个代码、代码具体版本是什么、依赖的组件、依赖的 library具体的版本是什么。依赖文件没有明确的表达,构建是可变的,意味着产生一个不符合预期的最终产物,有可能带来系统风险,在安全方面需要考虑的问题。
3.相同的构建环境
找到相同的构建环境,自己的电脑也不能保证昨天与今天是相同的,可能系统升级。构建环境本身需要描述像应用的运行环境需要描述一样,构建环境描述的方式一样,通过 Dockerfile 描述。应用在怎样环境下构建、构建环境里有哪些组件、组件版本是什么都可以描述清楚。
安装一个包,包的版本是否是确定的还是可变的。不同时候构建不一样,也是一个风险。构建环境与运行环境一样通过 Dockerfile 描述。构建环境的 Dockerfile 是否是运行环境的 Dockerfile,Demo 用同一个 Dockerfile 为了简单,不需要找工具链解决,Dockerfile 又做构建又做运行。如果研究层次发现 Dockerfile 比较大几百兆,构建一层占很大部门,想瘦身分开是很简单的方式,构建时引入许多不必要的依赖,开发库、工具链带来了风险。有兴趣可将文件修改,分为两个。一个是构建环境,应该包含哪些依赖环境、依赖包。另一个运行环境,哪些是必须的,哪些是不必要。此方式构建该如何写,感兴趣可尝试。
4相同构建脚本
有三块,一是确定构建依赖,构建脚本依赖构建环境。构建依赖是 go.mod 文件,依赖的模块,对应的版本是非常明确的版本。第二点包含构建、运行。构建环境不唯一,没有用确定的版本,永远用最新的不同时间不一样。golong 版本不同,是1.15还是1.14.8不确定,是不好的时间。
保证标准化,交付什么、交付的形态、交付并且部署运维。
保证预期系统日期一致,需要可预期构建制品,必须从三个角度不可变构建产生一致的软件制品。软件的准确性比做得更快更重要,如果做的不准确不一致,经常变来变去,版本不可控,所有的东西浪费。做好准确性单纯从构建角度考虑构建的效率。
五.构建的效率问题:积少成多
100名工程师,平均每人每天构建10次,每次1分钟,每天共耗时16小时非常大的数字。影响工程效率大的因素是构建太慢,构建影响的东西很多。
六.提升构建效率提升的实践建议
1.基本原则:构建的准确性>构建的效率
无论什么构建效率的时段一定保证准确性,构建的准确性永远大于构建的效率,在保证准确性的前提下提升构建效率。
2.实践建议:
.应用瘦身,减少应用的依赖项
做任何开发性之前考虑应用依赖情况,依赖是否太大、依赖是否太多东西、有些依赖是否需要、基础镜像是否可以做更小。
.分层构建,复用底层的构建结果
很大的系统依赖10个组件,每次系统依赖都要将10个代码拉下来,重新码构建,整个时间耗费很大。有多个层次将底层的先构建完成后能被上层复用,构建效率增量。
.构建缓存,避免重复拉取依赖和重复构建
.网络优化,保证代码、构建机器和制品库之间的低网络延时
很多人构建问题都是网络问题,代码构建机器、依赖的制品库 APM 的源与自己之间的网络链太大。首先保证低网络延时,另一点代码与构建机器是否为同一个网络域里。代码与构建机器不在同一个网络域里,之间的延时不会太短,整个机器网络路由长。
.仓库镜像,减少拉取依赖项的时间
自己可以补做镜像,简单仓库很多工具做,有镜像仓库极大减少拉取依赖的时间,投资非常划算。国内有很多镜像仓库,由于网络影响、防火墙,时间停顿久,团队建了3个仓库,减少拉取的时间。
提升构建效率提升的实践建议不是针对某个领域而是所有领域都要考虑。构建从哪里来,最终要找到源头,保证制品、产物一致性必须保证代码版本一致性、依赖的一致性、环境的一致性、构建脚本的一致性,日常生活中都由源代码管理。
源代码管理及软件配置
以代码为中心的交付过程,一切从源代码开始