导语
6月23日,蘑菇街技术专家蒋志强在GITC全球互联网技术大会上发表了题为《持续集成和发布在美联的实践》的演讲,介绍了蘑菇街和美丽说合并成立的美联发布系统的演进和特色。
演讲嘉宾介绍
蘑菇街
发布作为应用上线前的最后一个步骤,一直以来都是运维做的比较频繁也是风险比较高的操作,发布系统不仅要做到提升发布效率,更重要的是保障发布过程中系统的稳定,减少因发布导致的故障。本次演讲主要包括三个部分。一是发布系统的演进,二是美联发布系统的实践之路,三是发布系统的特色。
智能运维 系统做决策
发布系统主要可以分成三个阶段,首先就是人肉运维,什么都要靠自己。然后进化到自动化运维。自动化运维分两个阶段,首先是使用一些开源的工具,搭建一套运维体系、运维工具。但是随着公司业务的继续发展,慢慢的会发现,虽然开源软件功能非常强大,但这些用开源软件搭建的运维工具不一定能适应公司自己的需求。但是如果要进行二次开发或者功能的增加,成本也很大,因为不了解它具体的实现逻辑。慢慢的就会进入到一个自研运维系统的过程。
最后,智能运维,我个人理解,智能运维跟自动化运维最大的区别就是,智能运维可以自动做一些决策。自动化运维,所有的操作还是要手动触发,但是智能运维,系统会自动对各种数据进行分析,做出合适的决策。
举个最简单的例子,应用扩容。虽然现在有一套比较完善的扩容系统,但是还是需要由PE自己去决定扩容的时间和台数。但如果是智能运维,把智能的扩容系统做好以后,系统就可以自己根据监控的数据或者其他系统的数据,自动的决策是否需要进行扩容。当然,这是我们的目标,我们正在朝这个目标努力。
蘑菇街技术架构演进
蘑菇街创业初期的技术架构,开发语言用了PHP,运行环境用了非常流行的四件套:Liunx、PHP、MySQL、Nginx。技术架构很简单的分成三层,用的基本上全是开源。只支持PHP的发布。PHP发布,整个逻辑比较简单,只需要把代码打包放到服务器上解压。有了这个发布系统,至少可以做到可以选择需要发布的文件,并且知道需要发布的这个文件的版本号是多少。把运维从发布上解放了出来。这个过程中参与发布的主要是开发而不是运维。
中期,蘑菇街从一个单纯的导购网站变成了一个电商的平台。作为一个电商平台,就必须要有的详情页、购物车、下单、支付这些子系统,包括一系列用来支撑的后台的运营系统和基础服务。臃肿的PHP工程在一个代码仓库里面,如果把这么多电商业务都放在PHP这一个仓库里面的话,上百个开发对于同一个仓库进行开发,不可想象。所以要把业务进行拆分,JAVA服务化。
我们把应用逐渐拆成JAVA一个一个的服务化。为了适应这个需求,就有了第一版的JAVA的发布系统。JAVA的发布系统和PHP有很大的不同,PHP只要解压,而JAVA还要重启容器,这就涉及到在重启之前要关闭报警、切走流量,否则应用一重启一发布,就会收到一大堆的报警短信,影响用户的访问,这些都是不可接受的。发布系统的发布逻辑就会有很多的步骤。
但是随着时间的进步,公司业务持续的发展,蘑菇街和美丽说合并,虽然两家都是电商公司,但是在底层的运维基础上,还是有比较大的不同。多了很多不同类型的应用,这些应用都要纳入到发布的管理里面来。
发布系统的构建之路
首先要做运维工具,最重要的一点就是要把标准化做好。标准化跟发布相关的,主要有两点,一个是基础环境,和基础环境的配置需要标准,OS、JDK、TOMCAT等等。另外,应用要支持标准。为了支持这些标准化的落地,有一个应用的配置管理中心会管理应用最基础的信息,人员角色、应用类型、启停的命令、软件包的信息。虽然在应用规范里面定义了这个应用的一些启停的命令是有标准和默认的模板的,但是为了兼容性更好,功能更强大,我们开放了自定义的功能,它是可以修改默认的。如果是完全标准的应用,可以不做任何的修改可以用。
发布系统的依赖是什么?如下图所示。
架构主要是两块,一块是前端的,发布流程的控制、用户的界面等,下面是Python写的Worker,用于构建、部署,中间通过MQ进行解耦的操作和任务队列,中间通过DB进行数据的交换。
研发流程,首先是发布的流程,先线下进行发布,然后预发进行发布,最后到线上进行发布的时候,之前还有Beta发布,确认没问题了才最终进入线上的发布。线上的发布和预发的逻辑是一致的,但是从预发到线上就比较不一样了,会有一个Check List检查,只有通过了才允许进入线上发布。提交完以后会有一个审批流程,审批流程会根据时间的不同、硬件的不同,发布流程不一样。
默认主干都是Master。假如有一个需求需要进行开发,就会从Master拉出一个开发分支,进行需求的开发。开发完成后上线,开发系统就会从Master上拉出一个release分支进行发布,最后合并到Master,完成代码合并操作。
新建变更操作也是在发布系统中完成的,我们希望开发可以不直接操作gitlab,因为操作gitlab风险比较大。另外,我们提供了两种变更创建的方法,一种是新建分支,还有一种是导入分支,从现有的开发分支中再创建一个分支出来,这时候可以用导入变更的功能。
而集成,部署环境有三个,线下、预发、线上。中间发布的基础的信息,下面是发布的过程,大致可以总结成三个,一个是代码合并,一个是编译构建,第三是部署。对于特殊的应用类型,可能中间还会多几个其他的操作,比如Docker的发布,那么就在编译构建完成以后,多加入一个Docker镜像的构建完成。如果带有前端的文件,在代码合并之后会有合并前端的操作。
最下面一个是集成区,一个是待集成区。集成区就是当前发布,待集成区就是变更开发完成以后,提交发布就在待集成区,可以随时加入集成区也可以随时退出来。
不允许把变更拖入线上环境中,所有的线上发布都是预发过去,而且线上的release分支都是预发的release分支。首先保证预发都是成功的,然后预发右边集成区的Check List全部通过,这也是为了保证业务的稳定性,最后才能拖到线上。
这时候我们遇到两个问题,第一个,你往上合的时候难免会遇到一些冲突,有可能是两个Feature分支之间发生了冲突,也可能是Feature分支和Master之间发生了冲突。怎么解决这个冲突?冲突依靠自动化系统来解决,现在看来还是不太可能。我们提供了一个思路,就是如果发生冲突,还是强制合,但是会告诉这个分支冲突了,手动把这个release分支check out出来,本地解决了再上去。但是,这样解决一次冲突还可以,下次再冲突了怎么办?这涉及到release分支的更换策略,解决过的冲突不会再造成冲突。只有撤下一个release分支的时候才会更换这个release分支。这个办法也能很好的解决经常发生冲突的问题。
我们希望每一次的构建环境都是干净的,都是统一的。可能在一台构建的机器上,同时会构建十几个甚至是几十个任务,我希望它们之间是互相独立的,互不影响的。所以我们用Docker来进行构建,每次构建都会起一个Docker镜像,把代码挂在这个镜像里面,在镜像里面执行构建的命令。最后把产出拿出来。
部署的流程里面有很多的步骤会依赖到OpsAgent,要在目标服务器上进行操作。机房那么多,不可能把所有机器都跟这台发布机器进行通道打通。首先通过Agent检查目标机器的环境,应用机器在SA交付以后,是不是安装上了应用需要的东西,应用的初始化环境,用户有没有创建,部署的目录有没有创建,基础的环境要进行检查。然后下载更新报,关闭监控,Tesla下线、Web下线,然后停止应用,更新应用,再启动。这时候有一个健康检查的逻辑,最后就是两个上线,把流量引进来,把监控打开。
如果发布的发布策略是分批发布,这一个应用有好多分组,在分批策略的时候不会把所有分组的所有机器放在同一批。后面的不自动开始,就是构建完成之后,到了发布界面就停住了,需要手动的点发布按钮进行这一批次的发布。正常的JAVA的应用,一台机器发布在一分半到两分钟左右。分十批发的话,这么一次也只需要十五到二十分钟。
每个应用在规范里面都会强制要求有检查健康。这个机器需要做一些操作,通过返回值是否Success,来判断是否成功。首先检查核心依赖是不是能够连上DB或者Cache,依赖的其他应用是不是能够正常的调用到。还有一些特殊的应用需要预热数据,不管是缓存中的数据,或者是编译器的预热。有了健康检查以后,基本上可以保证应用不会有太大的问题。
我们在不同的时间审批链是不一样的,最普通的日常发布,周一到周四。如果是非工作时间,下班以后或者是节假日,就需要紧急发布,这时候需要研发D进行审批。最特殊的,例如6·16大促,为了保证稳定,原则上除非是紧急bug,否则不允许做发布的,这时候要做发布需要CTO进行审批。这些时间都是在系统中可以调整的。回滚主要分成两种,一种是基于基线的回滚。发布完成之后,会向基线插入一条数据,里面记录了本次发布的master分支的版本。想回滚到哪一次,就按后面的按钮帮你回滚到这个版本。
还有一个回滚的功能就是紧急回滚,在发布过程中发现应用有问题,需要进行紧急的回滚。每次都会把上一次的war包保存一份,直接把上次的替换过来重启应用,这就是紧急的回滚。
蘑菇街发布系统的特色
首先,实现研发流程的闭环,研发流程从需求到发布,只有在变更创建开始到发布那一段是在发布系统里面的。之前的需求、项目管理等等,都是在另外一个项目管理的PMO系统中实现。这两个是独立的系统,开发的时候要在这两个系统中来回切换,非常不方便。我们把它和项目管理系统进行了整合。项目管理系统中可以创建关联变更,在变更发布后更新需求、项目状态,这样就完成了项目管理的闭环。这样的好处就是,在需求管理系统中可以很明确的看到,某一个需求是哪一天什么时候发布上线的,在发布系统中又能很清楚的看到,某一次的发布是发布了哪些需求。
第二,多机房多分组的构建。因为我们有一个需求,同一个应用部署在不同的机房,可以按照不同的机房不同的分组创建不同的参数,部署的时候会根据应用机器所在的机房或者分组使用对应的包。
第三,多套环境。我们可以根据分组只发布到某一个分组,就相当于变相提供了多套环境的功能。
第四,Jar包检测和Diff。Jar包冲突会有很多的问题,明明写的时候是好的,部署上去就是不正常。原则上我们不允许使用Snapshot的包,因为它不可控。Jar包的Diff,会告诉他哪些Jar包有冲突,哪些版本不能再使用。基线和哪些Jar包的版本不一致。
第五,二方库的发布。有两种,一种是某个应用的子模块,还有一种是像中间件的产品。发布系统能为线上的稳定性做哪些保障?目前四个,前端扫描,前端应用,JS的文件的扫描,对接了前端团队的系统,对这些前端文件进行扫描。安全扫描,我们对接了一个安全的系统,对JAVA应用进行静态的代码扫描。集成测试,我们有一个测试系统,在发布的时候同时对发布代码进行单元测试、接口测试。性能监控和压测,这些系统不是发布系统本身提供的,都是对接了我们内部团队的系统,把它集成到系统中。我们的Cobra系统会对代码进行扫描,如果扫描不通过也是不能发布的。