[关闭]
@gaoxiaoyunwei2017 2018-02-23T13:34:18.000000Z 字数 7984 阅读 651

Deploy anywhere: orchestrating the DevOps toolchain with Jenkins Pipeline -林栗

黄晓轩


讲师 | 林栗
编辑 | 黄晓轩

讲师简介

林栗
Micro Focus DevOps工程师

前言

我今天跟大家分享的话题是:利用Jenkins Pipline 来编排DevOps工具链,把我们的产品部署到任何地方。

image_1c5lg486l4mii8p1bqb1eko14dg9.png-112kB

主要内容分成三块:
第一个我会简单介绍一下我们公司的敏捷和DevOps转型;
第二个简单介绍一下DevOps工具链;
第三个重点是以Jenkins Pipline为核心,怎样实施一个以微服务架构为基础的,以Kubernetes为部署方法的持续集成和持续部署的话题。

敏捷和DevOps转型

image_1c5lgflrcl3f1iig197d1j3pd47m.png-262.2kB

开始之前我先简要介绍一下我们是谁,我们是干什么的。Micro Focus大家会比较陌生,但惠普大家都听说过。不久前2017年9月1日,原先惠普的软件业务跟来自于英国的Micro Focus公司进行了合并,于是江湖上就诞生了一家新的公司。两家公司的历史都非常悠久,最早可以上溯到1939年,在慢慢历史长河里面,如果用三个字概括一下公司的话就是“买买买”,一路上买了很多的软件。

image_1c5lgi0006891nu96lq16b21ci613.png-200.8kB

最终形成了混合IT、DevOps、安全风险以及预测分析这四大领域非常丰富的产品线。总而言之一句话,我们公司是做企业级软件的,我们客户主要是一些欧美的大公司、政府、银行。比如说迪士尼、家乐福、哥本哈根大学。介绍这些是想让大家了解一下我们是在做什么,以及为什么有这么一些DevOps的选择原因。

image_1c5lgllrsmqgc861bmh118nn4h20.png-327.6kB

做为一个软件行业的巨头之一,我们整个公司以Scaled Agile Framework这种框架来作为公司运作的框架。
在这样大的框架下我们正在经历从传统软件行业向DevOps的转型。我自己在这里面也非常深切的体会到了中间巨大的变革。

image_1c5lgqq59n4v2ka1rao125n1tqp2d.png-103kB

我们有什么样的变化:

image_1c5lhbvl5l9q1lgipbcov31e7n2q.png-74.3kB

说这么多那我们项目是什么样子呢?我选一个最典型的指标来跟大家说一下。那就是我们release周期,两三年之前我们是一年release一次。这在今天,大家可能觉得都不可想象。我们也在迅速的发生变化,在进步。我们从一年到六个月到三个月,我们目前能力是两个礼拜release一次,我们是很大型的软件。大家可能会说这有什么难的,可是看一下我们项目的情况,我选取了三种比较典型的项目特征。
项目A,这个项目有20多年,接近30年了。那时候没有Jenkins,C++很多功能也没有诞生,我们还要维护它。最大的挑战是工具链的升级,以及怎么降低维护它的人力成本。
项目B,比较新一点,最开始是以微服务作为架构设计诞生的。但有一个很挑战的地方,它的Jenkins job达到600多个。支撑这个项目的DevOps工程师人力是三分之一个。这个事情说明的是:微服务给我们的持续集成以及持续部署带来了更大的挑战和困难。
项目C,做的事情就是把A、B以及其它的项目产出的东西,我们把它容器化,把它装在Kubernetes里面,部署到各种公有云和私有云上面。这个项目挑战难度是有很多跨团队的协作,以及产品集成上面的困难。

举例

image_1c5lho0ct10aa106110471om814637.png-749.1kB

我们以这个项目C为一个例子,打个比方,我们干的事情是什么呢?好比说我们在用乐高积木搭一艘军舰,要一块一块的快速组装起来。我们舰队的成员大概有180多个工程师,有21个时光团队。我们的弹药库是6个大的产品,从逻辑业务上切分成了20多个service,每个service向下还可以切出很多组件和技术角度的service。我们Git仓库有120多个,发布给客户的东西有140多个Docker image ,在Kubernetes里面部署起来有145个pod。我们认为这么高度集成复杂的产品,自动化的测试系统是我们的生命线,而CI/CD系统就是我们这艘军舰的动力系统,通过CI/CD的流程,我们拉动项目运转周期,协调各团队不同角色之间的协作。

DevOps 工具链

我们为项目选择了什么工具。刚刚也讲到,我们公司自己本身就有非常丰富完整的从DevOps到IT运维,到项目管理到敏捷的工具都是有的。工具这个东西没有最好的,只有最适合的。我跟大家分享一下,实际用到了哪些工具。

image_1c5lj0d131nmi3u4jvs13m71qkm3k.png-211.8kB

大家看这张图上,Jenkins像一个老管家一样,他在这里的地位就是起到了对工具编排引擎的作用,可以理解是一个框架。这里有开源的也有商用的,也有我们公司自己做的,选这些工具有一些方法和思路。

image_1c5lj36sbm05115j1fv132i8u41.png-63.2kB

我先分享一下方法:
首先,建立一个我们想要达到的目标,以及工具所能提供功能的矩阵图,因为满足需求永远是第一步。
第二,我们要考虑工具跟其它工具集成的功能、能力以及难度。
第三,扩展性和灵活性。就是说它是否提供丰富的API,它插件的数量、质量,它提供的SDK等等都是需要考虑的。
第四,用户的接受程度。这个对使用面很广的工具是很重要的。
第五,我们的学习曲线,实施的难度和维护成本都要考虑。
第六,商用软件的话还要考虑技术支持。如果是开源的软件,要考虑社区的活跃度,以及热点趋势还在不在,
第七,应用的范围。我们的工具到底是用在项目内部的,还是我部门的,还是BU的,还是整个公司的。不同范围对软件的一些高可用性、性能、灾难恢复以及很多企业功能要求是不一样的。
第八,价格考虑因素。
第九,考虑所有因素以后最重要的是要做POC,要做工具之间demo的对比,以及在一些小范围的项目做试点。因为只有经过项目运作检验以后,才能够证明这个工具是适合我们的,其实我们在做的时候,有时候确实会推翻,会觉得发现有一些issue是没有办法解决的。

回到工具链图看一下,最左边的这列是项目管理以及协作的工具,前面乐高军舰的图上,写出了非常精确的人力投入的点,以及计划的点,怎么体现出来的?就是Agile Manager软件,这个系统非常好用。它完全是基于Scrum模型的项目管理,里面有丰富的数据管理,很漂亮的Dashboard等等。
在基础设施上选用了Docker和Kubernetes,在云平台选用了私有云Vmware和公有云AWS,并实现了持续部署。
这边源代码管理工具,这里有两个风格,一个是GitHub,另外一个是谷歌出品的Gerrit工具。这两种工具是完全不一样的理念,谷歌的Gerrit在安卓生态圈特别流行,他的理念更注重控制,更中心化,更严格的审核机制。GitHub是经典的【请补充】coding的理念和产品设计。我们做了POC以后认为GitHub是更适合我们想法的,全公司里面我们选择工具有很高自由度,没有强制要求。可是整个公司GitHub应该是第一流行的,大家都选择了它,我们也是把这个工具从公司层面做了全球部署,它的性能、界面和功能都是非常好的,应该是一统江湖的工具了,其它的大家可能各自有各自的想法。
打包和部署Maven和Ansible是比较通用的工具。【请补充】Terraform优点是可以对付各种不同云平台的基础措施,包括AWS和Google Cloud Platform, 微软的Azure和私有的OpenStack也是同样支持的。Cloud Formation 是专门针对AWS基础设施提供的工具。
其它还有很多的工具,我们在选择它的时候要符合自己的需求,要按照一定的套路来进行这样的抉择。

利用Jenkins Pipeline 实现CI/CD

有了上面的这些工具我们具体怎么做持续集成和持续部署的呢?讲到CI/CD的话题,我想引用一句托尔斯泰的名言:幸福的家庭都是相同的,不幸的家庭却各有各的不幸。CI/CD这个事情,模型和框架都是一样的,各家公司有各家公司的困难和挑战,有不同的条件限制,最终实现的效果也是不一样的。
下面我想分享一些idea,就是我们在实施CI/CD时的一些想法。我认为做这些事情为我们真正带来好处的点分享给大家,希望有遇到跟我们类似情况的时候可以有所启发。

image_1c5nm41bs19pjas51e7l11ar2cq9.png-202.7kB

首先讲CI/CD有一个先入的条件,就是软件的架构设计。我们是以微服务作为架构设计的。这个架构很大程度上就已经决定了你的流程该是什么样子,以及具体实施应该有什么样的要求。我们一些新的项目一开始用了微服务,对一些老的项目,我们也花了非常大的经历去做服务的定义和切割。具体来讲反映到DevOps能看到的角度就是把以前的单体代码仓库切割成若干个独立的,每一个仓库里面的内容和服务都可以独立的编译、部署、测试,这是我们第一步架构。

image_1c5noc2r13f16f81vfi1ts315m3m.png-155.4kB

基于微服务的架构,我们CI/CD的想法就是形成多层级CI/CD的系统。每一层的内部是一个小周期,在这个小周期里面是一个标准的构建、测试、部署完整的闭环。当他跑完这个闭环以后,会向上一个级别promote自己的一个版本,就进入一个新的大周期新一轮CI/CD标准的循环。例如:假设一个工程师提交了一行代码,那么就会至少经过组件级别、Service级别以及Suite集成级别,至少有三层CI/CD的循环,最终会进入Release的队列。
多层级的CI/CD,这个也不算很新奇,很多都是这么做的,我想说有下面三点需要特别注意。首先是Service之间集成的验证。理想很丰满,现实很骨感。我们做微服务总是希望服务之间是解耦的,不依赖的,但现实向我们业务上高度集成的产品,现实有时候确实是互相依赖、互相伤害的。就好比说,我自己的小周期是ok的,可A和B放在一起就不好了,也许旁边还有一个无辜的C说我也不好了,怎么办?我们的思路就是说要做通过基于API的契约测试。我希望对方该有什么样的行为,我去写case测是不是符合我的预期。
第二,我们有Promote的机制,必须是一种灵活和自动化的Promote和Rollback机制。我们做集成的时候总会挑某一个服务Stable版本,以及其他的官方认证过的Stable版本,放在一起做集成测试,如果ok,那就是新的Stable,如果不可以的话,就Rollback回去。这套机制也是借助了GitHub的Pull request 方式来完成的。
第三,很重要的一点,我们有这么多Service而且还分层级,我们Version mapping关系的维护是很重要的。每一层的组件,Service以及整个产品集必须有声明自己版本的机制。Stable是什么版本,最新是什么版本,RC、GN分别是什么版本,我下面用到哪些组件是什么版本,向上我又被谁用到了我的什么版本,这些都会有很明确的mapping关系,这些也是随着我们代码和配置是编译在一起的。并且我们是以一种自动化脚本的方式,能够自动更新他们的,这点很重要。

image_1c5nq475c31dnh8oc2qbr1vt71j.png-34.2kB

下面一个话题就是Operation as a service。我们把自动化测试和监控等等公共的能力作为一项服务,提供给每个不同团队。打个比方就以自动化测试的框架和案子来说,它实际上是运行在K8s里面的,是作为K8s里面的Service跟我们产品运行在一起的。这么做是因为安全上的一些考虑,Service之间相互调用的端口和API是不允许曝露到Kubernetes外面网络的,全部都要走K8s里面的网络。我们要测这些东西,那自动化的框架就必须要在K8s里面。这么做我们发现带来一个好处,我们不仅仅是有官方每天执行的测试和报告,任何一个人,他只要去部署我们的产品,他的环境里面都会缺省给他起这样一个Service以及相关的Pod,里面装的就是测试系统,就会帮他跑。更进一步这些测试的代码,以及我们部署的文件(例如:docker profile文件,K8s的yaml文件等)都是放在GitHub上面。任何一个团队,除了用我提供的这个标准,还可以把仓库复制过去,针对特殊的需要做一些修改,就很容易的做出测试框架。好处是每个小团队不用花力气维护这些系统,我只关心我的业务,去写case就好了。他做出来以后,按照他自己特殊的标志,在K8s里面运行起来。并且一些共性的东西还可以贡献给中心的仓库。与此类似,我们的监控也是这样做的。我们监控是用了Prometheus和Grafana,这本身就是天生的为K8s和Docker做的,它也是作为一个Pod跟产品环境部署在一起的。这不是我们官方有一个总的监控系统,我们任何人都可以做自己的小系统,这就很方便了。这个意思就是说共享了基础的能力。

image_1c5oad2gg1li91a9113agcpq170d9.png-127kB

接下来Pipeline as code,这个用到了Jenkins 2.0以后的一个核心功能。用代码、脚本定义我们的流水线。这个事情跟以前【请补充】是有一个革命性的变化,在我看来,我们每一个仓库,在它根目录下面都会有一个Jenkins file。这个文件就定义了该仓库流水线的行为,并且大家可以看到这个版本化的管理。在这件事情我认为不仅仅是流水线是代码定义,更进一步我们做的是通过代码定义了我们的流程和规则。
举例:我们用Kubernetes部署东西,要编写很多的yaml文件。它本身是不需要编译的,但是它的格式很敏感,无法在构建环节发现错误。在部署时,如果语法有问题就会失败,我们想尽早的发现错误,那我就会对它做语法检查,另外还有语义检查。我们有很多的Service运行在共同的环境里面,我们内部协商了很多端口,协商了很多lable和配置,大家要有这套规则,这些规则不是通过开会、邮件、写文档就可以约束得住的。我们就把这些规则全部翻译成了代码,在我们CI/CD环境里面跑,任何一个工程师要改Kubernetes中的任何一个配置,当他提交修改时发送到GitHub,CI/CD首先做的第一件事情就是对这些yaml文件语法跟语义做检查,如果通不过根本不可能让你部署。
Pipeline as code与Operation as a service解决的就是怎么样在微服务的组织下共享一些能力。

image_1c5ob46bk1jer8211jol12621ldom.png-93.5kB

Jenkins Pipeline至少有三种方式可以达到代码的复用:

这是为了把我们部署的行为和环境,不仅仅要降低维护成本,主要还是做标准化的统一。

image_1c5obc8s35p75snpqf183a17kq13.png-37.3kB

统一我们的build chain。既然我们是微服务的团队,每个服务之间采用的技术栈,差异是非常大的。尤其是像JavaScript这种,编译打包的方式各不相同。我们每个项目都是用Jenkins file方式来做流水线的。我们希望Jenkins内部行为,接口的对象尽量是统一。我们就采用Maven抹平每个Service之间技术栈的不同。Maven插件非常丰富,可以对C++、Java、npm都可以完成打包,甚至包括docker image也是用Maven做的。【请补充】你持续集成的时候根据你各种不同的需要,不同的参数打进去,才会形成最终的yaml文件。这都是用Maven做出来的。最终形成的效果就是说简化了Jenkins file内部的逻辑编程和配置。

image_1c5objp8huis1sas2hcgbh581g.png-33.2kB

接下来是pre-flight build和private CI/CD的概念。第一个是在任何变更合入分支之前,跑一套CI/CD保证质量的。第二个是我们CI/CD不仅仅只有官方系统可以跑,任何一个工程师可以对自己私人的CI/CD完成持续集成的。

image_1c5obo8kj19qb14o2hq71sco3lp1t.png-143.4kB

接下来看看怎么样跟GitHub集成。我们官方的git仓库原则上是不允许存在有特性分支的。我们是秉持着主线开发的模式,任何一个工程师工作的时候,首先复制出这么一套仓库到他私人仓库的名下,做完了修改,Pull Request到官方仓库。这时候GitHub的webhook会告诉Jenkins这个事件,Jenkins就会根据Jenkins file定义内容运行job,并回写消息给GitHub。当这个Pull Request被批准合入以后,同时也会再被执行CI/CD的流程。

image_1c5oc3l1i1lg018h71t6813d0k6t2a.png-118.1kB

具体的效果我们是用的MultiBranch Pipeline。这个插件有一个好处值得拥有,它可以扫描整个代码仓库,只要发现有Jenkins file,可以自动帮你创建出每一个分支job。当你Pull Request提交过来后会自动扫描帮你创建出Jenkins的Pull Request的job,如果关闭会在job上打一个横线,这个job就会自动的销毁了。这个是节省了很多配置Jenkins job上面人力的成本,全都可以自动的做出来。

image_1c5ocfr71dc2fovsdv1k6e1lb537.png-110.5kB

前面主要集中于持续集成,接下来我们看一下部署。我们部署的情况也是比较复杂的,公有云要支持AWS,微软的Azure,私有的要支持OpenStack和Vmware。部署在实验室或者客户环境里面都要进行部署,部署的用途有给Dev,有给QA,有给Staging环境,有给Demo环境。具体到产品安装的方式就更五花八门和复杂了,有不同大小、不同模块,我们是变形金刚,A+B、C+D等等的组合。总的来说我们要做这个部署,大致粗略的划分要做这几件事情:
首先,基于公有云,要创建出一套基础设施,AWS上的EC2,网络,存储等等,在私有云上面相映的虚拟机要创建出来。
其次,要把K8s安装起来。
最后,在K8s中起我们的Pod和Service等等。

image_1c5ocv11ku3t11uiuusckl1cm83k.png-49kB

我们要怎么做这个事情呢?我们的思路就是针对每一步高度抽象出独立的工具,这个工具的能力可以完成很复杂的配置。
第一层是用Terraform(支持各种其他公有云)、Clud Formation、Ansible(主要针对VMware)。
做了第一层基础设施以后,有时客户已经有自己的IT基础设施,那我们有一个Pre-check。这个工具会帮客户检查是否符合安装Kubernetes的条件,如果不符合要展示出来。
第三层我们要有一个工具Container Delivery Foundation,这件工具专门负责安装K8s,包括多master,多node,各种网络和存储配置,都是用这个工具来完成的。
第四层就是产品安装环节。我们用Go语言封装了Ansible做大量的复杂逻辑,最终达到的效果是通过输入各种不同参数和配置,就算一个小白,任何一个人拿到他都可以把有140多个Pod的产品在K8s里面跑起来。

image_1c5odahm9ag86o71sdr1hs610ig41.png-189.2kB

有了这些独立强大的安装部署工具以后,我们怎么样来把他编排起来的呢?主要是用Jenkins Pipline的方式。这里有很多共性的步骤可以复用的,图的上半部分是我们在实验室内部模拟了一次产品升级。下半部分是一次在AWS上面的安装部署。大家可以看到有一个stage:check service status,它总共出现了3次,背后的代码和工具都是一样的,是高度的抽象复用。不仅仅是为了节省成本,主要是为了标准化。要保证大家在所有情况下做的事情都是一样的。

image_1c5odfg4vceufas15ro9vq170q4e.png-192.7kB

接下来给大家介绍一款比较好玩的工具 —— Slack,是即时聊天的工具,可以做工作写作。它的意义是形成一个虚拟的作战室,相同的话题可以在一起有一个专门的频道进行聊天。像一些部署的消息,我们测试的报告,我们都会发到这个系统里面。更重要的是他是【请补充】界面、接口的工具。你想让这个工具做一些事情,对话框里面打一些关键字,Slack收到消息以后,就会去跟你想操作的系统讲话。非常好,在手机上也可以操作。

image_1c5odovdeuoak9uge71h3malp4r.png-82.3kB

我在DevOps领域想挑一个我认为最有价值的实践来讲,我认为是infra as code。
这个事情不仅仅是解放了我们自己,把我们的基础设施进行了代码的标准化定义,更重要的是做DevOps我希望是everything as code,包括我们的流水线、流程、规则等所有的都翻译成代码,为什么?这才是我们做到自动化轻松运营的基石和前提条件。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注