4 发布服务
小公司或者某些特殊业务,可以由开发人员自己部署服务。不过,原则上开发人员要和生产环境分离,降低风险。本文主要介绍由专业的运维人员进行操作的发布流程。
服务首次发布时,由于没有线上用户,也不和其他服务交互,发布过程比较简单,服务部署好之后,对外开发业务功能即可。
本文重点介绍,线上正在运行的服务,在迭代更新时的发布流程。
1、服务交付
服务程序经过迭代开发、自测、压测以及QA测试,Bug基本修复完成,服务在测试环境中比较稳定,马上进入验收交付环节。
此时,需要在编译机上编译最终验收版本。
服务编译至少要有脚本编译,机制成熟的可以在MIS(Management Information System 管理信息系统)上,web页面操作进行编译。
专门的编译机编译,可以减少开发人员漏提交代码或配置,导致的编译风险。而且,编译机上产生的版本,必然是发布(Release)版本。
编译完成后,从编译机上取验收版本服务,部署到测试环境,由QA同学进行验收测试。
测试完成后,QA会提交验收报告。验收版本即可作为发布版本。
服务交付的同时,需要提交本次迭代新增配置项的配置说明,说明中包含配置字段名称、含义,配置方式,配置取值范围,以及配置注意事项等。
最后,需要提交一份发布CheckList(检查列表),包括发布服务名称及版本号,本次迭代修改内容,是否有新增配置项,服务发布顺序,发布过程中可能出现哪些问题,以及出现这些问题后如何应对。
服务文件,新增配置项配置说明,发布CheckList就是本次迭代发布前需要交付的发布内容。整理好后,邮件发送运维人员即可。
2、发布顺序
项目迭代之初,需求讨论之时,需要提前考虑服务发布时的发布顺序。
服务迭代,一般会伴随着前端客户端的迭代,首先要考虑前后端的依赖关系。依赖关系分为以下几类。
2.1 无依赖
对于无依赖的情况,发布顺序最为简单,客户端和服务端,按照自己的节奏发布即可,不需考虑对方的发布节奏。
这种类型的项目,拆分成服务端项目和客户端项目,单独立项都没问题,一般是因为产品需求的原因,一起立项研发。
2.2 弱依赖
弱依赖的情况,客户端兼容新老服务,可以先发布客户端。
客户端发布后,连接到老服务,表现和原来一致,或有部分前端展示改进;连接到新服务,通过读取配置或响应新服务的新消息,表现出新的功能。
由于客户端对服务版本无强制需求,因此服务发布可以比较从容的进行。
2.3 强依赖
如果出现了强依赖,客户端更新后,在老服务上无法运行,必须在新服务上才可以正常工作,就导致了必须客户端和服务端一起更新。
这是最不希望看到的情况。
一旦出现这种情况,需要全网停服,期间客户端不能连接服务。服务全部更新到新版本,客户端推送更新,也全部更新到新版本,然后放开服务,客户端连入。
这种方式有较高的发布风险,一旦客户端或服务端,发布后发现严重bug,需要再次进行全网操作回退到上一版本的服务和客户端。
因此,强依赖的发布关系,是在立项之初、设计之时尽量避免的情况。
3、灰度发布
在 第五章测试 一节中,我们提到过灰度测试发布,这里我们详细说明。
灰度发布时,服务会面向部分用户开放,其本质是使用小规模的真实用户,进行线上运行测试,以此验证服务的稳定性。
灰度发布时,可以按照需要,限制部分用户可以进入。比如进行渠道限制,单个渠道的用户可以看到功能入口;或者服务开放单个挂牌产品,因为用户可选场次很多,单个场次自然只进入部分用户。
灰度发布的服务环境,和正式发布的服务环境是隔离的,灰度服务产生问题,不会影响到其他正式服务。且灰度环境下的配置,可以和正式环境中配置进行差异化管理。
单次灰度发布最多持续两周时间。
灰度发布完成后,开发人员和QA都需要连入新服务,查看运行是否正常,符合预期。
如果灰度发布过程中发现问题,因为放开人数有限,影响范围也比较小,可以回退正式版本,修复后再次灰度发布。此时两周的灰度周期可以重新计算。
如果灰度发布过程中没有出现异常,则进入全网发布流程。
4、全网发布
服务的功能及稳定性,在灰度发布流程中得到了验证,可以将灰度的服务移到正式环境,并进行全网发布了。
全网发布是指,将所有的迭代变更的服务,更新到最新版本。
根据服务部署的情况不同,全网发布一般分为停服更新和热更新。
4.1、停服更新
停服更新,一般是选择在线人数较少的时候(比如凌晨2点到4点),提前发布更新维护公告,停止对外服务。
更新时,先暂停新用户进入,等已进入的用户都退出后,停止服务,替换新服务,再启动新服务。
服务启动后,运维人员进行简单测试,观察服务状态正常,即可对外开放。
停服更新是比较常见的服务更新方式,虽然选择了在线人数少的时候操作,但仍会影响部分用户的操作体验。更优的更新方式是热更新。
4.2、热更新
热更新是指,在不停止对外服务的情况下,更新服务功能。
热更新一般在游戏前端应用较多,通过更新前端的脚本程序,游戏玩家不需要重新下载安装app,就可以体验到新的游戏功能。
服务端通过优化架构设计,也可以达到类似的效果。
服务端实现热更新的方式,可以有插件更新,AB服务更新和动态更新。
插件更新
将一个服务拆分为不常变的宿主壳服务和经常变动得业务插件服务。
壳服务主要负责网络操作和插件管理,这些功能很长周期内都不会发生变化,因此不需要经常修改。
插件服务负责具体的业务内容,因为业务变动频繁,迭代时只需要修改插件服务即可。
服务由一个ChatService.exe程序文件,改为ChatSevice.exe和ChatSvr.dll组成,新的ChatService.exe就是壳服务,只负责基础功能,业务功能由ChatSvr.dll负责,迭代更新时,只需修改ChatSvr.dll即可。
更新服务时,将ChatSvr.dll V1.1部署到服务上,客服务通过文件变动的时间戳检测到服务版本变化,会将新版本的dll加载到内存。此时,现存的用户都在V1.0的dll上,但后续新进入的用户会分配到V1.1的dll上。
V1.0的dll上不再进入新用户,而老用户随着时间的流逝不断退出,最终,所有建立在V1.0上的聊天室全部销毁,用户都转移到V1.1上。壳服务检测到V1.0版本不再被使用,即将其释放,此时只剩下V1.1版本的服务,服务更新完成。
AB服务更新
AB服务更新是指将同一服务,通过配置编译为监听不同端口的两个服务,如下图所示。
两个服务除监听端口和名字不同,实现功能都一样。
假设目前运行的是ChatServiceA服务,所有用户都在A服务上。本次迭代编译完成后,先替换ChatServiceB服务。B服务上没有用户,因此更新时不需要关注用户影响。
B服务更新后,更新代理配置,后续新的用户和聊天室创建,被B服务响应,A服务不再接收新建聊天室的请求。
当A服务上的聊天室全部销毁后,由于不再接收新建请求,A服务可以停止服务。此时再将A服务更新到最新版本,和B服务保持同步即可。
当更新完成后,B服务保持运行,A服务不对外提供服务。等到下次迭代更新,再替换A服务。
如此轮流进行,滚动更新,就可以保证在更新过程中,不对外间断服务提供,客户端无感知。
动态更新
动态更新主要用于一组无差别分布式服务的更新上。
加入原来有4台服务提供服务,所有用户均匀分布在几台服务上。
当服务需要更新时,先调整配置,将用户逐步导向前一半的服务,后一半的服务不再接受新的用户进入,等待已进入的用户自然流逝。当所有用户离开后,更新后一半服务。
后一半服务更新完成后,采用同样的手段,将用户导入到更新后的服务。当用户都进入后一半服务,更新前一半的服务。
前一半服务更新完成后,再修改配置,将用户均匀分配到各个服务,一次更新的运维操作就完成了。
5、发布后追踪
从灰度发布开始,新版本的服务就已经面向了线上正式用户。
无论是服务版本有问题,存在未发现的bug,或者更新流程出现问题,配置出错,或者发布环境出现问题,与其他服务的版本不匹配等,都可能造成严重的线上事故。
即使发现问题,如果能快速处理,可以将影响降低到最小。因此,发布后需要密切追踪服务的运行状态,通过服务日志,监控报警,进入产品体验,以及客服反馈等,关注服务是否运行正常。
关于发布后,或者线上运行的服务,发生异常后如何处理,我们在下一章“线上问题处理”中,具体分析。
Last updated