持续集成、持续交付、持续部署

三丰 soft张三丰 “最后一哩”问题   持续集成解决了软件开发中的部分问题,但还有更为重要的一部分有待解决,即“通过什么样的方法,可以让软件尽快地在真正的生产环境下运行,从而实现软件的价值”。在软件开发过程中,“从功能开发完成开始直到将其部署至生产环境中正式运行”这一阶段被称为“最后一哩”。如果从一开始就对产品发布足够重视的话,那么这“最后一哩”可能只需要几分钟,甚至几秒钟就完成了。然而,事实上大多数项目在这一阶段会花上几个星期,更有甚者可能会是几个月。   为什么会这样呢?对于复杂软件来说,无论什么环境中的部署(测试环境,试运行环境,还是生产)都很困难。当软件第一次被部署到非开发环境去测试,或者当软件功能及其环境有较大变化时,通常都会暴露出很多问题。而在做用户验收测试时,常常会发现更多的问题,例如不能满足非功能需求,用户操作不方便,功能与用 户真正需要的东西相差太远等等。而开发团队只有修复这些缺陷后,才能再次部署测试。于是,这个过程会不断反复,直至该软件足够稳定,才可以部署到生产环境中。   即然部署到测试环境都这么困难,那么在生产环境中部署的风险岂不会更大吗?而且,更为严重的是:当生产环境部署出现问题时,摆在你面前的选择就所剩无几啦(通常是回滚到以前的状态,而“回滚”这段时间的停机成本是相当高的)。 上述原因就会导致大多数组织对产品的发布采用“保守策略”,即降低软件的发布频率,这也导致两次发布之间的版本特性差异相对较大。这样一来,发布风险并未因发布间隔时间加长而降低,反而更高了。当各方面的因素结合在一起时,软件发布这一环节就变得昂贵而又缓慢啦。而通常“发布过程与频率”决定了产品在市场中的位置。   那么,如何更好地解决“最后一哩”这一问题呢?实现持续部署!即将持续集成实践扩展到整个软件生命周期频繁且规律性地自动构建代码并将其部署到测试环境中,然后通过一系列的测试,选择适当的版本部署到预演环境中试运行,最后选择稳定的版本部署到生产环境中,从而使开发团队尽早从最终客户那里得到反馈,而最终客户尽早得到软件的价值。 墨菲定律大家都不陌生,越是担心什么就越会发生什么;在多团队协作时,比如系统对接时,我们都会担心对接是否顺利,往往也不枉我们担心,时常我们会被集成折磨得焦头烂额。有很多团队只是担心,并没有拿出有效的措施去避免这种事情发生,以至于延长了交付时间。既然担心,我们何不及早集成,把问题先暴露出来?

目前多数公司都已经使用了版本管理工具来管理源码,比如GitLab、SVN 等版本管理工具。在版本管理这一块,公司会根据自己的实际情况来制订版本管理办法。对于持续集成来说,业内建议只维护一个源码仓库,降低版本管理的复杂度。开发人员持续提交自己的修改,自动触发编译,自动集成,自动进行自动化的测试,及早反馈集成过程中的问题,就能更好地防止出现平时不集成、集成就出问题的现象。

通过自动化的持续集成,把管理流程固化;保证集成的有序性、可靠性;减少版本发布的不合规性(开发或者测试手动打包,可能一天打多个包,更新多次,测试不充分),保证版本可控,问题可追溯(至于哪个版本出现的问题,可以回溯)。

一旦把这种持续集成的过程固定下来,形成一个自动化过程,就具备了持续集成的能力,软件交付的可靠性就大大增强,这无形也是一种竞争力。这种竞争力保证了集成的有序性、可靠性。过程的自动化抛弃了人工,降低了出错率,提高了速度,自然会节省成本。

对于大规模的CI&CD,一旦系统数量、实例数量上去后各种问题就都来了。下面列举几个主要的问题。

1)更新问题。更新一次要耗费大量精力,很多企业都是晚上更新,员工得通宵加班,还不能保证更新没问题,不具备快速大批量部署的能力。

2)部署包(jar 包、war 包、ear 包等)的管理问题。为了保证版本可追溯,出错后能够回滚,我们需要保存各个历史版本,而且方便下载。

3)版本的安全性。传统上以Java 语言开发的系统多数以jar 包、war 包、ear 包的方式发布,容易被篡改(人为修改,传输过程不完整);通常我们用md5 来验证完整性,但包与md5 对应关系的管理并没有系统化,往往在出问题后人工进行md5 验证。

4)主机管理问题。系统部署到哪些机器上需要进行主机管理?在部署时人工选择部署到哪台主机显然不是一种明智的方式,能否自动进行调度?同时,不会产生有些主机性能堪忧、有些主机空闲导致的负载不均情况。

5)端口管理问题,在部署Java 项目时我们并不建议设置太多的JVM 内存,因此一台主机上往往能够部署多个应用实例,同一台机器上多个实例的开放端口就必须不一致,于是端口又成了需要管理的资源。有人会说可以选用虚拟机,一台虚拟机上只部署一个实例。当然,这也是可以的。实际上,多数企业也是这么做的。这种做法也有弊端,虚拟机虽然帮助做了隔离,但会损失一些主机性能;虚拟机要使用内网IP,这样IP 又成了稀有资源,当部署多个实例时IP 会不够用。

6)负载均衡管理问题。不管是在主机上部署多个实例,还是利用虚拟机来部署多个实例,在做集群时,都会通过代理(Nginx、Apache、LVS……)软件来做负载均衡,因此我们需要把各实例的访问地址配置到负载器的配置文件中。实例数少的时候手工配置还可以接受,多了就没法手工配置了。当然,方法也会有,比如用Etcd+Confd+Nginx(HAProxy)来做服务发现,我们需要自己部署一套工具,并进行维护,复杂的框架提高了对运维人员的要求。

7)服务伸缩问题。当服务访问量上去后,要能够具备快速扩充的能力;当访问量下去后,要能够具备缩小服务规模的能力,收放自如。这种弹性的服务能力显然是通过工具来完成的,手动完成是不可能的。

8)IP 管理问题。当大规模部署后,IP 资源会成为稀有资源,如何用更少的IP 来部署更多的服务?IP 的分配及管理显然不能人工完成,那样效率太过低下,这就需要一个IP 管理工具。

DevOps静态架构

CI&CD 流程

持续集成 的含义为:频繁的(一天多次的)将所有开发者的工作合并到主干上。 以图例说明持续集成的流程:

从图例上来看持续集成的流程就十分清晰了:

  • 开发人员提交代码到 Source Repository (源代码仓库),并通过 git hook 等
  • 触发 CI Server(持续集成服务器)的相关功能。执行 编译 -> 测试 -> 输出结果 的流程,
  • 向开发人员反馈结果的 report 可以看出,持续集成的 核心 在于 确保新增的代码能够与原先代码正确的集成。与后续要介绍的持续交付以及持续部署,其最主要的差别也就在于其目标不同。

持续集成的优势

和我们一直在使用的 阶段集成(完成一个阶段的开发后执行代码的集成) 相比, 持续集成 的策略能够为我们带来哪些好处呢?

  • 易于定位错误:每一次的代码集成都需要执行相关的测试工作,持续集成频繁的集成次数天然的将复杂的代码逻辑切割为了小块,也就使得每一次测试中遇到的错误能够更加容易的被定位;
  • 易于控制开发流程:更为细致的工作提交也就意味着更容易判断当前的工作进度,这对于管理者规划开发流程而言提供了一个有效的参考,同时也为开发人员省下了汇报工作的时间;
  • 易于CodeReview:对于大块工作的切分自然也有助于做 CodeReview;
  • 易于减少不必要的工作:build 以及 test 过程的自动化可以为你节约一大票的时间,从而投入到有价值的工作中去。 在传统的团队组织方式中,开发人员与运维人员之间是割裂开的,软件开发流程被分割为多个独立环节,分别由不同的人员执行。这使得软件开发过程中需要付出高昂的沟通成本,层层手动的流程将大量的时间耗费在了重复的劳动中。 在 DevOps 的指导下,不同技能的人员处在同个团队中,为了一个共同的软件开发目标而工作,更好的协同工作与自动化的手段能够优化整个 Code -> Build -> Test -> Release -> Operate -> Code 的循环。这一理念看起来很美,用图画来说明就构成了一个和谐友好的大圈,不过在实际应用中也许会遇到不少问题,例如不同技能人员之间相互沟通的额外开销、团队组织形式改变后为管理所带来的困难等等。这些问题大概等到真正将 DevOps 在开发过程中开展来才能做解答了。