基于Jenkins的持续交付全流程设计与实践

CloasGao 2020-01-09

1 从理论开始

什么是DevOps?

近年来,随着DevOps理念的逐渐深入人心,企业逐渐意识到从看似重复的手工劳动中实现自动化流程处理,对于提高企业劳动生产力已经非常重要,尤其是面向互联网的开发者,往往每次上线时,最大的挑战并非需求的走查或测试和改bug,而是由于发布的流程不够规范,将成果发布到目标环境后可能造成的配置错误或引发其他已知未知问题所造成的额外工作量,使得生产环境的发布流程总会存在不顺利。

而DevOps则致力于统一整合软件开发和软件运维,其特点是强烈倡导对构建软件的所有环节(从集成、测试、发布到部署和基础架构管理)进行全面的自动化监控。目标是缩短软件开发周期,提高部署频率和更可靠的发布,与业务目标保持一致。

在实际操作过程中,对于许多公司而言,开发者和运维者并没有明确的界限,而且即便将运维与开发的岗位职责分开了,也并非意味着双方的职业发展方向将大不相同。实际上在实际操作过程中,开发人员和运维人员依然会使用相同的工具、统一的流程、一致的管理思想,并通过一系列管理手段和工具实现流程的优化。

什么是持续集成和持续交付?

持续集成(Continuous Integration)与持续交付(Continuous Delivery)也正是DevOps中最为基础的两种企业级研发和交付活动。

持续集成来源于敏捷项目管理思想,其核心是团队成员应该经常集成他们的工作,通常每天要求集成一次,当然也可以要求团队成员每天集成多次。每次集成之后,会通过持续集成工具自动运行自动化的构建手段(例如编译、单元测试、集成测试、系统测试),并对集成后的成果进行验证,从而实现了企业管理流程中尽早的发现未知问题的目标。而在传统的软件交付中,可能会将所有问题积压到系统整体测试或甚至UAT(用户验收测试)环节,使得软件的测试时间被拉长,甚至使得软件的问题流入到客户现场,让客户成为小白鼠的情况时有发生。

实际上而言,看似简单的集成,却并非简单,他应该是企业管理过程中的一项铁律,只有严格执行,才能确保软件时刻处于可用状态;否则就意味着所谓交付过程中完成进度的百分之多少,只不过是一个虚无缥缈的空口白话。

持续集成往往离不开持续交付,在乔梁老师翻译的《持续交付》一书中,作者Jez Humber说:持续交付是一种能力,也就是说,能够以可持续的方式,安全快速的把代码变更(包括特性、配置、缺陷和试验)部署到生产环境中,让用户使用。这本书的作者也在书的最后一章中指出:它(持续交付)不仅仅是一种新的软件交付方法论,而且对依赖软件的业务来说,也是一种全新的范式。

持续交付与持续部署看似类似,其实有所区别,前者往往是指将环境推送到用户面前,使用户能够触及和使用它们;而部署则仅仅只是把软件包安装到目标计算机上,用户可能还无法直接使用。

对于面向互联网的软件企业来说,往往都已经在过去若干年间已经建立了一套完整的持续集成/持续交付流程,但对于某些处于飞速发展期的企业来说,依然相对而言后知后觉,主要是由于企业过去飞速发展的背后所依托的人力物力资源,能够足以保证企业的产出能够适应企业发展的需要,然而随着团队规模的发展赶不上企业业务发展的需要时,重复劳动和看似毫无价值的等待期、后期积压的测试任务、无法有效度量的软件功能实现,实质上也会造成企业的管理成本进一步提高。

从哪里可以获得系统的方法论?

对于需要搭建一套完整环境的开发者来说,网上资料很齐全,本文也试图尽可能的对各方面的内容进行综述,努力为开发者提供一个开箱即用的操作流程,但是对于那些想系统的学习持续交付或DevOps领域的知识的开发者来说,你其实不仅仅满足于把环境搭起来,那么你应该看看书。

在持续集成和持续交付领域有大量优秀的作品,而我觉得来自乔梁老师的作品《持续交付2.0》堪称精品,乔梁老师是一位经验丰富的行业专家,在他的职业生涯中积累了与该领域相关非常丰富的产品研发经验,他也身体力行的参与到许多企业的持续交付流程优化过程中,这些经验都让他能够从更全面的视角来分析持续交付的问题。在这本书中介绍了许多直接拿来就可以使用的管理方法、项目案例、工具,能够让有需求在该领域有所作为的开发者带来不少思考。

实质上对于一家要实践持续集成/持续交付的企业来说,将工具搭建完成并非核心难点,难点依然在于如何使用敏捷项目管理的思想,实现软件的细粒度任务拆分,并能够对单个任务进行更好的测试,或许TDD是一种不错的模式,但是却可能给开发者的基础技能提出了更高的要求,这将导致TDD无法落地。

如何快速验证产品需求?在《持续交付2.0》中提出的了一系列的方法,例如装饰窗、最小可行特性法、特区法、定向搜索法、稻草人法、、最小可行产品法等六种方法,通过建立快速验证模型,提高软件从需求到实现的整个流程,能够为企业带来不少便利。

而在研发阶段,可以采用特性分支和特性开关的手法,利用git源代码管理工具分支的妙处,将需求和代码有机的耦合在一起,同时又依托项目管理工具,实现从需求=》实现=》发布的完整闭环,从而为需求的验证提供了双保险;特性开关我最早在刘华老师的《猎豹行动-敏捷转型》一书中看到,通过使用软开关的形式,避免未开发完成的提前上线造成巨大的风险,而在这边书中也同样提到了这样的方法。在.NET中同样也可以使用特性分支组件,后期我将尝试一下。

而对于如何减少等待期,作者提到的方法是:

1、通过“拉动”让价值流动起来,例如,如果是一个生产线的滞留,通过扩大瓶颈的处理能力,让更多的需求能够快速交付,这种手法看似能够临时提高环节处理能力,保障团队的产出,但是显然不是个良好的措施,更合理的措施就是根据下游的生产能力来确定上游的处理能力,由下游来拉动上游的需求。这客观上要求将任务和需求的粒度进一步均匀化,将需求划分成更加易于执行、工作量类似的小需求,使得开发过程更加平滑。

2、任务自助化:也就是让团队掌握某些通用技能,以便在其他人员阻塞时,能够同步完成相关任务,避免了某些关键任务阻塞造成了整体流程的滞后。

在此我就不过多描述书中的精华了,有兴趣的可以入手一本,绝对物超所值。

2、总体流程和环境部署

接下来我将进入本文的主题,首先我将构建一个简单的企业级持续集成/持续交付的管理流程,然后再对流程的实现过程进行较为详细的介绍。

流程图

[图片](基于Jenkins的持续交付全流程设计与实践

)

在这个流程中,使用了master/dev的分支模式。

1、对于dev分支提交的代码,经过代码编辑、静态代码扫描、自动化单元测试的流程,在运行通过后,有测试人员进行代码的测试,并在代码测试通过后,通过pull request提交给master分支的审查人员进行代码检查和合并。

2、测试人员对dev提交的代码进行确认,并由master分支代码审查人员进行代码审查,通过后对代码进行确认,并生成用于发布的生成包。

涉及的组件和说明

在流程中,使用了以下工具,依次安装即可。

1、安装OpenJDK

2、安装Jenkins和相关插件

3、安装PostgresDb

4、安装SonarQube

5、安装dotnetsdk3.1

6、安装git

7、安装nexus包管理器 for windows版用以实现包管理。

8、安装Qy Wechat Notification或HTTP Request 用以实现企业微信提醒。

好吧,环境安装就不介绍了。。

安装补充说明

1、其中jenkins安装的版本为2.190.3,OpenJDK安装的版本为openjdk12.0。安装完jenkins和openjdk后,需要进行环境变量的设置。

2、SonarQube安装的版本为7.9.1,根据官方网站的说明,推荐使用的数据库包括:sqlserver\oracle\postgresdb,在7.9.1和更高版本中,已经不再推荐使用mysql。

3、SonarQube默认使用了基于H2内存数据库的嵌入式数据库,可以在测试环境下使用,但是不建议用于生产环境。

5、安装完sonarqube、和数据库后,需要修改sonarqube/conf/sonar.properties文件中的数据库配置地址,并将sonarqube的服务重启。

[图片]( 基于Jenkins的持续交付全流程设计与实践

)

在windows系统中,点击sonarqube-xxx\bin\windows-x86-64文件夹中的InstallNTService.bat用以安装SonarQube的服务,而StartNTService.bat则用于启动SonarQube的应用服务。如果数据库配置失败,则SonarQube会启动失败,并提示以下错误:

[sonar-1510653879773] exception caught on transport layer [[id: 0x346b46fb, /127.0.0.1:59330 => /127.0.0.1:9001]], closing connection
java.io.IOException: An existing connection was forcibly closed by the remote host

6、由于使用了自行搭建的Nuget包源管理器,所以在进行构建时,会提示错误,jenkins会使用

Jenkins的项目类型

在jenkins中提供了自由风格、单流水线、多分支流水线、多配置项目等不同类型的项目,可以根据实际情况进行取舍,在本人的尝试过程中,分别总结了三种不同类型的项目可适用的场景:

自由风格项目

操作流程简单,无需配置groovy脚本,即可简单的完成项目的自动化构建。

在自由风格模式的项目中,实现代码编译的过程主要在构建窗口中,主要使用dotnet -相关命令来完成。包括:

1、dotnet restore 还原依赖包。

2、dotnet build 编译

3、dotnet publish -o ./bin/release?发布到指定目录下。

4、如果需要使用sonarqube来进行静态代码检查,需要在服务器上安装dotnet-sonarscanner组件,这个组件是基于.net core构建的静态代码检查组件,安装的命令为:

dotnet tool install --global dotnet-sonarscanner --version 4.8.0;

5、如果采用.net framework 传统框架,则可以继续使用原来的SonarScanner.MSBuild.exe组件进行代码检查结果的上传。

6、如果需要在自由风格项目中使用powershell脚本,可以在jenkins=》插件管理=》可用插件中搜索powershell即可。

单流水线项目

单流水线项目:可适用于只有一个分支和一套环境需要部署时的项目构建,其发布流程需要使用groovy脚本来实现。点击查看pipeline的语法

1、在流水线项目中,都在项目文件的根目录中添加jenkinsfile文件(无扩展名)作为jenkins编译时的脚本文件,而这个文件的脚本语法采用groovy语言,并支持开发者按照脚本语言进行扩展。

2、在单流水线项目中不支持groovy的分支判断条件,支持逻辑比较简单的脚本。

3、与编译有关的结构均写在jenkinsfile中,因此jenkins的UI界面可以理解为配置与项目相关的环境变量信息。

4、可以在jenkinsfile中定义输入的参数,例如:

parameters{
? ? ? ? string(name:‘ProjectName‘, defaultValue: ‘Enter Your ProjectName‘, description: ‘Enter your project name here‘)
? ? ? ? string(name:‘Contact‘, defaultValue: ‘"@All","xxx"‘, description: ‘Enter Your Contract‘)?
? ? ? ? string(name:‘RepoUrl‘, defaultValue: ‘https://xxx.com/xxx/xxx.git‘, description: ‘ gitee代码路径‘)
}

在jenkins界面中,可以显示成

[图片]( 基于Jenkins的持续交付全流程设计与实践

)

在具体场景下就可以通过jenkins界面传入相关参数进行编译的测试了。

多流水线项目

多分支流水线项目:使用于一个仓库下各分支不同环境需要部署时的项目构建,其发布流程也需要使用groovy脚本实现。

1、多流水线项目支持使用分支判断条件的语法,因此可以使用的场景更多。

2、其他的总体上和单分支流水线差不多,此处就不在赘述了。

以下编写了一个简单的示例,仅供参考。

pipeline{
? ? agent any?
? ? parameters{
? ? ? ? string(name:'Contact', defaultValue: '"@All",""', description: 'Enter Your Contract')?
? ? ? ? string(name:'RepoUrl', defaultValue: '', description: ' 代码路径')
? ? ? ? string(name:'SonarUrl', defaultValue: 'http://xxx:9000', description: ' sonar代码路径')
? ? }?
? ? stages {
? ? ? ? stage('When Master') {?
? ? ? ? ? ? when {
? ? ? ? ? ? ? ? expression {BRANCH_NAME==~/(master)/}
? ? ? ? ? ? }?
? ? ? ? ? ?steps{? ? ? ? 
? ? ? ? ? ? ? ?checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'xxx', url: params.RepoUrl]]]) 
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? stage ("When Dev"){
? ? ? ? ? ? when {
? ? ? ? ? ? ? ? branch 'dev'
? ? ? ? ? ? }?
? ? ? ? ? ? steps{ rojectName}")? 
? ? ? ? ? ? }
? ? ? ? }?
? ? ? ? stage("test"){
? ? ? ? ? ? when{
? ? ? ? ? ? ? ? expression{return true}
? ? ? ? ? ? }
? ? ? ? ? ? steps{
? ? ? ? ? ? ? ? echo "OK"
? ? ? ? ? ? }
? ? ? ? }? ??
? ? ? ? stage('Web Dev Build') {
? ? ? ? ? ? steps{ 
? ? ? ? ? ? }
? ? ? ? }? 
? ? }
? ? post{
? ? ? success{
? ? ? ? SendToWeChatWork("CI Task success,ProjectName is ${params.ProjectName}")?
? ? ? ? echo 'Publish Success'
? ? ? }
? ? ? failure{
? ? ? ? SendToWeChatWork("CI Task Failure,ProjectName is ${params.ProjectName}")?
? ? ? ? echo 'Publish Failure'
? ? ? ? }
? ? }
}
def SendToWeChatWork(content) {? ? 
}

多配置项目

如果组件代码需要在不同的配置、不同的环境下重复部署,其基本逻辑类似,只是配置不同,就可以使用多配置项目。

好吧,我就没有尝试了,因为我已经用了多流水线项目来实现了。在这篇示例中对多配置项目有比较详细的用法,需要可自取。

总结

将企业级持续集成的环境搭建起来本身并不难,难的是如何将整套体系与公司现有的开发流程相结合,考虑到受康威定律的影响,不同的组织对于新事物的接受程度总是不同的,原有组织或许已经习惯了基于手工拷贝再部署的模式,而目前采用这种持续集成、持续发布的模式,会出现哪些问题,我已经做好了准备了。

相关推荐