需要单机还是集群部署_做好部署才能将研发流程的内/外循环高效运转起来

f0a77c533ad21ae611351d0dd48d95d8.png

本文章谈论的是《每个程序员应该知道的97件事》里的第20件事: 尽早部署并时常部署(Deploy Early and Often)

老规矩,先分享书里这个章节的大意,一来快速点题,二来为了感谢它给了我“借题发挥”的机会。

我们需要尽早地建立安装/部署流程,而不是将这个工作放在项目的结尾。尽早并经常进行部署,能够及早发现程序在产品运行环境的问题,减少项目风险并提高工程质量。

https://97-things-every-x-should-know.gitbooks.io/97-things-every-programmer-should-know/content/en/thing_20/

原文推荐:⭐⭐⭐

个人简评:感觉故事有点老。我觉得现在研发工程师不会低估部署流程的难度,但是一般我们也不会认为这是一个特别有风险的事情,因为现在成熟的工具和最佳实践太多了。

在团队的工程研发过程中,有一个稳定而高效的部署流程是很关键的。它是团队高效研发的重要基础。本文会从研发流程的inner loop和outerloop去说说部署的作用和一些实践经验。

研发流程的inner loop和outer loop是什么?


我没找到是谁第一个定义了软件工程里的inner loop和outer loop。我第一次听到这两个词,应该是在两年前左右。当时我主要做Cortana技能的开发体验,因此比较关注开发的敏捷性。

简单来说,inner loop是从本地编码,本地构建、本地调试和测试到完成代码提交的迭代过程,而outer loop指的是从代码的变更逐步部署到产品环境的迭代过程。注意,这只是描述大致的研发过程模式,不同的具体研发场景各有差异。

b7491c70da84693ddfdb3cdc101922e2.png

后文会主要中文名称“内循环”和“外循环”指代inner loop和outer loop。虽然我个人觉得有点别扭,但我还是希望多使用中文。

熟悉和理解研发流程的内/外循环是工程素养的重要体现


如果新加入一个工程项目,除了需要快速了解项目的业务领域概念和模型之外,其次就是需要了解研发流程中的内循环和外循环。我们给项目组新人安排的第一次编码工作往往是需要极少改动的,主要是为了让他们能够快速熟悉代码工程的本地编译、调试和测试到提交代码(内循环),以及代码提交后是怎么一步一步部署到线上环境并完成自动化的功能验证(外循环)。

如果我们是组里较资深的工程师,我们更是需要深入理解当前内/外循环,清楚目前哪里是研发效率的瓶颈。如此,一来我们能够更准确地评估工作所需要的时间和意识到其中有可能出现风险的环节,二来我们能够设法解决瓶颈环节提高团队整体的研发效率(后文会具体分享一些经验)。

如果是新创建的工程项目,前期就必须建立起完备的研发流程的内/外循环,否则团队研发工作难以有效开展。这工作一般会交给组内资深的工程师单独完成;一来为了保证研发流程质量,二来这工作多数是一次性的(one-off)而且难以多人并发进行。下一个部分会从部署的角度说说建立良好的内循环和外循环要做什么。

做好部署才能将研发流程的内/外循环高效运转起来


在此以一个分布式集群运行的网络服务为例。

说起部署,最直接想到的应该是将服务部署到产品环境。为了确保核心业务没发生故障,部署后会进行必要的功能验证测试。这可能是最简单的外循环了。然而,实际的产品研发并不会这么简单,因为部署后发现问题代价太大。

为了更早地发现服务变更可能出现的线上问题,我们往往会在产品环境部署之前加入预发布环境(PPE,Pre-Production Environment)的部署流程。当新的变更在预发布环境通过了功能验证测试,我们才会认为变更版本是能够继续部署到产品环境上。

当我们觉得预发布环境上的自动化功能验证测试无法检测出严重的业务退化,我们还能够将一些内部用户的流量接入预发布环境并确保每个新版本在预发布环境运行一段时候没有人反馈问题,才能够继续往产品环境部署。此时,为了确保每个新代码提交都能经过真实集群环境的自动化功能验证测试,我们往往还需要创建一个测试环境的部署流程。

不过,当自动化功能验证测试做得好的话,外循环有预发布环境部署和产品环境部署就足够了。

除了上述外循环的环节,内循环也和服务部署密不可分。

首先,内循环的质量不能只看迭代完成的周期长短和数量多少,每次提交的变更质量是非常重要的,因为代码回滚的代价一般是巨大的。如果代码提交前的自动化功能验证测试做的够好,我们便能够大大减少提交错误代码的风险。

怎么做?一般常见的做法是在代码提交之前将变更部署到临时占用的测试集群的某个机器上完成自动化功能验证测试(比如加入Pull Request的buddy build流程);只有通过测试才能够完成代码提交。这里你需要深入理解部署机制和部署环境。

其次,相比在buddy build发现问题,我们更希望能够本地环境的调试/测试中发现问题。内循环效率的重要指标便是开发人员是能否快速完成构建和本地调试/测试环境的搭建。这个时候,我们需要有一个本地单机环境的服务部署机制;由于它无法做到和集群环境完全一致,所以也是很考验我们对服务部署环境需求的理解的。

总之,研发流程中内/外循环需要大大小小的部署流程,有一个良好的部署基础设施建设才能真正地将这两个循环高效地运转起来。

随着应用应用容器化技术(比如docker)的普及,配置应用部署流程的复杂度小了许多。然而,我们还是需要针对不同部署环境(比如本地单机,测试集群的临时单机、预发布集群和产品集群)做许多定制化。理想情况下我们希望本地单机环境尽可能逼近产品集群单机环境,如此在内循环中我们就能完成主要的功能验证,加快整体迭代效率。实际上我们很难做到这一点。而且,当我们的服务上下游依赖越多,所在的技术架构越是复杂,本地单机环境部署的难度便越大,所要求技术能力也越高。

真实的研发流程会更加复杂


上面说的只涉及单个服务的研发流程。常见的互联网应用的研发流程会更加复杂,因为其技术架构一般由大量的服务构成。每个服务都有自己单独的集群环境和代码库,以及各自大小不一的内/外循环(如下图)。每一个服务的部署流程看似各不相干,但它的设计和实现还得以整个产品技术架构的功能验证策略以及上下游的部署机制为基础,比如我们需要理解在不同部署环境下该如何和上下游服务依赖互连进而能够进行产品端到端(End-to-End)的功能验证,等。

1f18991e8c1055c8114ed615f81e5e0d.png

虽然服务的拆分是为了业务研发流程的解耦,但是我们无法避免一些需要改动多个服务的新功能。在单个服务的外循环之上,一般还有以功能为中心的循环(feature loop),它有时需要多个服务的协作。新功能的部署一般会使用功能开关(feature switch)机制,以确保新功能在各个相关服务的代码实现能够听从统一指令同时打开或关闭;功能开关的配置一般会跟产品的用户测试系统集成,如此便能做灰度测试或A/B测试,从而能够从部分真实用户行为数据中了解功能是否按预期改善了产品指标。

改善内/外循环的方式也不仅仅是对当前应用的内/外循环的优化,对内/外循环进行拆分也是经常会考虑的方向。比如大量独立的业务研发工作在同一个大的内/外循环内,我们既可以保留同一个外循环(发布部署流程)但将单一内循环拆分成多个(比如,代码提交时只做相应的功能验证测试,或者拆分出不同的代码库最后将业务打包发布提交到共同的服务发布流程上,等),也可以直接进行服务拆分让不同业务的研发有自己的服务及其研发流程。

203c829b1e94b8f089070b2ffd724557.png

注意,关于上述两种优化方式,我们不能一概而论地认为哪一种更好。我们需要具体问题具体分析。在微服务理念盛行的今天,服务拆分的方式看似更加合理。然而,新增服务的代价并不小,即使微服务的基础设施建设已经比较完善。并不是所有团队都愿意承担服务部署运维相关的工作,因为需要建立完备的部署机制而且还需要安排人员负责相关运维工作。

分享我工作经历中的一些经验


早期Cortana的核心后台和Bing业务后台在同一个庞大的单体(monolithic)服务里。在这个服务的研发流程里,服务内的不同应用能够建立各自的内循环,从而服务内不同应用的研发相互不影响。但是,当时的Cortana并不满意这研发流程,比如本地构建太慢,调试和测试的体验不好,开发体验很难建立多轮会话逻辑的编程模式,无法控制业务变更的部署,等;因此我们将核心的会话引擎功能抽离出来,放入创建的一个新的集群服务中;我们还创建了技能开发框架,提供给技能组更良好的多轮会话的编程模式。此后,会话引擎及技能的研发有了更轻量级和便捷的本地开发体验(内循环),我们也能够自主控制部署(外循环)。

有了独立的集群服务后,我们的内循环效率快速提高,然而外循环的质量并不是很好;变更的部署经常引发故障,于是总需要回滚,或者先切换流量到其他数据中心然后做快速的修复。为了解决这个问题,我们加强了部署的功能验证测试,确保足够的测试覆盖率,使得过去很多类似问题能够预发布环境就被发现。在那个时候,产品环境部署的周期很长,一周才一到两次。一般而言预发布环境发现问题后我们还有足够的时间去处理。

后来,我们希望缩短外循环周期,每个工作日都做一次部署。这个时候,我们不能再等预发布环境部署后才发现问题,因此我们需要设法改善降低代码提交的缺陷率。我们改进了buddy builld的验证过程:每个代码变更都部署到一台线上测试机器,然后运行和预发布环境部署后的功能验证测试。

随着技能的增多,内循环中各个技能之间,技能和平台之间的矛盾慢慢显现和激化,比如某一个技能的变更经常被另一个技能不稳定的功能测试影响,某一个技能的问题影响了整个服务的部署,等。后来我们提供了新的技能开发体验——支持技能以外部服务的形式接入Cortana业务平台。这样子,技能有了自己的研发流程,其内/外循环不再受其他技能和平台的变更所影响。

是不是觉得这个故事就结束了?其实并没有。当技能作为服务拆分出去之后,平台的变更提交和部署不再运行这个技能的功能验证测试,那么如何有效保证平台的变更不破坏产品环境的技能服务成了平台组要面对的新问题。此外,拆分出技能服务之后,技能组一直花着不少精力去维护研发流程的内/外循环,因为这部分的基础设施的问题一直层出不穷。

上面这些例子不仅是在简单分享一些经验,我想传达的更多的是改善研发流程是一个会持续追求的过程,而深入理解当前的内/外循环是关键。


如果你耐心阅读到这里,愿你所在的研发流程的内/外循环越来越快且平稳。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值