1.1 持续交付与持续部署
我们一般认为,现代软件的持续交付主要体现在单元测试、构建、Staging(预发布)环境部署和Staging环境验收环节的自动化。对于开发人员来说,这是一种快速将需求交付给用户的方法。
持续交付最终产生的构建物是否能够发布,则需要产品人员及运维人员进行确认和操作。
持续部署则更进一步,除了上述持续交付的自动化流程,还将最终的构建物自动部署至生产环境。在持续部署的过程中,除了必要的人工测试阶段,几乎不再需要人工介入。
显然,持续交付和持续部署的不同点在于是否自动化进行构建物的部署。具体来说,两者的区别如图1-1所示。
图1-1 持续交付和持续部署的区别
1.1.1 为什么要持续交付
在Web应用之前,传统的软件开发是围绕“发行版”进行的:开发者编写代码完成功能,运行测试用例进行测试,直到一切运行正常。接着,产品会被一次性打包为发行版,通过软盘或CD-ROM等方式进行交付。这意味着,一般的交付周期将长达半年或一年。
网络和Web应用出现之后,发布变得简单许多,借助网络可以实现更频繁地发布,甚至自动更新。这种发布和交付方式彻底改变了传统软件的交付方式。
即便到了今天,发布周期过长仍然是软件工程实践最常见的问题之一,而持续交付通过控制更小单元的修改来降低发布难度,进而解决发布周期长的问题。
另一个常见问题是依赖漂移。随着未发布的代码越来越多,这些代码的依赖库及上游服务也在不断地更新迭代,当需要部署这些未发布的代码时,上游服务和依赖库可能已经更新数个版本。此时,意料之外的兼容问题将增加,尤其是这些依赖库或服务废弃了一部分正在使用的API。
开发人员一旦完成了某项功能的开发和提交,就会自然而然地进入下一个开发阶段。对于已完成的功能点,他们不再对其保持长久的记忆,因此一旦出现问题,要找出原因是非常困难的。由于发布周期长的问题,开发人员可能需要在1个月、3个月,甚至是6个月前的代码中找出隐藏的信息和问题,这对开发人员提出了巨大的挑战。同时,管理层也意识到必须使用更高效的方案来解决这个问题。
1.1.2 持续交付的好处
持续交付的好处主要针对开发人员和企业。
对开发人员的好处一般体现在以下方面。
• 更高的开发效率:实行持续交付后,开发人员编写的代码、功能、实验,以及Bug修复等将迅速发布到生产环境中,一旦出现问题,那么查找问题的范围缩小至刚刚编写的代码中,能够做到快速发现和立即修复。
• 更高的产品可靠性:在持续交付过程中,一般会配合使用自动化流程,例如自动化测试、自动化构建等来替代容易出错的手工步骤。
• 更快的循环反馈:与传统的发布过程相比,持续交付通过保持构建物随时可交付的状态,加速了信息在不同角色之间的传递,尤其是开发、发布、发现问题、修复再发布的循环反馈机制。
对企业的好处一般体现在以下方面。
• 更清晰的开发进度和成本结构:传统的大型软件开发通常无法确定具体的交付周期。持续交付可以将这些较大的发行版拆解为更小的开发流,有助于衡量开发进度和成本结构。
• 团队更具灵活性:持续交付追求小而连续的更改,因此非常容易知道团队成员目前的工作,在进行其他迭代时可以快速地进行人员调配。
• 易于创新:持续交付能够快速获取功能的用户验证结果,实现更加高效的创新和用户测试,这种正向循环能够让团队建立信心,进而使团队成员提出更好的想法。
持续交付从根本上改变了软件交付的方式,使软件交付更安全和迅速,且能够带来更高质量的产品。企业从原来的无法衡量的隐性开发成本结构向更加清晰的成本结构转变,能够激发更多创新活力。
1.1.3 保持随时可交付
在现代软件开发的过程中,交付的对象除了代码,还会根据不同的运行环境产生不同的构建物。这些构建物可能是编译后生成跨平台的二进制可执行文件,也可能是包含产品及其运行环境的Linux系统镜像或Docker镜像。
要实现持续交付,需要保持代码和对应构建物随时可交付的状态。
保持代码随时可交付意味着开发人员在修改代码进行功能开发、迭代、Bug修复时,需要和当前稳定可交付的代码版本进行区分。我们一般会使用代码版本管理工具进行组织,例如常见的Git和SVN。
如果使用Git进行代码版本管理,那么实际一般以“分支”作为管理粒度。即要保持主干(Master)的分支随时可交付,那么迭代、Bug修复等都基于该分支创建的新分支进行开发。当分支的代码修改完成后,通过提交Merge Request的形式将这些分支与主干进行合并,以此来保持主干的分支随时可交付。
当主干的分支被修改后,则立即自动执行完整的自动化测试及构建过程。在经过Staging环境验证后,当前版本的构建物就是稳定可随时交付的。这些稳定版本的构建物也需要进行统一的版本化管理,存放至构建物仓库。
通过以上手段,即可使代码及构建物保持随时可交付的状态。
除了保持软件随时可交付的状态,我们容易忽略一个非常重要的人为因素:构建物的具体交付行为一般由交付或运维团队实施,如果交付周期过长,那么会导致发现和解决问题的周期变长,进而导致循环反馈效率低下。
我们可以用提高发布频率的方法解决以上问题。
1.1.4 解决问题:提高发布频率
除了控制更小的修改单元,我们还可以通过“早发布,常发布”来解决潜在的人为因素导致的交付周期过长的问题,即提高发布频率。
与基于功能的发布策略相反,提高发布频率是基于时间的发布策略。这种“早发布,常发布”的软件工程哲学最早是由Eric S. Raymond在其1997年的《大教堂和集市》一文中提出的。他在文章中提到,“提早发行,经常发行,听听客户的声音”。
这种理念最初用于Linux内核开发及其他大型开源软件的开发,而后流行于现代软件开发工程中,大众熟知的GitLab、Docker、Ubuntu等均采用基于时间的发布策略。
最著名的对基于时间的发布策略的研究是2007年剑桥大学Martin Michlmayr博士的论文《开源软件项目中的质量改进:探索发布管理的影响》,他通过对Debian、GCC、Linux软件的研究,得到了这种发布管理方式能够显著提升开源软件的开发效率及质量的结论。
这种发布策略意味着固定的发布周期,面向时间周期开发可交付的产品。尤其是对于需要部署的Web应用来说,开发人员和交付团队不再以版本化的功能作为唯一驱动,可以更加独立自主地执行工作。另外,这种发布策略能够尽早发布较小且较重要的功能,每次发布的功能有限,这样也就降低了单次发布的难度。固定的发布周期也有助于团队找到合适的交付节奏,高效地完成开发和交付工作。
因此,提高发布频率除了能够让客户更好参与到软件迭代中,还解决了持续交付过程中因部署环节周期过长而导致的低效循环反馈问题。
1.1.5 自动化持续部署
谈到自动化持续部署,就不得不引入一个新的名词——流水线(Pipeline)。
自动化持续部署是部署行为的抽象描述,需要由某个实际的对象来具体实现“自动化”及进行“部署”,用于描述“自动化持续部署”具体行为的承载对象称为“持续部署流水线”。
持续部署流水线的自动化可以由多种形式驱动,常见的有Git仓库触发流水线、持续构建(Continuous Integration,CI)触发流水线等。
此外,流水线还能够提供编排持续部署阶段的功能。
借助流水线,我们能够轻松地实现自动化持续部署,还能实现不同的部署策略,例如滚动更新、蓝绿部署、灰度(金丝雀)发布、自动回滚等,这些具体的部署策略都可以附着在流水线中运行。一条典型的自动化持续部署流水线如图1-2所示。
图1-2 自动化持续部署流水线
一般来说,持续部署流水线是可编程的基础架构,团队可以根据自己的发布频率和场景对流水线进行编程,例如控制流水线某个步骤执行前等待一段时间、限制流水线部分阶段或者行为需要更高的人工审核权限。
在提高构建物的生产频率后,采用自动化持续部署是必要的,在固定且快速的交付周期内,自动化持续部署脱离了人工部署行为,使部署周期更加可控。所有经过自动化测试符合部署条件的代码都将快速发布到生产环境中。自动化部署完成后,便完成了一次完整的交付和部署环节,进而驱动下一个迭代周期。