互联网新概念:云原生

在进入21世纪后成长起来的新一代公司,已经找到了更好的做事方法。谷歌公司是一个伟大的创新者,它和其他一些互联网巨头一起,发展出了新的IT模式。据估计,谷歌在其世界各地的数据中心里运行着200万台服务器,那么是怎么做到得呢?

图展示了一个系统的示意图,它几乎经常描述的“坏”系统相反

这个系统的目标如下:

■ 简单且可以频繁地在生产环境中进行发布。

■ 运维具有稳定性和可预测性。你应该已经熟悉了这些因素的反面因素:

■ 碎片化的变化会造成发布缓慢和不稳定,可重复性则相反。

■ 有风险的部署会导致生产环境的不稳定及部署困难,安全部署则会带来敏捷性和稳定性。

■ 将依赖于不变环境的实践和软件设计,替换成为环境不断发生变化做好准备的,可以从根本上减少用于“救火”的时间。

在图中,你会注意到一个名为“持续交付”(Continuous delivery,CD)的新实体。那些在新的IT运维模型上获得成功的公司,已经重新设计了它们的整个软件开发生命周期流程,并将持续交付作为主要的驱动力量。这样可以显著简化部署过程,并为整个系统带来很多好处。首先解释什么是持续交付,以及软件开发生命周期中的变化如何催生了持续交付,以及持续交付所带来的好处。

持续交付

亚马逊可能是频繁发布方面的极端例子。据说平均每一秒amazon.com就会发布一次代码到生产环境中。

你可能会质疑在自己的业务中是否需要进行如此频繁的发布。当然,你可能不需要每天进行86000次发布,但是频繁发布能够给业务带来更好的敏捷性和可实施性,这两者都是衡量一个企业是否强大的标志。

在我们定义什么是持续交付之前,先了解一下什么不是持续交付。持续交付并不意味着将每次的代码更改都部署到生产环境中。它意味着可以在任何时候部署尽可能新的软件版本。

开发团队会不断地向软件中添加新的功能,但是每添加一个功能,它们就会进行一个完整的测试流程(自动化的!),并将发布代码打包,从而确保软件已经准备就绪。

图演示了这个测试流程。请注意,在每个流程的“测试”阶段之后没有“打包”阶段。因为打包和部署已经直接包含在了开发和测试过程中。

上图所示的流程与下图所示的传统软件开发实践形成了对比。在传统的软件开发过程中,因为前期进行了大量功能的开发工作,所以一个单流程所用的时间会长得多。在开发完一组预先确定的新功能之后,需要进行一个时间很长的测试环节,并为发布软件做好准备。

让我们假设图1和图2所示流程覆盖的时间跨度是相同的,每个流程从左边开始,“准备部署”阶段在最右边。如果你单独观察最右边的时间点,可能看不出有什么不同,大致相同的功能会在大致相同的时间点交付。但如果你深入研究,会发现两者之间具有显著的差异。

首先,在图1中,何时进行下一次软件发布依赖于商业决策,而不是由一个复杂、不可预测的软件开发过程决定的。例如,假设你了解到一个竞争对手计划在两周内发布一个与你的新产品类似的产品,公司因此决定立即发布自己的产品。你的业务部门说:“让我们发布吧!”如图3所示,将这一时间点叠加到前两张图上,就会看出显著不同。

如果你使用的软件开发方式支持持续交付,那么可以立即发布软件的第三个迭代版本(图3的上图中以斜体字显示的时间点)。虽然计划的所有功能还没有开发完毕,但第一个上市的产品(只含有一些重要功能)会有很强的竞争优势。再看看图3的下半部分,你会发现对应的企业非常不幸,IT流程成为拦路虎,而不是赋能者,从而让竞争对手的产品率先打入市场!

最后,不得不承认,我们不擅长估算构建软件所需的时间。一部分原因是因为我们天生乐观。我们通常会对正常的情况进行规划,即编写第一行代码之后就可以正常运行。(当我这样说的时候,你马上就意识到了它的荒谬之处,对吧?)我们还会假设自己会完全专注于手头的任务,我们每一整天都在编写代码,直到完成任务。事实上,我们可能会迫于压力,在市场需求或其他因素的驱使下,同意更加激进的时间计划,导致通常在我们开始之前就已经落后于时间计划了。

对于传统的开发流程,这些因素会让你错过原计划的发布时间点。图4的第一行描绘了理想中的软件发布计划,第二行显示了实际花费在开发上的时间(比计划的要长),最后两行显示了你可以做出的选择。一个选择是坚持计划的发布时间点,为此而压缩测试阶段的时间(打包阶段的时间通常无法压缩),代价当然是牺牲软件的质量。另一个选择是保持质量标准并推迟发布日期。这两种选择都不令人愉快。

可重复性

在前面,我们讨论了碎片化的变化会对软件开发造成的不利影响。因为必须不断适应各个部署环境之间的差异,以及部署构件之间的差异,所以部署过程变得非常困难。这些不一致使系统在生产环境中保持正常运行变得非常困难,因为不管是环境还是软件的某个功能发生变化,都必须进行特殊处理。如果你无法可靠地重现故障时的环境和配置,系统的稳定性就会一直无法保证。

可重复性会带来两个巨大的好处:有助于系统部署和保证系统的稳定性。正如在前面所说的,迭代流程对于频繁发布来说是必不可少的,并且通过在每次开发/测试的迭代过程中控制变化风险,可以缩短整个新功能的交付时间。而且,一旦系统在生产环境中运行,无论你是处理故障,还是进行扩容,基于完全可预测性的部署能力会减轻系统的巨大压力。

那么如何实现这种可重复性呢?软件的优点之一是很容易改变,而且可以快速实现,但这也正是过去我们制造出大量不可控变化的原因。为了实现所需的可重复性,你必须遵守一定的规则。具体来说,需要做到以下几点:

■ 控制部署软件的环境。

■ 控制正在部署的软件,也可称为可部署构件。

■ 控制部署流程。

控制环境

首先,你必须从标准化的机器镜像开始。在构建环境的过程中,必须始终从一个已知的起点开始。其次,为了部署软件而对基础镜像进行的更改,也必须通过编程的方式来实现。例如,从一个基础Ubuntu镜像开始,并且软件需要Java开发工具包(JDK),那么可以通过脚本将JDK安装到基础镜像中。此模式也经常被称为基础设施即代码(Infrastructure as Code)。当需要一个环境的新实例时,可以从基础镜像开始并执行脚本,这样就可以保证每次都拥有相同的环境。

这个流程一旦建立,之后对环境的任何更改也必须按照同样的方式进行。如果运维人员经常通过SSH进入计算机更改配置,那么就违背了严格管理环境的原则。你可以使用多种技术来控制初次部署后的环境变更,例如,禁止通过SSH访问正在运行的环境;或者不禁用SSH,但是设置为当有人通过SSH进入时,计算机会自动下线。后者非常有用,因为该方式允许进入机器检查问题,但不允许对运行中的环境进行任何更改。如果需要对运行中的环境进行更改,只能更新标准的机器镜像及应用运行时环境的代码,这两者都通过某个源代码控制系统或者类似的系统进行管理。

可以由不同的人来负责创建标准化的机器镜像和基础设施即代码的脚本,但是作为应用程序的开发人员,你必须使用这样的系统。在软件开发生命周期早期是否使用这样的实践方式,对企业是否能够在生产环境中有效地部署和管理软件有着重要影响。

控制可部署构件

为了实现高效、安全和可重复的生产环境运维,在整个软件开发生命周期中使用单个可部署构件是很重要的。在开发期间构建并通过回归测试的JAR文件,就应该是部署到测试环境、预发布环境和生产环境中的同一个JAR文件。为了实现这一点,需要按照正确的方式来组织代码。例如,属性文件不包含任何与环境有关的值,而是定义一组需要后续注入值的参数。然后可以在适当的时候给这些参数赋值,或者通过正确的来源生成值。作为开发人员,你可以决定如何从环境中抽取适当的可变因素。这样只需要创建一个可部署的构件,在整个软件开发生命周期中使用它,从而改善敏捷性和可靠性。

控制流程

在保证环境一致性,以及创建一个可以贯穿整个软件开发生命周期的可部署构件之后,剩下的就是要确保这些部分以可控、可重复的方式组合在一起。图2.11演示了我们期望的结果:在软件开发生命周期的所有阶段中,可以根据需要放心地增减任意数量、完全一致的运行副本。

上图里没有碎片化的差异。可部署的构件、应用程序,在所有部署过程和环境中都是完全相同的。运行时环境虽然在不同的阶段有差异(由不同深浅的灰色表示),但是底层是相同的,只是使用了不同的配置,例如连接的数据库地址。在软件开发生命周期的任何一个阶段中,所有的配置都是相同的,它们的颜色完全一样。这些一致化的运行单元,正是由我一直讨论的两个实体组成的,即标准化的运行时环境和单个可部署的构件,如下图所示

安全部署

当你在生产环境中进行试验时,也需要有合适的“安全网”。这张“网”实际由运维实践和软件设计模式共同编织而成,再加上可靠的软件工程实践,比如测试驱动开发,就可以将失败的概率降到最低(但完全消除它并不是我们的目标)。预期出现失败(失败总会发生)大大降低了它成为灾难的可能性。也许一小部分用户会收到错误消息,需要刷新系统,但是整个系统仍然在运行。

实现这种A/B测试的要点如下:

■ 软件构件必须实现版本控制,并且版本必须对路由可见,以便恰当地切分流量。此外,由于会对数据进行分析,以确定新的部署是否稳定,以及是否实现了所需的结果,所以所有数据必须与软件的相关版本关联,以便后续进行对比。

■ 用于分析新版本工作情况的数据可以有多种形式。一些度量指标完全独立于任何实现细节,例如,请求和响应之间的时间延迟。而其他一些指标会关注正在运行的进程,报告正在使用的线程或内存数量等信息。最后,一些与业务有关的指标会用来判断部署效果,例如每笔在线交易的平均金额。有些数据可能由软件运行的环境自动提供,因此不必编写代码来生成它们。数据度量指标的可用性是首要考虑的问题,请思考一下如何生成支持生产环境试验的数据。

■ 路由是并行部署的一个关键因素,而路由算法属于软件的一部分。有时算法很简单,比如将流量按照一定百分比发送到新的版本,也可以通过对基础设施的一些组件进行配置来实现路由。有些时候,可能需要更复杂的路由逻辑,并需要编写代码来实现。例如,可能需要测试一些地理位置上的优化,并且只想将相同地理位置的请求发送到新版本。或者,可能只是希望向高级客户公开某个新特性。路由逻辑无论是由开发人员来实现,还是通过环境配置来实现,它都是开发人员要考虑的头等大事。

■ 我在之前提过要创建更小的部署单元。与其像电子商务系统那样一次部署很多功能,例如,类目服务、搜索引擎、图像服务、推荐引擎、购物车和支付模块等,不如将部署内容限定在一个更小的范围内。你可以很容易地想到,与支付相关的模块相比,新的图像服务对业务而言风险要小得多。应用程序的正确组件化(或者用现在常用的名词“基于微服务的架构”),与系统的可运维程度直接相关

猜你喜欢

转载自blog.csdn.net/wmq880204/article/details/114854336