一次服务上线

2018-02-24 15:46 更新

原文出处:http://weibo.com/p/1001643879307398512716
赖佳俊@LierD 

引言:

前面几节课程中,我们已经对常见的互联网技术,代码编写规范有了一定的学习和了解。这些技术以及规范在我们日常的开发中都是经常被使用到的。

这些技术的学习最终都是以应用为目标的,即需要把这些技术通过coding、软件系统的组合(资源、服务)等实现方式最终应用到线上环境当中。

对于小规模的开发团队,因服务规模比较小,服务的部署可能是很简单的——手动部署代码成本也可控。但对于数十人上百人的开发团队,大规模的分布式系统来说,原来简单的方式就难以运转了。我们急需一套符合软件工程原理的流程体系,来保证我们研发效率、上线服务质量。

今天我将结合微博平台目前的上线流程以及个人的一些体会,来介绍一下研发上线流程体系。

培训大纲:

工程师的日常工作内容

对于一个刚进入公司的新同学,肯定有好多问题:

  • 作为一个工程师,日常工作内容都包括哪些呢?我是否只要写代码就可以了?

  • 日常工作中,都需要与哪些同事打交道呢?

  • 日常工作中,是否有一些流程需要了解?如何推进项目、事情的进展?

作为一个系统研发工程师,我对日常工作的一个总结如下:

主线任务:完成需求开发,推进上线

业务需求:完成各个业务部门产品需求

改造需求:来源于部门内部技术方案、整体架构升级、改造需求

支线任务:提供其他的技术支持

线上系统问题跟踪

产品数据分析支持

我们可以看到,一个工程师日常最主要的任务即是完成各类需求研发工作。这部分的工作实际上包括两方面的内容:

(1)功能研发,也即通常所说的写代码

(2)推动上线部署

上线:

上线指的是将工程师开发的功能推动到线上生产环境中,为系统面向的用户所使用。

在一些中小企业中,因面向的用户总量不大,所需的服务器也无需太大,上线的过程可能会比较简单——开发工程师完成代码的编写后,编译,测试,手动部署可发布包到服务器上重启即可。

但在大型的互联网环境中,上述简单的做法面临着很多问题:

(1)分布式系统环境复杂,上线所涉及到的读写顺序、操作流程更复杂

(2)同一个系统可能由多个工程师维护,代码的变更涉及到多个功能、多个团队

(3)系统代码总量大,生命周期长,每一次的变更都有可能带来潜在bug以及严重的故障

微博平台历史上也曾发生过多次故障。通过对故障的分析和总结,我们发现约80%的故障都来源于系统变更。即每次上线变更其实都是隐含着很大的风险性的。

但是,作为互联网工程师,我们并不可能因为上线变更所可能带来的风险,就不进行新功能的研发,停止不前。相反,我们根据软件工程的开发流程原理以及业界的一些成熟的经验,结合我们的业务特点,以及自身历史上发生过的几次故障的经验,总结设计出了我们自己的一套的开发、上线流程。总的来说,我们期望能够:

(1)按时、高质量交付需求、功能实现到生产环境

(2)研发过程明确清晰,每一步都是经过充分review的

(3)上线过程步骤明确,较少运维误操作

(4)出事可操作,always have plan B,即使出问题了,我们也可以快速恢复

(5)对研发效率的影响尽可能小

研发上线流程说明:

我们的研发上线流程由多个步骤组成,需要一步一步完成。对于我们的研发工程师来说,他需要关注每一步的:

(1)该步的检查内容

(2)需要提供的明确的产出物

(3)该步相应的审查人员

如图1所示为研发上线流程的一个示意图:

图1 研发上线流程示意图

从图1中可以看到,整个研发流程由多个步骤组成,每个步骤都涉及到一个或多个人员。

因本节课更多的关注在上线部分的推动上,所以我们将从开发完成后开始详细介绍:

代码审查(Code Review):

即代码评审,意指待交付的代码经过除开发工程师以外的人员进行评审检查。

Code review的意义在于:

(1)帮助梳理逻辑,从而发现更好的实现方式

(2)帮助规范代码,保证线上代码的规范、质量

(3)减少因开发人员个人经验、疏忽所可能带来的bug

开发人员在提交codereview时候需要明确写出相关代码变更的需求来源、实现方案以及其他的能够帮助评审人员快速了解这次变更内容的文档、资料。

当然code review本身也是需要一定的规范制度的:

  1. 明确reivew点。reviewer需要从多个层次进行reivew:代码本身质量(编码风格,防御性编程等),代码功能逻辑实现,现有系统的影响(侵入、耦合、数据兼容性等)。需要避免单一角度review。对于上述的每一个层次的review,也需要有更细致的review点。

  2. 开发人员需要预留充足的review时间,避免在临上线前才提code review。在没有充足的review时间的保证下,很难对上面说的代码功能逻辑实现层次以及现有系统的影响进行充分细致的评估,code review非常容易沦为一个流程过场。我们的做法为上线前一天下班前为code review的最晚提交时间点。如果没有在这个时间点前完成code review的评定,则上线延迟。

测试(QA)

为了保证待发布代码的质量,我们在实际的工作中,会将测试交由单独的测试部门同事负责检测。测试的方式和手段也根据带发布的变更不同而不同。总的来说,对于日常功能变更,只需进行新功能测试已经原有功能回归即可。对于系统架构级变更,则会通过压测等衡量系统的负载情况。

对于功能测试来说,测试是对某个特性分支进行测试的,即对每个测试提案单独测试。这样做的目的是避免待上线功能相互影响,提高测试效率。

引入测试环节的意义在于:

  1. 由测试部门提供专业、全面的测试,从而保证测试的覆盖度,以及测试的准确度

  2. 减轻开发工程师的负担(单测还是要的)

上线申请以及汇总:

上线申请值得是在代码完成了cr以及通过了测试部门的检验后,需要向上级主管以及部门上线总调度人发送上线申请通知。

上线申请内容主要包括本次上线变更内容,代码涉及的功能模块,设计方案,需要上线的服务器集群,回滚方案等。开发人员需要明确提供上述的各类信息,以帮助上级主管、总调度人知悉本次上线功能以及可能造成的影响。

Code review以及测试更多的关注在代码本身的质量上,而业务主管的关注点更多的在于上线方案、变更可能造成的服务的影响点等问题上。

上线申请以及汇总的意义在于:

(1)上线功能的内容经过更高一级的review,从而保证上线服务的方案经过更全面的review

(2)协调跨个人、跨团队的上线。如上所述,我们每次上线都不是单独某个人代码变更,可能涉及到的团队、功能是多个以上的。代码的提交顺序,模块的打包顺序,回滚方案等,都需要经过统一的评估和协调。避免出现各自为政,代码冲突无人负责等问题的出现。

(3)上线变更在部门范围内透明。如果出现某个功能问题,可以做到更大范围的支持帮助。

提交代码,打包

当上线被业务主管以及上线总调度人通过后,研发人员可以将自己的特性分支合并到主分支上。并且交由运维同学打包部署。

对于线上代码主分支的修改,我们是统一在上线调度人发出上线汇总邮件后,才进行。目的也是保证线上主干分支在任意时刻都是production级的。避免出现从主干分支获取代码后,出现不可知bug。

当研发人员将代码提交完成后,由运维同学负责打出可发布包(docker镜像)。

预览验证

运维使用没有线上真实请求流量的线上机器,部署包含新代码的发布包,完成操作后由测试同学负责在该环境上执行测试用例。

在前面的课程中也已经提到了,在线上系统中,应用服务是由web服务器集群来提供服务的,同时由nginx来提供主要的负载均衡以及健康管理。

如图2所示,线上nginx服务器会定时的向它所维护的web服务器发起健康探测请求,当实际的web服务器返回200响应时,则nginx认为该web服务器正常工作;相反,如果web服务器返回的是503响应时,则nginx认为该web服务器无法正常提供服务,nginx将会把该web服务器从服务列表中摘除,后续请求将不会打到该web服务器上。

利用这个特性,我们可以将线上服务器从线上环境中摘除,即让该服务器没有线上请求。运维同学将部署新的代码,重启tomcat服务进程。启动成功后,由测试对其进行功能测试,包括本次上线的所有的功能的测试,以及原测试用例的回归。

预览验证的意义在于:

(1)使用线上服务器环境配置进行测试,所用数据都是真实的线上数据,避免测试环境不一造成的bug遗漏

(2)合并后代码的测试,避免合并代码过程中造成的冲突修改引入不可知bug

放量

预览测试通过后,运维同学会将线上流量重新导到预览机器上。此时,预览机器部署的新代码、配置,将经受线上真实流量的检验。

线上流量是最好的检验环境。qa受限于测试用例覆盖度的影响,可能无法做到所有代码分支都检测到,所以我们在线上服务器中引入真实流量用以检验代码的功能的正确性以及对原有服务是否有明显的影响。

我们通常通过对访问日志统计、错误日志以及监控系统的观察来判断变更是否符合要求。

全量上线

完成上述的上线步骤后,运维可以通过发布系统,将待发布的代码推送到所有线上应用服务器上,完成本次系统的变更操作。

在推送的过程中,由于启动服务进程本身需要一定的时间,所以运维发布系统会按一定的步长进行操作,即,将线上机器分批重启,一批完成变更后,再进行另外一批。

得益于目前微博平台的双机房架构,我们在核心服务的上线是按机房进行上线的。即先上tc机房服务,完成整个机房的变更后,由测试在tc的线上环境中进行测试用例回归。通过后,再进行yf机房的上线。采取这样的分机房上线的意义在于,如果tc发生问题,我们可以迅速的将流量导流到yf中,避免服务完全不可用。

回滚

故障是无法被避免的,无论我们的code review做的多细致,测试覆盖的多全面,上线流程的审查多么严格,最终在变更的过程中依然可能会有因疏漏、bug导致的故障。

如果代码中有预留相应的防御性措施,如开关等,那么可以通过采取相应的措施进行规避。

对于无法通过防御性编程进行错误恢复的变更,我们则需要进行回滚。即将线上代码回滚到上一个稳定版本中,取消变更。

在大型分布系统中,回滚也不是一件简单的事情。即在很多时候,单纯的回退代码可能引入更为严重的问题:

(1)上线过程中写入的数据无法做到向前兼容,回退代码,会造成这部分数据丢失,甚至影响系统功能。

(2)代码、配置回滚顺序。系统采用了代码、配置分离的管理策略。代码的回滚可能依赖于配置的先回滚,单纯回滚代码可能会造成服务无法启动的问题。

(3)队列机、前端应用服务器回退顺序。在线上系统中,我们采用前端应用服务器加队列服务器的架构,即由队列服务负责对资源进行更新,前端服务器只对资源进行下行获取。当涉及到资源的上线回退的时候,资源的读写顺序回退的错乱可能会造成线上数据不一致。

图3 异步消息处理架构

所以我们在上线申请过程中,就需要明确回退步骤,包括:

(1)代码配置回退顺序

(2)前端机、处理机回退顺序

(3)数据尽量做到向前兼容,无法向前兼容的需要明确指出

当线上真的发生了问题时,我们将马上按照上线定好的回滚方案进行代码、配置回滚。从而保证线上服务稳定。 

总结:

本文主要结合微博平台的开发、上线流程进行说明。如文中所说的,上线是一件具有风险的操作,每次新加入的功能点、改造变更,都可能给线上系统引入不可知的问题与故障。为了尽量减少类似风险,以及在类似问题出现后,可以快速的进行恢复,我们根据自身的业务特点总结设计出了一套研发上线流程。

引入流程并不是为了限制研发人员的研发效率,而是希望借助对每个流程环节的把控,从而保证我们每次变革的质量,尽量减少bug、故障的发生。

当然,我们也不认为我们的流程能够完全将故障排除。借助流程中的上线前梳理、代码的防御性编程,回滚方案的准备等措施,以及上线后的监控、发布系统的调度等措施来做到出事可干预,有问题可以快速回滚,从而减少故障对线上服务的影响。

展望:

     目前我们也在积极的推动持续集成、微服务、docker化,希望借助于这些新的技术来提升我们开发、上线的效率,本文因篇幅所限虽未就这些方面进行展开描述,但希望各位关注@微博平台架构 后续的微博、课程分享、讨论。如果你有好的资料和建议,也可以随时反馈给我们。

------------------新兵训练营简介------------------

微博平台新兵训练营活动是微博平台内部组织的针对新入职同学的团队融入培训课程,目标是团队融入,包括人的融入,氛围融入,技术融入。当前已经进行4期活动,很多学员迅速成长为平台技术骨干。具体课程包括《环境与工具》《分布式缓存介绍》《海量数据存储基础》《平台RPC框架介绍》《平台Web框架》《编写优雅代码》《一次服务上线》《Feed架构介绍》《unread架构介绍》。微博平台是非常注重团队成员融入与成长的团队,在这里有人帮你融入,有人和你一起成长,也欢迎小伙伴们加入微博平台,欢迎私信咨询

------------------讲师简介------------------

     赖佳俊(微博昵称:@LierD ),2013年7月毕业于哈尔滨工业大学后,加入新浪微博工作至今,担任系统研发工程师。曾先后参与微博Feed流优化、后端服务稳定性建设等项目。关注jvm调优,微服务,分布式存储系统。微博平台新兵训练营二期学员.

业余爱好三国杀,dota,喜欢自己做饭下厨希望能在微博的平台上认识到更多优秀的小伙伴,一起交流,一起成长

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号