你们好,今天我将分享通联数据使用Docker和Rancher构建自动化发布管道的经验,我会介绍通联数据自动化发布的流程及方案设计,咱们从踩过的一些坑中总结出来的经验,以及咱们的自动化发布管道的系统运行现状。前端
通联数据是一家这几年新崛起的金融科技公司,主要目标是致力于将大数据、云计算、人工智能等技术和专业投资理念相结合,打造国际一流的、具备革命性意义的金融服务平台。这里面涉及到几个关键词:大数据、云计算、人工智能,这些热词已经有不少企业说起过,所以我也不赘述了。nginx
通联数据做为一家创业公司,也会遇到不少创业公司都会遇到的问题,好比产品太多,并且每一年不少产品都会推翻重来,就会遇到各类各样的问题。好比,应用在开发时,涉及到须要多少CPU、须要用什么语言去编程等问题,开发人员不会与后台人员事先沟通,出来一个产品,直接让运维人员上线。web
基于这样一个背景,咱们迫切须要打通上线这条管道,由于在可预见的未来,通联数据每一年将会有大量的新应用出现,如何打通上线这条管道亟待解决。编程
咱们作的第一件事,就是作持续集成。因为咱们公司每一年会有上百个新的项目产生,每一个项目的语言、框架、部署方法都不尽相同,发布流程也是比较冗长低效的,部署应用占用了运维人员大量时间。后端
咱们决定用容器解决这个问题,评估了多家供应商后选择了Rancher,主要缘由在于:安全
首先,Rancher的操做界面很是简单,相信用过Rancher的人都会有这种感觉,它不须要太多专业的知识,很容易上手;其次,Rancher在部署时也很是简单,能够一键式部署,Rancher还提供了良好的API支持,方便集成。框架
随后,咱们开始搭建本身的CI/CD。当时咱们在流程方面遇到了不少困境,下图已是简化制做后的了,实际上还有更多各类各样的分杈和分枝:运维
就咱们原来的流程来讲,流程最开始的部分是研发;随后进入QA环境部署,这时候就须要人去部署,一般是运维人员,可是运维通常不肯意作这个事情;部署完成后,进入QA环境测试阶段,通知开发和测试人员进行测试,过程当中可能会出现延迟,由于测试人员可能正忙于其它的事情,不能立刻进行测试;QA环境测试经过后,进入STG环境部署;随后再进行STG环境测试,这几个过程可能会循环不少次。跟着进入安全测试阶段;测试经过后,还有一个正式包的一个准备过程,最后才能上生产部署。即便是这个简化后的流程也是很是复杂的,并且其中涉及到不少线下的沟通,效率不可能高得起来。微服务
在使用Rancher以后,本来的流程大大简化了,改进后的简化流程以下图:测试
流程的第一步即持续集成,意味着开发人员写好代码后能够直接经过CI,CI触发自动编译,随后自动部署脚本,测试环境已经就绪。简单来讲,每次开发人员提交代码以后,测试环境就始终处一个就绪的状态,这时候就能够直接进入测试阶段,整个过程都处于线下,不须要走任何流程,所有实现自动化了。
测试人员完成测试后,再进行STG环境测试,由于后台已经跟Rancher完成对接并实现自动化,这赋予了QA环境测试从未有过的强大的自动化能力,意味着QA环境测试能够自动对接到STG环境测试。测试经过后,进入安全测试阶段,这个阶段是公司要求的,没法避免,安全测试经过后就进入到生产部署。之前绕不开的线下沟通那些步骤和一些部署就能够省去了,整个流程优化而且简洁,效率也提高了。
CI/CD提及来可能很简单,好比PUSH代码、QA环境自动就绪。可是实际操做起来并不是如此,仍然存在不少须要解决的问题。好比开发的分支模型就涉及到在开发代码的时,要PUSH什么分支才能部署,仍是PUSH全部的分支都能部署?
当时咱们想到最好就是PUSH任意分支都能部署,这样就很是方便。但随后就发现这方法行不通,会形成混乱,并且难以管理。此前咱们Git的一个The Successful Branch Modeling分支模型就相似于此,此模型规定了一个develop分支、一个feature类型的分支、一个release类型的分支、一个hotfixes类型的分支和一个master分支。
在平时开发时,开发人员经常会在develop分支上切出一个feature的分支,好比,开发一个包含不少功能的story,那么全部人切一个story,这边story有本身的ID,而后再去开发,把story开发完以后,再把它merge过来。最终,咱们只选择了一条线作CI,当代码PUSH或者合并到develop分支时,咱们帮你去作这个,而在这个feature分支的时候,咱们另做处理。
feature分支意味着当用户提交一个feature分支以后,咱们会另外帮你部署一套,每个feature分支部署一套,至关于每个story均可以分开独立去测试,最后把它合并到develop分支。那么测试的时候,测试能够根据本身的需求来决定它到底在哪一条分支线上去测试。
好比,A测试中若是用户只关心story A,那就在story A这个分支测试环境去测,这些story所有合并进来以后,再进行一次集中测试,测试经过后,在发布时把这个分支切到release 的一个分支上来,而后,在release上发布正式包,让QA在STG环境继续进行测试,就如前面看的那个流程图。分支模型很是混乱,为了作CI,咱们会跟开发人员定义好每一条分支,每一个分支对应的每一个不一样行为也定义好,这在混乱的分支模型下很是有用。
为了作CI,版本号的规则必须一致,若是每一个团队版本号命名不同的话,匹配规则就会很是麻烦和混乱。后来,咱们选择了Semantic Versioning的一个版本号的规则,就是几点几点X,这是一种常见的版本号命名方法,此版本号包含标准的一个文档,文档描述了此版本号的具体定义,第一个叫MAJOR,第二个叫MINOR,第三个叫PATCH,后面还能够加各类本身的版本号。
下面我将介绍咱们的CI的触发路径——Git push,push到develop分支,Git push就会push到Gitlab的server上,随后经过webhook去调用Jenkins,Jenkins会把这个包bulid出来。本来咱们是想经过Jenkins调用Rancher的API,后来发现直接调用Rancher有差距,过程进行的也不那么顺利,为了解决Jenkins和Rancher之间的gap这一问题,咱们在它们之间装了一个Ponyes的软件。
为何须要Ponyes这一中间层?它到底提供了哪些价值?
(1)动态修改版本号
首先,它能够解决动态修改版本号的问题,你们用Rancher的时候,发现Rancher的商店很是好用,咱们能够在商店里把一些东西定义好,接着QA只要填几个参数,就能够把一个应用部署起来了,没有Ponyes这一中间层以前,必须找运维人员去作,过程也比较复杂。
为了用起来Rancher应用商店,咱们还定义了一个应用商店的模板,这样咱们就能够在每次PUSH代码的时候,把这个模板生成一个真正能够部署的应用。可是,版本号仍是存在一些问题,咱们每次PUSH代码的时候,到Jenkins,咱们会根据阅览数给Jenkins升级一次版本号,好比说1.0.1.0-1,第一次阅览是-1,第二次阅览即-2,Jenkins的版本号根据阅览次数相应变化。这时候应用商店也要随之而变。
所以,咱们作了这样一个模板,经过这样一种方式,在Ponyes中,每一个Jenkins能够得到对应的版本号,而后把这个版本号经过变量把它注入进去:
sample: image: {{ REGISTRY }}/automation/auto-sample:{{ m['auto-sample']}}
这一过程还涉及到registry,由于QA环境和STG环境是彻底分开的,在进行模板渲染时,咱们须要知道究竟是发到QA环境仍是STG环境,从而对registry的地址作出相应改变,这样的话,上面说的修改版本号的问题就解决了。
(2)多service管理
还有一个比较棘手的问题,即一个stack中有多个服务怎么办?好比,一个比较小的团队可能总共就几我的,每一个人负责好几个项目,与微服务的关系有些类似,那么一个stack可能有好几个服务,最典型的就是前端、后端,或者是其余的一些中间件,每一项是一个服务。
开始部署时,Rancher里面的这些服务也要用一个stack来管理,有多个服务就会面临怎样管理的问题。好比,一个stack有ABC三个服务,服务C更新时,整个stack也应该更新版本号,由于在Rancher里面,stack被部署出来以后,有个update available的黄色按钮,若是有新版本,点这个按钮,就能够升级这个stack,并且必须升级整个stack,而不是只升级某个服务,这时候就须要管理多个服务。Ponyes记录了服务和stack之间的关系,任意一个服务更新,也会触发stack的更新,更新的方法就是每一个服务每次更新,stack的版本号+1。
(3)多版本并行发布
接下来还有一个更严重的问题,若是多版本并行发布,咱们应该怎么去处理?好比说咱们ABC三个模块,A发布过1.0和2.0,B发布过3.0、4.0,C发布过5.0和6.0。若是发布一个C应用的5.0.2,那么对应的AB模块号应该选什么,这个问题困扰了咱们好久。
有好几种方法能够解决,好比用某个东西记录C5.0和AB的一个版本号之间的关系,这表示用户能够自定义。但也存在一个问题,用户须要本身去管理它们之间的版本号关系,时间长了,可能会弄乱或弄错版本号,或者弄错版本号之间的关系,随后上线到生产环境,后果更严重。
还有一种方法,就是将用户的C版本号最后一次发布到5.0时AB是什么版本静态地记录下来。但这样的话系统会变得至关复杂,很容易出错。
最后,咱们选择去除多版本并行发布的能力,只支持单个版本的发布,意味着若是要发布,必须是最新版本,历史版本能够用另外的方法进行人工处理,这样的话,系统会更简单,并且不容易出错,也不须要用户去维护一个版本号之间的关系。以上这些功能都是借助咱们本身写的Ponyes解决的。
Rancher 2.0也提到了CI/CD这样一个功能,在实际过程当中,会遇到一个很是现实的问题,从代码研发到整个生产环境部署有许多环节,具体状况也很是复杂,而CI的做用可能仅止于QA,后面的环节还会有新的难题,这时就须要一个体系把整个生命周期贯穿起来管理,Rancher承担的做用就在于此。
(1)部署几套Rancher环境?
这问题看似很小,最初也在咱们内部也引起过很多讨论。最初咱们只部署了一套Rancher,在Rancher里面用环境去区分QA或production。部署一套Rancher的方式最简单,可是有一个严重的问题:Rancher升级了怎么办?这套Rancher既管了QA,又管了production,升级的时候须要把生产环境也升级,此刻若是出现bug,问题将很是严重。
后来咱们把它拆成四套,Rancher平台自己也得有一个升级的环境。建议你们在前期部署Rancher的时候要部署多套环境(至少两套),咱们其实是dev、QA、staging、production每一个环境都有一套,总共四套。
(2)配置项爆炸
第二个问题是关于配置项。咱们最开始是使用Rancher compose的,它很是简单强大,咱们在Rancher Catalog里点一下“部署”,配置选项全弹出来,咱们只须要点选一些东西,就能够部署一个很复杂的应用。后来咱们却发现,应用的配置项愈来愈多(甚至多达上百个)。这致使它们难以在一个页面里展示,同时上百项的配置让咱们无从填写,运维也没法成功部署,咱们从而面临了配置项爆炸的问题。
咱们的解决经验是,在容器平台里面,配置项必定要集中管理。咱们把配置项所有用consul管理,每次容器启动时到consul里把配置拉到对应的容器里来。如此一来,容器能够在任意平台漂移。另外配置项自己在原server存有副本,咱们copy原配置项加以修改,就能够部署另外一个实例了。因此说,配置项必定要所有集中管理起来。
(3)泛域名+Rancher LB
咱们每个业务都有一个web服务,要申请本身的域名。每一年咱们有上百个项目上线,这意味着有上百个域名要申请。加上这些域名都分别须要在开发、测试、生产环境中使用,因此咱们每一年要申请将近五百个域名。这是一件恐怖的事情。
后来咱们就用了泛域名的方法。好比用*.sub.example.com的域名,直接CNM到Rancher环境中的其中一台主机。而后在Rancher上设置它的LB,LB就能够分布在全部的主机上,每一个主机上都会有一样的LB。这个域名任意指向一台主机,它均可以工做。
好比说,在QA环境,这个LB上serve了以下这些域名,若主机坏掉了,容器自己会启动,入口问题则能够很简单地经过修改*量来搞定。若是是生产环境则能够在上面再加层nginx,配三个upstream,死掉任意一台,还能够经过另外两个入口进来。
使用泛域名的方法,在配置好泛域名以后,在Rancher LB上再加任意域名都不用再去申请新的域名,而是能够直接写123.sub.example.com,而后直接在LB上配置,配完以后域名便可用,无须再走申请的流程了。