应用部署是工程人员(包括开发、测试和运维)每日面对的重要问题之一。尤其是在应用交付频率越来越高的当下,工程人员经常需要花费巨大的成本和心血来完成频繁的应用部署工作。在过去半年里面,我们接触了大量的企业客户,他们来自不同的行业,有着不同的规模,但我们发现他们在应用部署上面都有一个类似的困惑,即是应该选择增量部署还是全量部署。这里,我们希望展开阐述一下我们的观点。在开始讨论这个问题前,让我们首先重新明确两个问题。
- 部署(deployment)还是发布(release)?
部署一般指把应用或者服务“安装”到目标环境(开发、测试或者生产)中,而发布则应指把应用或者服务交付给最终用户使用。尽管这两个动作(尤其是在生产环境中)经常是同时发生的,但它们理应是两个完全不同的阶段。实际上一个好的持续交付流程恰恰应该把“部署”和“发布”解耦,变成两个可以独立控制的阶段。 - 部署的内容包括什么?
无论是增量部署还是全量部署,都需要关注其部署的内容是什么,尤其是在广泛讨论微服务的今天。如果从部署角度看,我们把任何可以独立部署的内容称为一个“部署单元”。一个部署单元可以是一个模块,几个模块的联合体或者一个完整的应用,而如何划分则要视具体场景来定。一般来说,划分部署单元的最佳实践为一个可以独立演化、部署且和应用其他部分松耦合的集合。
在明确完上面两个问题之后,我们可以把这里要讨论的主题更准确的表述为“对一个部署单元应该采用增量还是全量方式进行部署?”
增量部署
增量部署一般指在每次部署过程中首先提取当前版本和即将部署版本之间的增量(包括代码、可执行文件或者配置等),并在部署过程中仅更新增量部分。这种部署方式的常见部署流程如下:
- 利用代码管理工具(SVN、GIT等)提取两个版本之间的增量,并结合其他方面的增量变化。
- 按照增量部分制定具体的部署方式,编写部署脚本,并准备增量部署包(包括混淆代码等)。
- 分发和部署增量部署包到已经运行上一版本的目标环境,完成系统的版本升级。
目前,这种部署方式在不少企业内部使用,尤其是在以动态语言(如PHP、Python、JavaScript等)为主的团队中使用得更为广泛。团队选择这种部署方式的主要原因可以总结如下:
- 部署速度快。增量部署每次仅对增量部分进行更新,无论是文件分发还是配置更新的内容都会更少,部署需要的时间也就相对较短。
- 减少变化量。增量部署可以减少对于整个系统的变化幅度,很多已经完成的配置工作不需要每次重复设置。从而可以避免误操作,降低部署失败率。
- 提高安全性。增量部署每次只会涉及到增量代码部分,不会直接暴露系统的整个代码部分更新,避免系统代码泄露的风险。
增量部署与全量部署
在仔细比较这两种部署方式之前,我们有必要思考一下对一个部署操作来说,哪些考量是最为重要和核心的,也是我们应该优先保证的。在我们看来,一个部署操作最为重要的考量应该是部署操作的“可重复性(repeatable)、可预测性(predictable)和可回滚性(undoable)”,其次才是考虑部署的效率和安全性。之所以这么定义优先级的最主要原因是当下系统部署普遍面临着下面“三多”挑战:
- 部署操作次数多。持续交付已经成为企业的普遍追求,也是企业IT服务能力最核心的指标。当部署次数多起来,每次部署的可预测性和可回滚性则是最基础的保障,不然很难实现频繁的部署上线。
- 部署环境变化多。企业IT基础设施的云化和动态化,部署环境已经不仅仅面临着传统的开发、测试、预发和生产(DTAP)这四类环境迁移的挑战,而且每个环境内部基础设施的变化频繁得多(增量和全量部署的场景都会频繁出现和切换)。这对于部署可重复性提出了非常高的要求。
- 部署操作人员多。随着自动化部署和持续交付的普及,越来越多的人需要有执行部署操作的能力。这不仅包括传统的开发、测试和运维人员,还包括公司管理人员,甚至市场及销售人员都需要有自己快速部署一套系统(用于演示或者其他目的)的需求。这也要求部署操作有很好的可预测性和可重复性。
如果对照如上要求,我们会发现“增量部署”在满足这些需求上有不少的挑战,具体可以如下分析:
- 增量部署对于任何部署外的更新非常敏感,降低了部署流程的可预期性。
在日常工作中经常会出现为修复一个问题而临时修改运行环境的部署外更新,一旦这些部署外更新未及时考虑到增量计算中,非常容易导致之后的增量部署失败。全量部署过程则会完整执行完整个环境的配置、初始化以及部署工作,对于这些部署外更新有更好的容错性。 - 增量部署让回滚操作变得非常不容易。
每次回滚都需要逆向计算增量部分,然后做回滚操作。及时的备份策略有机会降低这个难道,但是如果需要回滚多个版本仍然是一个巨大的挑战。 - 增量部署无法直接满足从头部署最新系统的日常需求。
在云环境中资源的动态变化(尤其是虚机的增加和减少)逐渐会成为一个常态,用户时刻都可能面对两种场景的部署要求:从上个版本升级到最新版本,或者从零重新部署最新版本应用。显然,这两种部署需求一个增量更新,另一个则是全量更新。
如果团队以“增量部署”作为日常部署方法,就必然需要面临维护两种部署模式,增量部署和全量部署。而如果统一使用“全量部署”则可以避免这个问题,一套部署流程统一两种不同部署需求,增强部署的可重复性。
如上这些挑战都让增量部署模式越来越难满足高频部署的工程需求,越来越多的自动化部署系统及工具都选择了全量部署模式。当然,在选择全量部署模式时,前面提到的全量部署模式弊端也需要认真考虑。简单来说,我们可以通过下面这些策略解决或者缓解这些弊端:
- 提前在本地准备好全量部署所需要的所有材料(部署包、配置文件等)后再开始部署操作。这样可以让部署操作都变成本地操作,大大提升部署的效率和速度。
- 使用如灰度发布或者负载均衡等方法降低全量部署对于应用可用性影响。同时还能够有效解耦部署和发布两个阶段,提高应用发布的灵活性。
如何选择部署模式?
如前所述,越来越多的部署系统或者工具都选择全量部署模式,但这并不意味着增量部署没有用武之地。相反,很多状态相关的部署单元(例如,数据库)基本还无法采用全量部署模式,其中最为典型就是“数据库Schema”的更新操作。对于这里部署需求,工程人员基本只能采取增量部署,并需要小心设计部署的流程、脚本及回滚策略。
总结来说,对于现代系统中绝大部分状态无关的部署单元(应用、模块,微服务等),全量部署一般应是最优的选择。而状态相关的部署单元(数据库等)则依然适合增量部署逻辑。为此,在FIT2CLOUD的产品中,我们的代码部署功能选择了全量部署模式,并在系统内部以及和Jenkins对接的插件中支持脚本部署模式,为需要精细控制的增量部署单元提供支持。