返回 登录
0

超长干货:通联数据是如何使用Docker+Rancher构建自动发布管道的?

本文由2月8日晚通联数据容器平台负责人蔡云飞在Rancher官方技术群内所做分享的内容整理而成,分享了通联数据使用Rancher构建自动化管道发布的实战经验。文末有下载福利,添加Rancher小助手为好友可加入技术交流群,还可实时参加下一次技术分享~


大家好,今天我将分享通联数据使用Docker和Rancher构建自动化发布管道的经验,我会介绍通联数据自动化发布的流程及方案设计,我们从踩过的一些坑中总结出来的经验,以及我们的自动化发布管道的系统运行现状。

通联数据的需求及选择Rancher的原因

通联数据是一家这几年新崛起的金融科技公司,主要目标是致力于将大数据、云计算、人工智能等技术和专业投资理念相结合,打造国际一流的、具有革命性意义的金融服务平台。这里面涉及到几个关键词:大数据、云计算、人工智能,这些热词已经有很多企业提及过,因此我也不赘述了。

通联数据作为一家创业公司,也会遇到很多创业公司都会遇到的问题,比如产品太多,而且每年很多产品都会推翻重来,就会遇到各种各样的问题。比如,应用在开发时,涉及到需要多少CPU、需要用什么语言去编程等问题,开发人员不会与后台人员事先沟通,出来一个产品,直接让运维人员上线。

基于这样一个背景,我们迫切需要打通上线这条管道,因为在可预见的将来,通联数据每年将会有大量的新应用出现,如何打通上线这条管道亟待解决。

我们做的第一件事,就是做持续集成。由于我们公司每年会有上百个新的项目产生,每个项目的语言、框架、部署方法都不尽相同,发布流程也是比较冗长低效的,部署应用占用了运维人员大量时间。

我们决定用容器解决这个问题,评估了多家供应商后选择了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触发路径

下面我将介绍我们的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上配置,配完之后域名即可用,无须再走申请的流程了。

Q & A

Q:rancher在安装k8s时遇见这个问题:server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none 不是时间不同步问题,求解
A:Rancher 安装k8s,我现在还没有去做。以后有时间会去尝试,暂时缺乏相关经验奥~

Q:consul中都存哪些配置呢? 怎么拉到容器里?
A: consul中存放应用程序的所有配置项,通过自己编写的脚本,用http的方式拉取到容器中。

Q:一个应用如果需要多版本同时运行提供服务,怎么做,配置上是否也要区分?
A :同时运行,可以部署多个Rancher stack。这就是Rancher商店比较有用的地方了。你可以部署任意多个stack,这些stack版本可以各不相同。通常配置是要区分的,但是很多配置其实是一样的,因此我们用了consul的继承功能,有一个存放所有配置的路径,然后每个stack只存放不同的配置项。

Q:你们的CI就是只走一个分支一套环境吗,比如develop分支 QA环境,其他环境分支都不走webhook吗?
A:我们的CI确定了唯一的分支名称”develop,这样可以做到所有项目一样,其他分支我们也有做,比如以 feature-开头的分支,我们会每一个不同的分支部署一个stack。

Q:是不是有个配置网关,分布式多台机器统一都配置文件?
A:对,我们称之为配置中心,用的是consul。整个集群所有的服务都会从这里获取配置。

Q:jenkins与gitlab联动的时候是如何做到 指定分支构建的,而且是要自动化的?比如我改动了gitlab的test分支,然后jenkins只构建test分支,jenkins发送邮件的时候只发送有关构建test分支的内容。
A:jenkins 可以对分支名进行正则表达式匹配,比如,匹配 feature-* 的分支,我们会为每一种case 建立一个jenkisn job。实际上我们只有三种job,develop , feature-* , release-* ,分别对应CI 分支、 story分支和发布分支。

Q:LB和Gateway分别用的什么?如何做的架构?
A:全局软件LB 用的是nginx,gateway是个大工程,我们公司专门有一个团队在写。整体架构会比较复杂,难于用文字描述。大致的流量走向是:公网 -> nginx -> gateway -> rancher lb -> 容器。

Q:镜像仓库怎么解决?灰度发布怎么解决?
A:镜像仓库使用的是harbor,harbor非常棒,推荐大家使用。灰度发布我们是使用Rancher多个stack来暂时结局的。刚刚提到我们公司有一个全局gateway,由这个gateway操作Rancher的lb,将流量在两个stack之间切换。当然如果你没有gateway,自己手动切换也可以 。灰度功能k8s实现的更好。

Q:gitlab上一个项目有俩个分支一个master一个test,请问你们的jenkins 那边对应了几个项目? 是每个分支对应一个jenkins项目吗?
A:一个git库,我们一般有3个jenkins job, 通过正则表达式匹配分支名的方式,来区分作用,分别为 develop, feature-* , release-*

Q:生产环境只有一套Rancher能应付这么多业务么?难道不应该两套Rancher用nginx切换么?
A:一套Rancher Controllor 足够。使用Rancher 的环境来做业务间的区分和隔离。可以在Rancher中部署多个stack,在Rancher中操作lb来切换。这样更容易操作,不需要操作nginx 来切换。

Q:可以理解为一种并行迭代,还是需要多套stack呢?
A:可以理解为同时部署多个版本,然后由rancher lb切换来使用哪个版本。

Q:创建stack时需要外部数据怎么解决呢?
A:外部数据库直接使用「域名+端口」的方式写到配置文件中即可,配置放在consul中就可以了,和虚拟机访问外部数据库没有区别。我们的数据库本身不放在Rancher中,是独立的。

Q:jenkins用了什么插件?为什么Rancher不用k8s编排呢?cattle有什么优势?
A:jenkins用的插件较多,现在因为无法一一列举。 Rancher的k8s编排还在beta版本,Rancher 1.x的k8s编排每个版本之间变化较大,我们评估觉得还未能上生产环境。cattle的优势就是简单。

Q:请问你们的webhook那里是如何写的呢 ?我尝试了通过分支名字区分构建,也尝试了通过正则匹配 但是一直都没有成功.
A:webhook我们用的是自己搭建的gitlab中webhook功能,可能你哪里没有调对。

Q:有没有读取配置文件的时候网络或者其他原因不是每台机器都读取到最新的配置文件。
A:没有遇到过。遇到网络问题,可能读不到配置文件,导致容器启动失败。读到旧配置文件,那肯定就不是网络的问题了,你得检查别的地方。

Q:请问下容器内部使用rancher manage network吗?Rancher外部的网络是如何规划的?在CI过程中,比如QA环境对应develop分支,testA正在测试merge storyA的功能,这是又push一个storyB的功能上来,或者三个QA以上正在测试,这种情况会不会出现问题或者冲突呢?
A:1. 是用的rancher manage network;
2. 外部网络规划,我不太明白具体指哪方面;
3. 大型项目,同时多个story开发的,那么我们会使用feature分支的方式,每个feature分支对应一个story,会有一个对应的stack,多个story就会有多个feature stack。这些story开发完毕后,统一merge到develop分支,develop分支会对应一个唯一的CI stack。

Q:你们的Jenkins集群是怎么通过rancher管理的?
A: jenkins server部署在容器中这个没有问题,jenkins agent也在容器中。需要注意的是,jenkins agent需要占用一个主机端口,方便jenkins server连进来。我们用了7个左右的jenkins agent容器,由于数量非常少,因此手工管理即可。7 个agent可以管理上百个jenkins job,容量还是很大的。

Q:请问监控是怎么做的?基于什么技术?如何保证应用的健康状态呢?日志是如何展示的?
A:这两个问题都非常好,是我们遇到的比较核心的痛点。
1. Rancher的健康检查目前只支持tcp和http的,因此对于 web 服务是可以这么做的。但是有很多程序并没有暴露tcp或http接口,这样的程序我们通常不会去做内存限制,防止应用由于内存使用过多被杀掉,这样就会导致应用内存使用不可控。我们通过每台主机的内存监控来发现内存异常占用的程序,然后手工处理。
2. 应用的健康状态,我们会要求应用方必须提供 一个heartbeat接口。我们有统一的heartbeat监控,如果heartbeat 失败,就立即报警。这个heartbeat接口其实很简单,及时一个 restful 的接口。

日志的我们是通过将日志挂载到宿主机上,然后在宿主机上部署了filebeat将所有日志归并到一台主机上。然后在这台主机上部署了一个tty.js.这样开发就可以通过浏览器看到所有的日志,非常方便。敏感信息在记录日志的时候需要脱敏,所以让开发看线上日志没有问题。

评论