不可变构建及如何提升构建效率(一)|学习笔记

简介: 快速学习不可变构建及如何提升构建效率(一)

开发者学堂课程【ALPD 云架构师系列-云原生 DevOps36计不可变构建及如何提升构建效率(一)】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/82/detail/1269


不可变构建及如何提升构建效率(一)

 

内容介绍:

一、可预期的系统,始于可预期的制品产物

二、几个常见的构建问题

三、不可变构建

四、构建效率问题:积少成多

五、提升构建效率的实践建议

其实做软件肯定是面向终态的,终态是什么,就提供稳定、可预期的系统,用户需要服务提供了相应的请求,可以给用户相应预期的范围。但是可预期的系统,需要确保环境和软件制品(例如:容器镜像)一致性,不然系统将很模糊向,不清楚其究竟如何。

所以这里提到问题,怎么去确保环境和软件制品的一致性,这里先考虑如何确保软件制品的一致性,也就是说如果需要将东西分发出去,那分发出去的东西应该如何确保其版本的一致性。可能其他为不变量,但是最后结果前后不一致,这里是不可以的,会影响整个的稳定交付。

 

一、可预期的系统,始于可预期的制品产物

所以这里认为可预期的系统,始于可预期的制品产物。其中制品产物是一个新的概念。

其实熟悉制品交付的同学知道,例如代码也属于制品,编出来的包,镜像等等都属于制品。但在这里把构建出来的软件制品当做容器镜像,那在传统的构建过程里,基本是代码以及代码的依赖。比如说依赖的 ip 包、so 文件,然后在一个确定的构建环境里,有确定构建脚本,然后执行构建动作,最后生产出来软件制品,这是一个非常常见的操作。

那例如本地使用 PPT 和 C 的情况下,计算那些构建可以计算产出一个二进制文件,而二进制文件也是软件制品。

1、软件制品所必须包含的东西:

(1)确定的格式:到底是逻辑意向,还是 ip 包,或者是二进制的 adout ,可以具有明确印象。

(2)有唯一的版本:对每次构建出来的版本都不一样,但是有很多时候,其版本和代码的版本,依赖版本,环境版本或者构建脚本的版本是恰恰相关的,比如说这些内容不一样,导致产生出来版本也不一样。所以软件制品是有版本的。

(3)能够追溯到源码:明确是构建出来的地点及方式。如果制品溯源存在困难,就会产生很多的制品管理的问题。比如说现在在某一家生产环境里,二进制文件出现问题,但是不知道由于什么原因导致。比如说这里本身构建混乱,在有些企业里,谁都可以直接替换生产文件,就并不知道目前跑的代码是谁为源头。

(4)通过制品能够追溯到生产和消费过程:制品通过哪些阶段,经历哪些活动,最后将其产生出来,可以进行溯源。制品被用在哪些环境,被谁利用了,也可以进行溯源进行了解。这样其实相当于给制品贴标签,知道它是什么,拥有哪些特性。

 image.png

 

二、几个常见的构建问题

构建对大家来说一点都不陌生,因为在电脑里,每天要执行很多次,那么简单的列举一下,就是构建当中可能常常遇到的一些问题。

第一个问题是应用代码库里没有 makefile/package.json/...,也就是所认知的的东西其都不具备,这时该如何构建呢?很多初学者可能拿到代码符时,面临到第一个问题就如此。其实很多时候拿到代码,首先需要将其构建出来。

第二个问题是例如 make build 执行成功,但制品却缺少了几个依赖的组件。这些东西怎么来,在何处定义,并不知道。

第三个问题是同一份代码,上周还可以编过,今天就报编译错误了。

第四个问题是电脑上编译成功,但工具(服务器)上不行。也就是换个环境,编译就失败了,并且并不清楚导致失败的原因。

最后一个问题是这个包在测试环境下可以正常运行,但在生产环境就会出错。也就是编译出来后,运行环境不一样,导致其也不一样。例如以前写过一段代码,在 A 电脑上一直没有出错,在 B 机器上运行就会出错。这时找了一台共同的另外的 C 机器去操作,发现并没有出错。那其实就是由于运行环境,构建环境,或者不同依赖的版本,导致遇到类似的问题。所以这些问题归根结底是构建本身是可变的。当构建可变的时候,会带来一系列的问题。解决这个问题的方式就是不可变构建。

 

三、不可变构建

不可变构建的含义,同样的代码以及同样的依赖,在同样构建环境的描述下,和同样构建脚本的描述下,构建的软件制品应该是相同的。所以这里强调就是,所有东西都要保证一致性,也就是如果前三者一样的话,那产生出来的制品也应该是一样。哪怕它是不同时间构建出来的,也应该一样。

所以这是不可变构建的基本的理解,这并不是废话,并不是所有东西都一样,其就会一样。不然,看起来很简单,但有些地方并不能完成。例如可能代码一样,但是构建环境,还有就是依赖表面上一样。但其实可能是不同的。

例如这里可能使用了很多 snapshot,比如说 Java 等等,那么这时 snapshot 永远都是 latest 这样的版本,此时并不知道这个版本和与其他版本是否为同一个版本。所以这里需要做好的不可变构建,并不是要使下图中绿色的相同的软件制品一样,而是说如何确保左边蓝色的几个可以满足相应的条件。

 image.png

1.确定的依赖:依赖是什么?依赖的版本是什么?

接下来分别来看一下相同的代码和相同的依赖,就是怎么样可以被称为相同的依赖?依赖其实分两类:一类是源码的依赖,一类是依赖组件或者依赖制品。依赖源码,可能像 go 中很多都是源码,但是 Java很多时候,总仓库里面依赖的是 class+包,每个依赖其实都有一个明确的版本要求(依赖的什么的什么版本),这个源码或者这个制品加上这个版本,应该是能够唯一描述这个依赖的。

如果这个依赖的源码或者制品,加上这个版本是可变的,那这个依赖其实就是可变的。就做不到它是一个相同的依赖性的概念。可以分享常见的描述方式,比如说需要 go.mod,需要 package.json,但其实可以知道  package.json 本身里面东西其实是可变的,就里面由于一个版本,它其实区别其他版本比较新,底力范围也都可以。但一般会有 androck,要把它固定下来,只有将这两个加起来,它才是不可变的。

那 home 里面如果有 step short 是 release 版本。一般来说,也认为它是一个确定版本,如果不是,它也会有问题。那最后 requirements.txt,就是 python 里面,在现在 demo 事例的代码里它里面 requirements.txt,它其实具有依赖,具有可变性。其并未做到有依赖项 requirements+特定版本,其并不具有版本。这样可能存在随时间变化的问题。

举例子之前有一个做外部服务的库,然后隔了一周的时间再去构建,发现了它有内存溢出的 bug,后来发现是在构建的前一天,其提交上去了一个 bug。提交带有一个 bug,去源码提交才发现这个问题的,并不是只有高级语言才会遇到类似的问题。其实对类似意言类同样会遇到类似的问题,有时候 the library 的兼容性可能会出现问题时,这时是最危险的。

 image.png

所以大家可以去对照一下,就是去查看构建的依赖文件。在依赖文件里,首先是否指明依赖代码具体的版本,或者说依赖的组件,依赖的 library 具体的某个版本。如果在空文件,或者在依赖文件里没有明确表达,这个构成是可变的。也就是如果构成可变,意味着就会产生一个不符合预期的最终的产物,最终在系统上也有可能会带来风险,并且风险不可预知。其实就在整个安全里,也要相应的去考虑一些问题,这里只点到为止。

描述代码:go.mod、package.json、package-lock.json、pom.xml、reqirements.txt)

2.相同的构建环境

相同的构建环境,这里举 Dockerfile 的例子,如何去找到相同的构建环境。

例如像自己的电脑,也不能保证昨天与今天是相同的,因为存在系统升级的问题,像上文提到的应用环境要描述一样,构建环境本身其实也需要描述。

而且描述方式也是相同的,通过 tkmapper 去描述,就是应用应该在什么环境下去构建,构建环境里有哪些组件,组件的版本是什么,都可以描述清楚。这问题与上文的依赖问题是相同的,例如说安装包,包的版本是否确定,还是其也是可变的,不同时间去构建也可能不相同,但这也具备风险。

所以构建环境和运行环境一样,都要通过 Dockerfile 描述。这里提出问题,构建环境的 Dockerfile 不一定就是运行环境的 Dockerfile。现在使用 demo 的应用时,其实使用的是同样 Dockerfile,这是为了操作的简洁化,这样不需要找工具链去解决这个问题。现在在里面又做构建,又做运行。但是如果仔细研究 Dockerimage 的 layo,也就是层次,会发现其中面积都达到了好几百兆,都比较大。但里面构建的一层占了很大一部分。

就是如果说需要给它“瘦身”,使用很简单的方式,就是将其分开。而且当构建时,会引入很多不必要的依赖,这也会带来一些风险。如果有同学感兴趣,可以尝试将文件修改,也就是将其分成两个:一个是构建环境里,其应该包含哪些,应该有哪些 to side,然后包括相应的依赖环境、依赖包。另一个,可以思考如果需要硬件环境的情况,每个必须或不需要在的位置。如果最终生成了可交付的镜像,相对来说会比较轻量级。可以想象如果构建环境不一样时,应该怎么去写,可以下去尝试,这相当于课后练习或者实验。

image.png

3. 相同的构建脚本

构建脚本分为三部分,与上文构建环境相对应。也就是确定的构建依赖、语言无关的构建脚本、机器无关的构建环境。

构建依赖里是 go.mod 文件,其实可以看到里面,这里依赖的模块,与其对应的版本是非常明确的版本,比如说有对应的 TAI,或对应的permit ip,这是明确的依赖。

目前这里包含构建,可以看到 go build,go build 的部分包含构建,同时也包含运行,这就是整个进程。

目前构建环境,其实在 go long 这里,构建环境也是不唯一的,这里并没有使用确定的版本,是使用 latest 或者类似的版本,也就是永远使用最新版本,不同时间也是不同的,而且 go long 版本也是不确定的。.其实这是不好的事情。

所以说刚才简单提到了,重复这边基础设施以后,第一个要保证标准化,也就是交付部最终交付时使用什么样的形态,进行交付并且部署的运维。第二个是如果要保证不可变,首先要保证和预期的系统运行一致,所以需要一个可预期的构建制品。可预期的构建制品必须从三个角度来做好不可变的构建,然后才能做一致的的软件制品。

image.png

相关文章
|
3月前
|
移动开发 监控 前端开发
构建高效Android应用:从优化布局到提升性能
【7月更文挑战第60天】在移动开发领域,一个流畅且响应迅速的应用程序是用户留存的关键。针对Android平台,开发者面临的挑战包括多样化的设备兼容性和性能优化。本文将深入探讨如何通过改进布局设计、内存管理和多线程处理来构建高效的Android应用。我们将剖析布局优化的细节,并讨论最新的Android性能提升策略,以帮助开发者创建更快速、更流畅的用户体验。
66 10
|
20天前
|
前端开发 JavaScript 开发者
揭秘前端高手的秘密武器:深度解析递归组件与动态组件的奥妙,让你代码效率翻倍!
【10月更文挑战第23天】在Web开发中,组件化已成为主流。本文深入探讨了递归组件与动态组件的概念、应用及实现方式。递归组件通过在组件内部调用自身,适用于处理层级结构数据,如菜单和树形控件。动态组件则根据数据变化动态切换组件显示,适用于不同业务逻辑下的组件展示。通过示例,展示了这两种组件的实现方法及其在实际开发中的应用价值。
28 1
|
4月前
|
存储 SQL 运维
MSSQL性能调优精要:索引深度优化、查询高效重构与并发精细控制
在MSSQL数据库的运维与优化领域,性能调优是一项复杂而细致的工作,直接关系到数据库的稳定性和响应速度
|
6月前
|
存储 缓存 安全
【C/C++ 项目优化实战】 分享几种基础且高效的策略优化和提升代码性能
【C/C++ 项目优化实战】 分享几种基础且高效的策略优化和提升代码性能
336 0
|
分布式计算 关系型数据库 BI
KYLIN 建模设计学习总结(概念、空间优化、查询性能优化)
KYLIN 建模设计学习总结(概念、空间优化、查询性能优化)
143 0
jira学习案例124-代码分割优化性能
jira学习案例124-代码分割优化性能
77 0
jira学习案例124-代码分割优化性能
|
SQL 消息中间件 运维
环境管理的应用场景(一)|学习笔记
快速学习环境管理的应用场景(一)
249 0
环境管理的应用场景(一)|学习笔记
|
XML 运维 Cloud Native
不可变构建及如何提升构建效率 | 学习笔记
快速学习不可变构建及如何提升构建效率
不可变构建及如何提升构建效率 | 学习笔记
|
自然语言处理 算法 机器人
高级能力和算法效果优化(一)| 学习笔记
快速学习高级能力和算法效果优化。
高级能力和算法效果优化(一)| 学习笔记
|
机器学习/深度学习 数据采集 自然语言处理
高级能力和算法效果优化(二)| 学习笔记
快速学习高级能力和算法效果优化。
高级能力和算法效果优化(二)| 学习笔记