本文由 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 可以获得对应的版本号,然后把这个版本号通过变量把它注入进去:
这一过程还涉及到 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:
是用的 rancher manage network;
外部网络规划,我不太明白具体指哪方面;
大型项目,同时多个 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:这两个问题都非常好,是我们遇到的比较核心的痛点。
Rancher 的健康检查目前只支持 tcp 和 http 的,因此对于 web 服务是可以这么做的。但是有很多程序并没有暴露 tcp 或 http 接口,这样的程序我们通常不会去做内存限制,防止应用由于内存使用过多被杀掉,这样就会导致应用内存使用不可控。我们通过每台主机的内存监控来发现内存异常占用的程序,然后手工处理。
应用的健康状态,我们会要求应用方必须提供 一个 heartbeat 接口。我们有统一的 heartbeat 监控,如果 heartbeat 失败,就立即报警。这个 heartbeat 接口其实很简单,及时一个 restful 的接口。
日志的我们是通过将日志挂载到宿主机上,然后在宿主机上部署了 filebeat 将所有日志归并到一台主机上。然后在这台主机上部署了一个 tty.js.这样开发就可以通过浏览器看到所有的日志,非常方便。敏感信息在记录日志的时候需要脱敏,所以让开发看线上日志没有问题。
评论