阿里在使用一种更灵活的软件集成发布模式
作者:董越(花名荷锄),阿里巴巴研发效能部高级产品专家
当今典型的软件集成发布模式是,通过类似GitHub的Pull Request或GitLab的MergeRequest的方式管理特性分支(Feature Branch):在通过代码评审等方法确认一条特性分支上的改动没问题后,将其合入集成用的分支。随后,代码改动进入在集成分支上运行的持续交付流水线,直到发布上线。
在阿里巴巴内部,尽管这种工作方式也得到了研发协同工具平台(Aone,对外叫云效)的支持,但广大研发同学选择的主流工作方式却不是它,而是用一种被称之为变更(全称变更请求,英文Change Request)的对象来管理特性分支,直到发布。
初看起来,变更与Pull/MergeRequest有不少相同点,但实际它们在理念上的差别很大。
本文详细介绍它们的相同点和不同点,并探讨用户喜欢变更这种方式的原因,当然也会介绍相应的风险和弱点。或许阅读本文,能给你带来一些思考。
相同点
- 变更与Pull/MergeRequest的相同点主要在于对特性分支本身的质量和流程的控制:
- 一个变更,就像一个Pull/MergeRequest一样,大体上对应一条特性分支。
- 在Pull/Merge Request中,可以看到这条特性分支上代码改动内容,进而进行代码评审(Code Review)。类似的,也可以以变更为粒度进行代码评审。
- 在特性分支上的代码提交,可以自动触发持续集成工具做构建以及各种自动检测,其结果可以在Pull/Merge Request中展现。类似的,在变更中也可以展现。
- 可以把Pull/MergeRequest上的最新代码构建部署到它专属的测试环境并运行,以进行测试和调试。在变更中也可以这样做。
- 在Pull/Merge Request中可以设定通过的条件,比如至少两名评审者同意,且所有的代码评审中发现的问题都已修复或澄清,且特性分支上的持续集成流水线运行成功。在变更中也支持类似设置。
不同点及其主要价值
变更与Pull/MergeRequest的不同之处关键在于,这个特性分支与其他特性分支一起集成和交付的方式。
对于Pull/MergeRequest,随后把特性分支合并到集成用的分支,然后就没有然后了。哦不,是然后就不再以特性分支为粒度去管理了。这条特性分支已经合入集成用的分支,其上的代码改动已经融入集成发布的洪流之中,被裹挟着和其他特性分支上的代码改动一起前进,去闯关通过集成-发布的各个阶段(Stage)。
而变更不同。即便是已与其他变更集成,它仍然具有一定的独立性和灵活性,在确有必要时,可针对单独变更进行操作。下面我们通过两个例子来详细介绍。
第一个例子:简化起见,假定集成交付过程有三个阶段:日常集成测试、在预发布环境测试、正式发布。某应用的变更A到变更E共五个变更,在通过了日常集成测试这个阶段后,进入了在预发布环境测试这个阶段。测试时,发现变更C有一个缺陷。这个缺陷因为受日常测试环境所限,在日常集成测试阶段没有暴露出来。经分析,变更C与其他四个变更间没有依赖关系,不会互相影响。因此,为了让其他四个变更的发布尽量少受影响,决定把变更C从在预发布环境测试这一阶段中摘除出来。其他四个变更在一起再次测试验证,此时该缺陷不再出现,这四个变更在一起通过了在预发布环境测试阶段,进而进入正式发布阶段,发布上线。
在这个例子中,在摘除了变更C后,没有将其他四个变更在一起再次经过日常集成测试阶段,是出于两方面考虑:一是,此时的日常集成测试环境,已经被若干新添的变更所占用。它们的测试需要时间,而且可能也会反复调整。把新添的变更赶出去,或者把这四个变更和新添的变更混在一起,或者让着四个变更等着,都分别有明显的不利之处。另一方面,A、B、D、E四个变更,它们与变更C在一起,已经通过了日常集成测试。而变更C又与它们无关,因此对它们再次进行日常集成测试,发现问题的可能性很低。测试是要讲究性价比的,而不是一味追求保证产品零缺陷。出于以上原因,在具体实战中,开发团队就有可能根据当时实际情况,在评估后决定,在摘除了变更C后,不再将其他四个变更在一起送回日常集成环境,而是直接在预发布环境再次测试。
第二个例子:仍假定集成交付过程有日常集成测试、在预发布环境测试、正式发布三个阶段。某应用的变更A到变更E共五个变更,在通过了日常集成测试这个阶段后,进入了在预发布环境测试这个阶段。此时,根据市场情况变化,需要对变更C所承载的新功能做出少量调整,比如页面说明文案上改几个字。考虑到新的修改与变更C原有内容要么都发布,要么都不发布,所以为便于管理,新的修改就在变更C所在的特性分支上完成。这样形成的变更C的最新内容,与其他四个变更在一起,在预发布环境进行测试,通过后正式发布。
以上两个例子,是在传统的集成-发布方式基础上,加入了一些灵活性:集成-发布过程中,必要时可以中途撤下变更,可以中途修改完善变更。而有些团队在使用变更时,采用了更进一步的方式:不再设集成工程师之类的角色,不再规划统一的集成、发布的计划和时间点。而是每个开发同学负责自己的变更,不仅跟踪它直到把变更的质量提升到可集成的程度,而且由开发同学自己把他负责的变更依次适时推入(也可能是自动进入)集成-发布的各个阶段,跟踪它直到发布上线。也就是说,尽管进入了集成-发布阶段,各个变更仍是被各自的开发者分别跟踪和推进的:它们可能有各自的推进速率和节奏,而不会相互拖累。彼此无关的变更,只是碰巧一同使用某个测试环境,一同批量测试以提高测试效率、一同上线以避免排队而已。据此,尽可能缩短了一个需求从开发到发布上线的时间,并表现为相当频繁的发布上线。同时也契合了DevOps的理念:“谁开发谁运行”(You build it, you run it)。
这一变化趋势其实和软件研发的管理实践中发生的事情类似:瀑布模型时代就不提了。随后迭代方法取代了瀑布模型。典型的,Scrum方法中的Sprint。而更进一步,在精益方法的看板墙上,迭代被弱化,关注的焦点从每个迭代做什么,每个迭代进入到什么阶段,演化为关注每个在制品流动到了哪个阶段,以及每个阶段包含的在制品总量。
类似的,在上述变更管理方法中,从关注某个集成版本进入到集成-发布的哪个阶段,演化为关注每个变更进入到集成-发布的哪个阶段,以及每个阶段包含了哪些变更。
另一方面的价值
以上,介绍的是使用变更管理方式带来的灵活性,以及因为灵活务实而带来的效率提升。变更管理方式,在信息记录和跟踪方面还有一些的好处:
要想方便地知道,本次测试、本次发布,到底包含了哪些特性,只要看看包含了哪些变更就好了。变更本身有说明文字,变更还可以关联需求、任务、缺陷等工作项,更详细地说明变更的目的。而在变更之外,也没有别的代码修改可以通过直接提交到集成分支等途径溜进来。
而从变更的视角,这个变更相关的所有改动,都在该特性分支上,而不会因为多次反馈修改而散乱到各处。因此这些修改总是可以方便地一同查看,一同操作。同时,总是能够清晰地知道这个变更的状态,它到了哪个阶段:开发完毕了吗?进入日常集成测试阶段了吗?已经正式发布了吗?等等。
变更可以关联需求、任务、缺陷等工作项,同时变更的状态是可以自动获取的。因此,看板墙上跟踪的工作项,从原理上就可能被自动移动,以反映其实际状态。协作和进展,在看板墙上一览无余。
弱点和风险
以上谈的都是这样的变更管理方式能带来的好处。那么,它有没有弱点和风险呢?
是的,它有。从大爆炸式集成到持续部署流水线,业界几十年来几乎一直在采用一个基本模式:总是一个集成版本,去顺序经历集成-发布的各个阶段。这样可以保证,下一阶段收到的内容,总是精确的经过了上一个阶段的检验。而本文介绍的变更管理方式所引入的灵活性,意味着颠覆了这一基本模式。灵活性从来都是双刃剑。灵活性意味着风险增加,意味着可能被滥用。
敏捷宣言认为“个体和互动高于流程和工具”,上述变更管理方式暗合了这样的思想。但在实际使用该方式时,需要注意到它对团队成员提出了更高的要求:要求他们在具体场景具体案例中,能够对变更间的相关性及相应风险做出评估,并了解不同选择对效率的影响,最终综合做出特定场景特定案例中的决策。具体来说:
- 变更对应的代码改动越少,中途撤下变更带来的风险越小。
- 中途修改完善变更所对应的代码改动越少,带来的风险越小。
- 软件架构越好,变更中途撤下或修改完善带来的风险越小。
- 本次变更与其他变更的相关性越小,中途撤下或修改完善带来的风险越小。
- 越紧急,越考虑灵活处理。
- 业务角度,对软件质量的要求越高,就越不要考虑灵活处理。
延伸一下,事实上,在微服务甚至函数服务时代,即便不使用上述变更管理方式,也有类似上文的风险,也相应需要团队成员具备类似上文的自主判断能力。为什么这么说呢?
之所以把单体应用拆分为微服务甚至函数服务,一个重要原因就是为了每个服务能单独测试和发布上线。然而,在使用微服务甚至函数服务方式时,被测对象严格地讲并不是一个服务,而是该服务以及测试环境中与其直接或间接打交道的所有其他服务。而当把每个服务单独测试和发布时,就经常会导致本阶段测试时某个其他服务的版本,与下个阶段测试时的版本不同,或者与将来正式发布运行时的版本不同。于是就意味着类似上述变更管理方式中的风险。
相应的,这里面就需要人来判断(当然可以有智能算法的辅助),本次哪些服务上的改动务必要一起测试和上线,而另外几个服务上的改动可以单独运作。而下次可能又是不同情况,要根据下次的具体情况判断。
由此看来,“总是由一个集成版本,去顺序经历集成-发布的各个阶段”这个基本模式,其实已经被悄然突破了。上述变更管理方式,只是使这个突破更加明显了而已。
落地及工具支持
以上是介绍了一种独特的变更管理方式,介绍了优点,也介绍了相应的风险。下面我们来看看它在阿里是如何落地的。
首先需要一套分支方案来支持它。大体上是这样:
- master分支总是代表最新已发布版本。
- 代码改动总是在特性分支上完成。特性分支总是从master分支上拉出的,并在必要时从master再次同步。
- 没有一条长期存在的集成发布用的分支。而是集成发布过程的各个阶段,各对应一条短期的,被自动管理的集成发布分支。从master分支自动拉出该分支,再把各特性分支自动合并到该分支(出现冲突时人工介入),于是它上面就有了用户想要的各特性。
- 如果发现某个特性需要进一步修改完善,在特性分支上完成,并再次合并到相应的集成发布分支。
《在阿里,我们如何管理代码分支?》这篇文章对上述分支方案有更多介绍。
《云效 > 使用指南 > 持续交付 > 开发模式 > 分支模式》这篇文档是该分支方案的详细说明。
可以看出,这套方案,对工具平台的要求是比较高的:从界面角度,用户只需要管理各个集成发布阶段分别要有哪些变更。而工具平台要将它映射为对集成发布分支的管理,包括创建新分支或复用已有分支、从各特性分支到集成发布分支的合并等等。这里面也包括了不少算法,以尽可能减少相同的合并冲突重复出现。
对工具平台的高要求,或许是这套方法多年来一直只是在阿里巴巴内部被广为使用的原因。
不少曾在阿里巴巴工作过的同学,出去后都念叨着没有这样的工具支持了。不过现在好了,就像Google基于内部的Borg对外提供了Kubernetes,阿里巴巴也基于内部研发协同工具平台Aone对外提供了云效。
云效的公有云版和专有云版,都提供了上述方法的完整支持。不论是你对上述方法抱有兴趣还是怀有疑虑,都可以尝试研究一下。
相关阅读:
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
MySQL 数据库设计经验总结
背景 此文仅在数据库设计层面进行探讨,数据库的运维与底层调优不在讨论范围之内。 更丰富的知识可以在 MySQL 官网文档 查阅。 学习官方文档也是一种好的习惯,能更系统更全面的掌握某一领域的知识,具体知识点也可以通过搜索引擎快速获取,但是很难让你深入到细节或者上升到宏观层面。 基础知识 存储引擎 通常来说,我们做业务开发,指定存储引擎为 InnoDB 即可。 字符集 通常来说,只要指定为 utf8 即可。 如果业务中需要使用 emoji 表情,那么就必须要设置为 utf8mb4 MySQL 可以在 server 级、database 级、table 级、column 级进行字符集的设置。 数据库设计 总则 命名以“_”分割 数据库层面还是推荐使用“_”作为分割,这里多说几点: 1、约定俗成。长久以来不仅 MySQL ,其他数据库也推荐使用“_”,这是一种 SQL 规范。 2、JSON 返回的数据一般也会将驼峰转化为“_”来分割。 3、JAVA POJO 对象还是使用驼峰命名,现在的 JSON 转换工具, ORM 工具可以很便捷的指定参数来设置驼峰或者下划线的偏好。 如果仅是使用 Myb...
- 下一篇
基于Spring Security和 JWT的权限系统设计
写在前面 关于 Spring Security Web系统的认证和权限模块也算是一个系统的基础设施了,几乎任何的互联网服务都会涉及到这方面的要求。在Java EE领域,成熟的安全框架解决方案一般有 Apache Shiro、Spring Security等两种技术选型。Apache Shiro简单易用也算是一大优势,但其功能还是远不如 Spring Security强大。Spring Security可以为 Spring 应用提供声明式的安全访问控制,起通过提供一系列可以在 Spring应用上下文中可配置的Bean,并利用 Spring IoC和 AOP等功能特性来为应用系统提供声明式的安全访问控制功能,减少了诸多重复工作。 关于JWT JSON Web Token (JWT),是在网络应用间传递信息的一种基于 JSON的开放标准((RFC 7519),用于作为JSON对象在不同系统之间进行安全地信息传输。主要使用场景一般是用来在 身份提供者和服务提供者间传递被认证的用户身份信息。关于JWT的科普,可以看看阮一峰老师的《JSON Web Token 入门教程》。 本文则结合 Sprin...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Linux系统CentOS6、CentOS7手动修改IP地址
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7安装Docker,走上虚拟化容器引擎之路
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- 设置Eclipse缩进为4个空格,增强代码规范
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- 2048小游戏-低调大师作品