持续集成与持续部署宝典Part 2:创建持续集成流水线

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

在本系列文章中,我们将探讨在容器时代如何在基于Docker的环境中创建连贯的工作流程和流水线来简化大规模项目的部署。另外,我们还将详细介绍如何利用Docker和Rancher自动化处理这些工作流。

在上文《将构建环境容器化》中,我们开始了构建持续集成流水线的第一步工作——构建系统(Build System)的创建。我们分析了【Build】这一环节的常见的三大挑战——依赖管理、管理环境依赖、复杂项目的漫长构建时间,以及如何用传统工具与方法解决这些问题。接着,我们分享了如何利用Docker创建容器化的构建系统以更轻松地解决那些传统挑战,包括如何将构建环境容器化、如何使用Docker打包应用程序、如何使用Docker Compose创建构建环境,最终创造一个可重复的、集中管理的、良好隔离的、并行化的构建系统。

现在我们已经将【Build】系统创建好了,那么在本文中,我们将为示例的应用创建一个持续集成流水线。这样我们既可以确保遵循最佳实践,又可以确保彼此冲突的那些变化不会相互作用、引发问题。不过,在我们为代码建立持续集成之前,我们先花一点时间讨论如何将代码划分到分支中。

分支模式

在我们实现持续集成流水线的自动化时,一个需要考虑的重点是团队遵循的开发模式。这个模式通常由团队如何使用版本控制系统来决定。由于我们的应用程序托管在git仓库中,因此我们使用git-flow模型进行分支、版本化以及发布我们的应用程序。它是基于git仓库上最常用的模型之一。简而言之,该模型的思想是维护两个分支:一个开发(者)分支,一个主分支。每当我们想开发新功能时,就会从开发分支创建出新的分支,并在功能开发完成时,将它合并回来。所有功能分支都有开发人员单独管理。一旦将代码提交到开发分支,CI服务器将负责确保分支始终能够编译、通过自动化测试并且可以在服务器上进行QA测试和评审。当我们准备进行发布时,可以从开发分支创建一个发布,并将其合并到主分支中。被发布的特定的commit hash也会使用版本号进行标记。被标记好的发布项接着就可以被推送到Staging/Beta或者生产环境中。

下面我们将使用git-flow工具来帮助管理我们的git分支。安装git-flow请参考这里的说明:https://github.com/nvie/gitflow/wiki/Installation。安装好git-flow,你就可以通过下面所示的git flow init命令配置你的仓库。Git flow会问一些问题,我们建议你使用默认的设置即可。执行过git-flow命令后,它将创建一个开发分支(如果原先没有开发分支的话),并将其检出作为工作分支。

现在,我们使用git flow输入git flow feature start [feature-name]命令创建一个新功能。通常做法是用ticket/issue id用作功能的名称。比如,如果你使用的是Jira处理ticket,那么ticket id(例如,MSP-123)就可以作为功能名称。你还会发现当你使用git-flow创建新功能时,它将自动切换到功能分支。

到了这一步,你可以去完成该功能所需的全部内容,然后运行自动化测试套件以确保一切正常运转。一旦你准备好发布工作,只需告诉git-flow去完成这一功能即可。根据你对该功能的实际需要,你想要进行多少次提交都可以。在我的这个示例中,从我们的目的来看,我们只需更新README文件,并通过输入“git flow feature finish MSP-123”来完成更新。

需要注意的是,git flow合并了开发分支的功能,删除了功能分支并且返回到了开发分支。此时,你可以将开发分支推送到远程仓库(git push origin develop:develop)。 当你提交了开发分支,CI服务器就会接管持续集成流水线。对于更大的团队来说,一种更合适的模式是在完成功能之前将功能分支推送到远程,让它们经评审(review)后,使用pull request来合并到开发分支中。

使用Jenkins创建CI流水线

在这一节,我们假设你已启动并运行了一个Jenkins集群。如果没有的话,你可以在这里使用官方的Jenkins镜像:https://hub.docker.com/_/jenkins/ ,在这里可以看到更多关于建立可扩展Jenkins集群的内容:https://rancher.com/deploying-a-scalable-jenkins-cluster-with-docker-and-rancher/ 。在你有了运行的Jenkins集群后,我们需要在Jenkins服务器上安装下面的插件和依赖项:

Jenkins Plugins 2.32.2+

  • Git Parameter Plugin 0.8.0+

  • Parameterized Trigger Plugin 2.33+

  • Copy Artifact Plugin 1.38.1+

  • Build Pipeline Plugin 1.5.6+

  • Mask Passwords Plugin 2.9+

    Docker 1.13.1+

    Docker Compose 1.11.1+

安装好需要的插件后,我们就可以在【Build流水线】中创建最初的三个任务:编译、打包和集成测试。这些将作为我们持续集成和部署系统的起点。

构建应用

序列中的第一个任务将会在每次提交后从源码控制中检出最新的代码并且确保其可编译。它还会运行单元测试。如果要在我们的示例项目中设置第一个任务,选择New Item->Freestyle Project。进入项目配置视图中,选择General选项卡以及“The project is parameterized”选项。添加一个叫做GO_AUTH_VERSION的git参数,将参数类型设置为Branch(分支)或者Tag(标记)。接下来选择Advanced配置参数,使用Tag Filter设置获取匹配“v”的所有标记(比如v2.0)。将Default Value设置成develop(开发分支)。这将有助于从Git获取版本标签列表,并为该任务填充选项菜单。如果该任务要在没有给定值的情况下自动触发,那么GO_AUTH_VERSION默认设成develop(开发)分支。

接下来,在Source Code Management选项卡部分添加仓库url,指定分支为${GO_AUTH_VERSION},这样手动构建时将会使用git参数来选择分支或标记进行构建及设置轮询间隔。这样一来,Jenkins就会持续追踪我们开发分支的未来所有更改,在我们的CI(和CD)流水线中自动触发第一个任务。这里要注意的是,GO_AUTH_VERSION的默认值(比如开发分支)将用于自动检测到的更改。

现在在Build选项卡部分选择Add Build Step > Execute Shell并从本章前面部分复制docker run命令粘贴到这。这样可以从Github获得最新的代码,并将代码构建到go-auth可执行文件中。这里需要安装并运行docker。如果你使用的是Linux服务器,可能还需要在docker客户机命令中添加sudo以便能够访问docker守护进程。

在构建步骤之后,我们需要添加两个后续步骤,其中Archive the Artifacts(工件归档)将go-auth二进制文件和帮助脚本归档到项目中。我们在任务中需要指定下列的工件进行归档。

接着我们使用Trigger parameterized builds(触发器参数化构建)启动流水线中的下一个任务,如下图所示。在添加Trigger parameterized build时,请确保是从Add Parameters中添加的Current build parameters。这样可以让当前任务的全部参数(比如GO_AUTH_VERSION)用于下一个任务。请留意在Trigger parameterized build部分中用于下游任务的参数名称,我们将在下面的步骤中用到。

构建任务的日志输出应该像下面展示的这样。你可以看到我们使用了docker化的容器来执行构建。构建时将使用go fmt来修复代码中任何格式的不一致,并且执行我们的单元测试。如果出现测试失败或者编译不通过,Jenkins就会检测到失败。此外,你应该通过email或者chat integrations(例如Hipchat或者Slack)设置通知,这样在构建失败时就能通知到你的团队,快速地修复它。

打包应用

代码编译通过了,接下来我们就能将它打包进Docker容器中。创建打包任务的方式是,选择New Item > Freestyle Project并给你的第二个任务起一个名称,该名称对应于在先前任务中指定的内容。和之前一样,该任务也是一个带有GO_AUTH_VERSION参数的参数化构建。要注意的是,这里和所有后续的任务中,GO_AUTH_VERSION只是一个默认值为develop(开发分支)的string参数。我们希望它的值是从上游传过来的。

和之前一样,添加构建步骤来执行shell。注意这里你不需要指定SCM设置,因为我们将从前一个构建生成的工件中提取所需的二进制文件和脚本。注意一下,我们这里是先创建未标记的usman/go-auth,之后再重新标记,这样我们就可以在后面执行集成测试,搭建好未打标记的容器环境。

为了构建Docker容器,我们还需要前一步中构建的可执行文件。为此,我们增加一个构建步骤复制上游构建中的工件。这样可以保证我们有可用于Docker构建命令的可执行文件,该命令可以打包到Docker容器中。注意这里我们选择了flatten目录来保证所有工件都复制到当前项目的根目录中。

我们一直在使用GO_AUTH_VERSION变量标记我们正在构建的镜像。在默认情况下,开发分支的变更,总会构建usman/go-auth:develop并覆盖现有的镜像。在下一章中,我们会发布应用程序的新版本并重新审视这个流水线。

和之前相同,使用了Trigger parameterized builds下(包含了Current build parameters)的后构建(post-build)来触发流水线中的下一个任务,该任务将使用我们刚刚构建好的Docker容器以及在之前章节中详述的Docker Compose来执行集成测试。

执行集成测试

接下来是执行集成测试,先要创建一个新任务。和打包任务相同,新任务是一个使用GO_AUTH_VERSION string变量的参数化构建。然后从构建任务中复制工件。这一次我们将使用上面的Docker Compose模板来搭建一个多容器测试环境,并对我们的代码执行集成测试。集成测试(不同于单元测试)通常是与正在测试的代码完全隔离的。因此,我们会用到一个shell脚本,它可以针对我们的测试环境执行http查询。在执行shell命令中,将目录修改为go-auth并执行integrationtest.sh。

脚本的内容可以在这里找到:https://github.com/usmanismail/go-messenger/blob/master/go-auth/integration-test.sh。我们用Docker Compose搭建我们的环境,然后使用curl发送http请求到搭建的容器中。这项任务的日志将会和下图显示的相似。Compose将会启动一个数据库容器,并将它连接到goauth容器。数据库连接之后你应该会看到一些列“Pass: …”信息,说明各项测试都在运行和验证。测试完成后,compose模板将会自行清理数据库和go-auth容器。

经过了三个任务的设置,你就可以在Jenkins视图中选择【+选项卡】,选择build pipeline view来创建一个新的构建流水线视图。在弹出的配置界面中,选择你编译/构建的任务作为初始任务,然后选择ok。现在你应该能看到CI流水线已经成型。这将给你一个可视化引导,展示每个提交是如何通过你的构建和部署流水线的。

当你更改开发分支时,你会注意到流水线是由Jenkins自动触发的。如果要手动触发流水线,选择你第一个(构建)任务,运行它。系统会要求你选择git参数的值(比如GO_AUTH_VERSION)。不指定的话会执行默认值,并且会运行针对开发分支中最新内容的CI流水线。当然,你可以直接在流水线视图中单击“Run”。

我们快速回顾一下到现在为止我们所做的工作。我们通过以下步骤为我们的应用程序创建了CI流水线:

  1. 使用git-flow添加新功能,并将他们合并到开发分支中。

  2. 跟踪开发分支的变化,在一个容器化环境中构建我们的应用程序

  3. 将我们的应用程序打包到docker容器中

  4. 使用Docker Compose搭建短生命周期环境

  5. 执行集成测试以及清理环境

通过上面的CI流水线,每当新功能(或者修复)合并到开发分支时,CI流水线就会执行上述所有的步骤,创建出“usman/go-auth:develop” Docker镜像。此外,我们在接下来的章节中将构建更深层的集成部署流水线。另外因为该视图有清晰的测试阶段,你还可以使用此视图将应用程序版本推广到各种部署环境中。

总 结

在本章中,我们分享了如何利用Docker为我们的项目创建一个持续集成流水线,该流水线是集中管理的、可测试的,并且在机器和时间上可重复。我们能够根据需要对各种组件的环境依赖进行隔离。这是我们未来部署一条更长的基于Docker构建和部署的流水线的起点,我们将在下一次的文章中继续构建这一流水线的余下部分并将过程经验记录下来。

我们流水线的下一步是创建持续部署,后续文章我们将展示如何使用Rancher部署整个服务器环境来运行代码,我们还将介绍如何为大型项目设置长期运行的测试环境和持续部署流水线的最佳实践。

转载于:https://my.oschina.net/u/3330830/blog/1915645

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/451077.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

64 装饰器函数: 母版 csrf防御机制 cookie

主要内容: 1: 装饰器函数 a: 原理: 在不改变原函数的代码和调用方式的情况下, 给函数动态的添加功能 b: 实例: 装饰器的原理: def yue(tools):print(使用%s约一约 % tools) def wrapper(fn):def inner(*args, **kwargs):print(先准备好钱)fn(*args, **kwargs)return inner yue …

Eclipse将引用了第三方jar包的Java项目打包成jar文件的两种方法

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 方案一:用Eclipse自带的Export功能 步骤1:准备主清单文件 “MANIFEST.MF”, 由于是打包引用了第三…

一个判断session是否过期的小技巧

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 Session一直是我们做web项目经常使用的,以前没太注意,这次又细致的看了下! 1.session其实就是一个Ma…

jQuery基础-事件

只能添加一个处理函数,后面的会覆盖前面 会有兼容性差异 冒泡 会依次检查父元素 截获冒泡 或者 最大的缺陷 并且在IE和NetSpace里面分别为冒泡和捕获两种 多个处理函数 依次执行 捕获--->自己--->冒泡 true 只捕获,不冒泡 false 只冒泡 从父元素的…

别的AI还在打游戏,这个AI已经当上“超级马里奥”游戏策划了|GECCO最佳论文

AI打游戏已经不是什么新鲜事了,“沉迷”Dota 2、星际争霸、LOL的AI一个接一个的出现。 但是你也许不知道,相比这些“游戏玩家”AI,还有一位优秀的AI,直接当起了“游戏策划”,做的还是脍炙人口的“超级马里奥”。 &…

单点登录的三种实现方式

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 单点登录SSO(Single Sign On)说得简单点就是在一个多系统共存的环境下,用户在一处登录后,…

java对象占用内存大小?

一个不包含任何内部成员变量的空Object大约占33byte,若增加成员变量,则增加相应大小的内存占用。 测算方式:设置jvm的堆大小为1m,在堆中不停new不含任何成员变量的OOMObject对象,直到堆内存溢出。如下图,在…

Java程序员面试中的多线程问题

很多核心Java面试题来源于多线程(Multi-Threading)和集合框架(Collections Framework),理解核心线程概念时,娴熟的实际经验是必需的。这篇文章收集了 Java 线程方面一些典型的问题,这些问题经常被高级工程师所问到。 0.Java 中多线程同步是什…

单点登录原理与简单实现

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 一、单系统登录机制 1、http无状态协议 web应用采用browser/server架构,http作为通信协议。http是无状态协议,浏…

java接口类支持多继承

一个类只能extends一个父类,只能有一个父类,但可以implements多个接口。java通过使用接口的概念来取代C中多继承。与此同时,一个接口则可以同时extends多个接口,却不能implements任何接口。Java中的接口是支持多继承的。

xmind-HTTP协议

转载于:https://www.cnblogs.com/margot921/p/9764788.html

弹性布局

弹性布局 一、Flex布局是什么? Flex是Flexible Box的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。任何一个容器都可以指定为Flex布局。 二、基本概念 采用Flex布局的元素,称为Flex容器(flex container&…

解决dataTable 报错:cannot read property “style“ of undefined

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 其实这错,我之前也遇到过,只是太久了,没有记录下来, 今天看到群里朋友也遇到这个错误&a…

万恶之源 - Python基础数据类型一

整数 整数在Python中的关键字用int来表示; 整型在计算机中运于计算和比较 在32位机器上int的范围是: -2**31~2**31-1,即-2147483648~2147483647 在64位机器上int的范围是: -2**63~2**63-1,即-9223372036854775808&…

jackson/fastJson boolean类型问题

1.我们以Person对象举个栗子,person有三个属性。name,age和isGay Data public class Person {public Person(String name, int age, boolean isGay) {this.name name;this.age age;this.isGay isGay;}private String name;private Integer age;priva…

狗窝里的小日子 ...

来,把平时作的菜菜整理下下: 1. 2. 3. 4. 5. 6. 7. 8.

Android直接用手机打包apk!

你没有看错,用手机浏览器访问Jenkins,就可以打包apk,并生成下载二维码,发送邮件通知测试人员下载,从此解放双手,告别打包测试。先上本人手机邮箱收到的打包成功通知效果图: 废话少说&#xff0c…

java中byte、short、char、boolean实际都是按照int处理的!

byte、char、short、boolean四种类型在汇编期或运行期间采取和int类型一样的存储方式,在计算时会先转换为int类型,后进行计算。所以两个short类型数据做算数运算,结果却为int类型。这主要是因为jvm的字节码为了简洁高效,设计时只使…

狗窝里的小日子- 2 ...

来,把平时作的菜菜整理下: 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20.

优秀程序员必备素质——快速调试

你是否有过这些经历: 1.代码敲完了,刚想松口气,一运行程序,满满的Bug。 2.找啊找啊找,怎么找都找不到哪里出了问题。 3.调试了半天出不来,就开始便得心烦气躁。 4.一天连一个Bug也没调出来,…