写点什么

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

2020 年 4 月 15 日

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

在本系列文章中,我们将探讨在容器时代如何在基于 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 流水线:


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

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

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

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

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


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


总结

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


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


2020 年 4 月 15 日 23:04161

评论

发布
暂无评论
发现更多内容

「架构师训练营」第 2 周作业 - 设计原则

森林

江帅帅:精通 Spring Boot 系列 03

古月木易

Spring Boot

程序员买买买,纸书半价,电子书55折,抢券叠加使用更划算

图灵社区

图灵教育 活动专区

第二周作业

戴维斯

架构是训练营

分布式柔性事务的TCC方案

奈学教育

分布式

依赖倒置原则联想

极客大学架构师训练营

江帅帅:精通 Spring Boot 系列 04

古月木易

Spring Boot

架构师训练营第二周作业

东哥

极客大学架构师训练营

架构师训练营 第 2 周总结

Lingjun

极客大学架构师训练营

架构师训练营-02作业

ashuai1106

架构师 极客大学架构师训练营 架构设计原则

centos7升级内核

唯爱

构架师训练营-第二周总结

Dawn

极客大学架构师训练营

对进入面向对象世界的思考

云飞

UML 面向对象设计原则

教程序员使用Jekyll搭建漂亮的个人博客

小傅哥

小傅哥 Jekyll GitHub Pages 个人博客

程序设计原则

南宫煌

极客大学架构师训练营

架构师训练营-第二周命题作业

牛牛

极客大学架构师训练营 命题作业

江帅帅:精通 Spring Boot 系列 04

奈学教育

Spring Boot

架构师训练营 week2

devfan

设计模式

「架构师训练营」第 2 周作业 - 总结

森林

软件设计原则

superman

产品周刊 | 第 19 期(20200614)

Herbert

产品 设计 产品经理 产品开发

Java高频BAT面试题汇总:SSM框架+Redis+高并发+MySQL+JVM带解析

周老师

Java spring 程序员 面试 IT

我写了10年博客,却被人说“不火”?我是这样怼回去的!

王磊

Java 程序人生 「Java 25周年」

分布式柔性事务的TCC方案

古月木易

分布式

杜克大学提出 AI 算法,拯救渣画质马赛克秒变高清

神经星星

人工智能 算法 分辨率 GAN

设计原则

东哥

极客大学架构师训练营

GitHub 热榜:适合初学者学习的 Prometheus 监控系统

JackTian

GitHub 运维 Prometheus 开源项目 监控系统

第二周课程作业

Geek_a327d3

作业

江帅帅:精通 Spring Boot 系列 03

奈学教育

springboot

架构师训练营 第2周作业

Lingjun

极客大学架构师训练营

在野望中奔跑:镜头前"摆摊"的联想来酷总裁们

Geek_116789

2021年全国大学生计算机系统能力大赛操作系统设计赛 技术报告会

2021年全国大学生计算机系统能力大赛操作系统设计赛 技术报告会

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