前言
随着系统业务的发展,进而导致系统的架构也在不断的发生着变化。原代码以不足以支撑现业务稳定的发展,于是需要将原.net framework程序进行重构,改为.net core微服务程序。本篇文章将讲述如何从0到1,如何将原来业务梳理,并重构出一个新的微服务,并利用之前讲的从0到1devops,将此服务发布上线。
文章参考欧创新老师,DDD实战课中的讲述思想。我也把一个产品的重构分为战略和战术两个部分。战略部分从全局的角度描述如何把控一整个产品的生命周期,一个产品是如何从零到一,并高质量按时交付的。战术部分将主要讲述,如何将.net framework应用改为.netcore微服务应用,并使用我之前写的CI/CD进行发布上线的。
自己以前一直有个误区,总觉得作为开发,一定要技术新,满嘴技术名词高并发,大数据才是一个厉害的开发。而实际上,技术是服务于产品和用户的,不落地,产品自身的质量不过关,所谓技术也不过是空谈吹牛。事实上,只谈技术不谈产品的程序员,大部分写出来的代码和实际做事的能力,也是一言难尽。所以,这篇文章更多以产品的视角,来阐述如何高效重构一个优秀的产品。
我工作经历过三次重构,第一次单体(.net framework)到单体(.net framework);第二次基于soa的单体(VB .net)到基于soa的单体(.net framework);第三次面向微服务的单体(.net framework)到微服务(.net core);第一次重构,以我现在视角来看是感觉没啥效果和意义的;第三次重构因为初创公司整个产品体系、敏捷流程还不是很完整,而且整个微服务改造比如预期拆分40个服务,目前只是上线了15个服务,所以还不算特别完整。第二次重构,10多人的产品、测试、研发团队,10来个月,成功保质保量重构出一个千万销售额的产品,我个人认为是比较成功的一次重构。所以以第二次重构的思想,杂糅第三次重构的方案来写此篇文章。
特别鸣谢:文章中内容开发流程主要为产品经理张进成输出,我只是作为复盘与思考,并写成文章。
文章中的部署架构为同事设计,我只是做了实现与落地。
战略比战术更为重要,产品本身比技术实现更为重要。
开发是解决问题的人员,而不是分析问题的人员,用户不会关心你的问题原因及实现,只会关心产品是否好用
重构流程
上图是整个产品的开发周期,产品的功能清单和产品的蓝图是整个重构的目标,需清晰明确,并有相关文档,不然重构很容易跑偏。而且要把控好重构的时间节点,重构如果遥遥无期,目标持续延后。则会影响公司营收与成本,可能实际收益反而不如不重构。
重构分为局部重构和整体重构,我们的业务场景属于整体重构,而整体重构时,很容易受到历史系统的,历史包袱的影响。不过个人认为如果整体重构,还考虑历史问题,比如从原系统抄代码、考虑兼容问题,这些都是不可取的,很容易将原来的问题延续到新系统,所以上图重构基本跟开发一个新产品没有什么区别。
整个流程以B端产品为例,并省略了部分内容,如上市评估等,但是主体流程无论B端C端,大体应该是相似的。
上图是我将100多张PPT和10多个文档进行整合而成,每个环节都有具体输入,出于保密原因无法展示给各位。橘黄色部分实际是整个敏捷开发体系,为了流程清晰也省略了敏捷的内容,所以这个图内容还是挺多的,可以仔细看看。
重构策略
部署策略
重构之前,需想清楚整体系统架构、部署架构等内容,底层设计如果出了问题将是灾难性的,因为基于底层再进行改动,一是工作量巨大,二是可能为了妥协,做出很多兼容处理导致重构失败。无法达成目标。
因为我们服务是以后端Api的形式提供访问接口,所以前端应用有很多种,比如我们的CS客户端、官网页面、内部CM网站等。图中展示了用户访问这三种应用的过程及部署情况。
- 用户访问官网
用户->发起访问官网请求->通过nginx配置https转发到官网应用->官网应用从授权Pod拉取证书->通过CA公钥证书验证用户登录->客户正常访问到官网应用->官网应用经由网关访问要访问的服务(我们实际是直接官网访问其他服务,不经过网关,个人不推荐) - 用户访问客户端应用(如电脑QQ)
用户->登录客户端->通过nginx配置https转发到授权Pod->客户端从授权Pod拉取证书->通过CA公钥证书验证用户登录->客户登录成功。
因为客户端涉及的微服务特别多,故可能存在情况微服务未完全迁移完成,新服务和老服务(接口)共存的情况。所以针对不同操作时,我们通过nginx进行转发,如果客户端相关的操作已经完成新服务的迁移就使用nginx将其转发到网关,由网关服务请求其他的一些服务,将数据返回客户端。如果客户端相关的操作还未完成迁移,则nginx将其转发到老服务,由老服务将数据返回到前端。 - 用户访问CM
用户访问CM同用户访问官网,故不再赘述
此部署策略是一个简化版本,比如未展示高可用,未实现serviceMesh,未展示后端应用依赖的服务,如kafka、mongo、es、redis等。此文是为了更容易理解,而且我们第一版服务确实也还未实现serviceMesh,其他依赖的kafka、mongo等部署策略,不同业务场景部署也不一样故省略了,只展示主体架构。
正常情况服务的路由应该是通过网关进行转发,但是我们因为一些早期设计和其他的一些原因将路由功能直接放在了nginx,网关服务Pod可以当作一个加强的BFF。(基础稍弱的同学,此处可能会有一些疑惑,可能看了战术设计,这里就明白了)
分拆策略
微服务的改造不是一蹴而就的,因为整个工程量还是比较大的,所以我们会面临几个问题。
- 如何保证线上业务正常运行,同时完成服务的改造上线?
- 服务上线后如果出现问题,如何快速回滚?
- 服务上线后如何保证不出现大规模事故?
针对这几个问题,我们想到的粗略解决方案是: - 服务改造后上线,可能存在数据迁移和应用迁移两部分内容,数据迁移的工作可以参考 MongoDb数据库迁移与方式,无论mysql、mongo还是啥,其迁移思路基本都是一样的。
- 因为同时维护老的数据库和新的数据库,老的服务和新的服务也同时存活,当出现问题时可以直接切换nginx进行快速的应用切换。待新服务稳定运行一段时间后,可以停掉老服务。如果此后新服务出现问题,可以利用k8s的滚动更新机制,切换镜像就可以很方便的回滚了。
- 为了不造成大规模事故,新服务只能分批上线,尽量控制影响范围。所以最新上线的服务不能依赖其他服务,比如先上线授权服务、验证码服务等基础功能服务。所以服务改造,需要从基础服务入手,基础服务完成后,再进行依赖于基础服务的相关为服务改造
发布策略
前文我们讲述了如何从0到1建设自己企业内部使用的devops,同样的,原来的应用经过微服务改造后,被拆分成了几十上百个微服务应用,发布时不可能通过人肉进行发布,所以我们使用devops进行自动发布。
我们首先从git仓库拉取代码,然后经过代码扫描,代码分析然后生成镜像,发布到测试环境K8s集群,经过测试人员测试通过后,我们从镜像仓库拉取镜像,发布到预发环境,预发环境再进行一轮简单测试,测试通过后。从镜像仓库拉取镜像发布到正式k8s集群。
可能针对这个发布流程,大家还是有很多的疑问。比如我某个仓库代码一天部署1000次,难道我镜像仓库要保存1000个
镜像版本?那么多镜像,我正式环境怎么知道哪些是稳定发布的镜像,哪些是测试的镜像?上百个服务,我jenkinsfile每个微服务项目都一个?那如果修改维护jenkinsfile岂不是每个项目都要改?发布回滚怎么做?蓝绿、灰度怎么弄?开发分支管理怎么处理?
镜像管理
我们将所有非正式版的镜像都标记为Tag:latest,这样我镜像仓库就只有一个用于测试的镜像,版本为latest,避免了我镜像仓库太多镜像,也避免了我k8s集群服务器,过多的镜像导致磁盘占满。当测试通过后,我们用jenkins进行发布时,勾选“tagOfficialImage”这个勾选框,这样我们就对镜像打一个tag,tag格式为“版本.日期.git提交次数”。同时git代码上,打上相同的tag。
这样我们再预发布、正式发布时,找到git项目上最新的tag,因为git、镜像tag同名,所以我们发布的也是最新的镜像。如果要回滚,同理,jenkins上选择相应的git项目的tag,根据这个tag去镜像仓库找相同名称的镜像就可以回滚到相应的版本了。
分支管理
因为git分支有很多种类型。我们是分了如下几类:迭代开发分支(12Sp2)、修复分支(hotfix)、专项分支(feature)、主分支(master)。一般,master分支作为主干分支,需要稳定安全,所以一般开发人员不允许直接推送代码到master分支。所以每个迭代开始就按迭代日期创建新的分支,比如12月第2个迭代,就增加分支12Sp2。等迭代结束时,再由SM将审核通过的12Sp2代码合并到master,这时候给客户的就是稳定的master的代码。
在测试环境用jenkins发布部署时,就用12Sp2,因为开发过程中会存在反复修改与调试的情况。如果用master分支,改一个BUG需要SM审核一次代码,合一次代码,开发测试人员还得等代码合并后,才能重新进行测试,造成很大的内部损耗。而12Sp2改一次,发布一次。真正做到持续部署,等到测试通过,就将代码合并master,并打上tag进行发布,这样只需要一次代码合并审核。
jenkinsfile管理
配置pipeline需要在项目中配置一个Jenkinsfile文件,存放pipeline脚本(不考虑job中直接写脚本的方式,维护起来太繁琐)。可能很多同学这个时候会想到shared library。但是shared library只能解决功能复用的问题,解决不了jenkinsfile复用的问题。于是我参考这个文章# Jenkins的Pipeline脚本在美团餐饮SaaS中的实践做了如下处理。
至于灰度发布,可以使用nginx,也可以直接使用k8s的ingress,本质也是nginx。蓝绿发布也只是把流量切分到不同的k8s集群。因为这块还没实践,所以先不赘述,免得误导,但是感觉这一块并不复杂,网上资料,相信聪明的你,应该足够搞定了。
总结
文章用一个比较全局的视角描述了如何进行微服务重构与发布。但是没有具体落地实现,相信你还是有很多困惑,不过没有关系,这个只需要了解是怎么回事就行,了解大概原理和方案就足够了。具体的落地和实现会在战术篇中讲述,同时会附上git代码和地址。
邀请链接,