概 述
互联网软件的开发和发布,已经形成了一套标准流程,最重要的组成部分就是持续集成(Continuous integration,简称 CI)。
在讲解之前,先给大家普及几个概念:
- 持续集成(Continuous integration,简称 CI):持续集成指的是,频繁地(一天多次)将代码集成到主干。持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。
- 持续交付(Continuous delivery):指的是,频繁地将软件的新版本,交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。
持续交付可以看作持续集成的下一步。它强调的是,不管怎么更新,软件是随时随地可以交付的。 - 持续部署(continuous deployment):是持续交付的下一步,指的是代码通过评审以后,自动部署到生产环境。
持续部署的目标是,代码在任何时刻都是可部署的,可以进入生产阶段。
持续部署的前提是能自动化完成测试、构建、部署等步骤。它与持续交付的区别,可以参考下图。
我们常说的CI/CD就是指上面的持续集成、持续交付和持续部署。
一般来说,一个完善的git代码的分支模型,主要有以下几个分支:
master:用于生产环境的分支,一般是用来产品上线或者发布使用
hotfix:用于对master分支上发现的紧急bug或者问题进行的修复分支,修复完毕后合并到master和develop分支
release:用于对产品的预上线的分支,即准备上线分支,即上线前的预发布分支,供内测及上线,一般master分支的代码主要由release提交
develop:用于开发者产品开发的合并分支,即不同的coder开发完程序之后,进行代码合并的分支
feature:feature分支即我们开发者个人的分支或者某个新增功能的分支,用于开发阶段的分支
CI/CD流程
在了解了以上git分支模型的概念后,我们就来梳理一下CI/CD的整个基本流程
1、我们开发者每天在自己的feature分支上进行了代码的编写,并提交以后,需要将自己的代码提交到develop分支上,此时需要持续集成(CI)来为我们的代码进行编译构建
即通过Jenkins服务器或者基于Jenkins的云服务器进行代码的拉取、编译、单元测试甚至构建到开发测试服务器上。
这步的工作一般可以设置为develop有提交即可进行或者每晚固定时间进行一次,按需设置即可
同时这一步的构建成功或者失败,尤其是失败了,可以直接反馈到开发人员这里,进行及时修复,属于第一道关卡
2、构建成功的代码,即可以部署到生产环境进行运行,通过Jenkins服务器打包发布文件,并推送到相关服务器上进行运行,即持续部署(CD)
但是我个人理解,持续部署这个操作可以发生在我们开发过程的好几个阶段
2.1 当我们提交develop分支的代码后,通过CI进行了持续集成,CI结束后,可通过Jenkins服务器推送到我们自己开发的测试服务器进行部署(CD)。
2.2 在开发服务器进行自测完毕,并解决了大部分bug之后,我们可以再CD到正式测试服务器上,供测试部门及三方测试单位进行评审和测试。
3、正式测试完成之后,我们可以将我们的产品进行打标签,从develop分支上提交到release分支,提交到release的分支也可以再进行一轮CI/CD的流程,为生产环境的发布做准备,
同时也可以将release部署出来的版本供外界进行公测等。
4、当release版本的试运行及公测都基本稳定并按计划允许上线的时候,我们将release的分支提交到master分支上,再进行一轮CI/CD,将打包的产品推送并发布到正式环境服务器上,供线上使用。
综上所述:CI/CD是一个持续的自动化集成和部署的一个过程,存在于我们的开发、测试、生产的每一个环节
以下是基于我们自身的实际需求,进行的一个简单的CI的过程,基于coding平台的持续集成,进行从不同的仓库拉取相关的代码,进行dotnet的编译,并将生成的dll提交到对应的代码仓库中。
首先在coding的项目中,有较好的帮助文档可以帮助我们使用其功能,https://help.coding.net/docs/ci/intro.html
找到左侧的持续集成->构建计划->创建构建计划,创建构建计划中有内置的相关一些模板,如图
创建构建计划
基础信息配置
我这块选择的是自定义构建过程,选择后,进入coding的基础信息配置,这里允许我们选择相关的代码仓库来源,如coding、GitHub、GitLab、码云等,我这里用到的是coding
然后会选择代码仓库,选择了代码仓库以后,我们默认的git操作对象就是该仓库,同时相应的触发规则也是基于该代码仓库进行的,当然我们也可以拉取操作其他的仓库代码,详情见下文。
配置来源里面中我们使用静态jenkinsfile,这里还提供了一个选项是使用代码仓库中的jenkinsfile,意思就是我们可以从git仓库里拉取代码后使用里面的Jenkinsfile执行计划,存储于代码仓库里的jenkinsfile有利于我们的维护和修改
节点池配置:coding进行CI的时候,默认会提供一台Linux Ubuntu的云服务器,用来执行我们的相关命令及程序,所以这里的节点池配置就是云主机的节点信息,我们选择上海的主机即可。
流程配置
流程配置里,主要用来设计我们构建计划的整个流程,即CI流程,这步就是我们CI的核心部分,他支持图形化编辑器和文本编辑器
图形化编辑器就是我们在流程图上点击按钮增减相应的流程、命令等。他默认内置了一部分常用的流程空间,我们点选即可。
文本编辑器就是我们通过写脚本代码,进行流程的处理。这个脚本,主要遵循jenkinsfile的语法,所以,只要是jenkinsfile可以运行的脚本,在这个文本编辑器里就可以执行,但是如果我们是使用的自己安装的Jenkins的话,也支持yml文件来编写Jenkins脚本,由于我们用的是coding的云服务器,默认只支持jenkinsfile。
由于个人实在无法看懂图形编辑器生成的代码,所以最终采用了写脚本的方式来处理,这样自己掌握脚本的过程,便于控制和修改。
这里我们着重讲一下一些关键的脚本语法
首先Jenkinsfile的本质,就是一个流水线的脚本,也就是pipeline,pipeline就类似于我们任何一个程序的main函数一样,是一个入口。
整个pipeline里面的语法:
pipeline {
agent any
environment {
VAR1 = '123'
}
stages {
stage('检出') {
steps {
// 写相关的脚本
}
}
stage('编译') {
steps {
// 写相关的脚本
}
}
}
}
从上面可以看出来,pipeline是整个流程的入口,也叫做一个流水线,在一个流水线里面,可以有主机(agent)、环境变量(environment)、阶段集(stages)、阶段(stage)、步骤(steps)
主机(agent):表示我们用的云主机是哪一个节点,一般来说,any即可,即任意一台主机就行
环境变量(environment):我们可以自定义一些环境变量,用于在脚本中使用,环境变量的使用根据放置的位置来决定其生命周期,coding中的环境变量的优先级从高到低,分别为:
-
Jenkinsfile 中的 withEnv
-
Jenkinsfile 中的 environment
-
构建计划(Job)中的启动参数
-
构建计划(Job)设置中的环境变量
-
构建过程中系统内置的环境变量
详情参见:https://help.coding.net/docs/ci/configuration/env.html
阶段集(stages):阶段集就像我们的List一样,是一个集合或者数组,里面存放每个阶段对象
阶段(stage):阶段就是我们定义的一个阶段对象,在该阶段内,我们主要处理哪些任务或者步骤,属于一个业务分类
步骤(steps):步骤就是我们具体要做的事情,第一步、第二步、第三步。。。
步骤中主要编写相关的脚本:
支持的脚本有:
Linux shell脚本,用法:sh "" 、sh ''、sh """ """,无论单引号、双引号还是三引号,里面都写相关的Linux命令
Groovy语法:Groovy是一种基于JVM(Java虚拟机)的敏捷开发语言,它结合了Python、Ruby和Smalltalk的许多强大的特性,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码。由于其运行在 JVM 上的特性,Groovy也可以使用其他非Java语言编写的库。
Jenkins:Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件项目可以进行持续集成。
所以说,所以整个groovy、Jenkins都是Java大家庭的东西,为了Java而存在的,所以Jenkinsfile支持groovy的语法,也是很正常的了。
groovy语法的使用,我们可以写在script中,示例如下
script {
if(!fileExists("${env.WORKSPACE}/tool/")){
sh "mkdir tool"
}
下面给大家介绍在Jenkinsfile中我用到的几个功能语法
git代码拉取:
dir()表示在某目录下执行,类似于Linux的cd命令
dir ("${env.WORKSPACE}/xxxxPath"){
git branch:'master',
url:'https://e.coding.net/xxxx/xxxx.git',
credentialsId:"这个ID是在开发者管理里面录入凭据以后生成的一个ID,他代替了我们的git账号的用户名密码,更加安全"
}
判断文件是否存在:
${env.WORKSPACE}表示系统环境变量,即当前工作区
if(!fileExists("${env.WORKSPACE}/tool/")){
sh "mkdir tool"
}
dotnet sdk的环境搭建:
由于默认的云主机上是没有dotnet环境的,或者coding的云主机上提供的dotnet是2.2的sdk,不满足于我们的需求,所以我们在首次通过wget从微软官网下载下来dotnet5的sdk的tar.gz包,然后在云主机上进行解压,并指定好路径,解压之后,即可以使用dotnetsdk进行程序的编译
script{
if(!fileExists("${env.WORKSPACE}/tool/dotnet5/")){
echo("${env.WORKSPACE}/tool/dotnet5/ not exists")
if(!fileExists("${env.WORKSPACE}/tool/dotnet-sdk-5.0.404-linux-x64.tar.gz")){
echo("${env.WORKSPACE}/tool/dotnet5/dotnet-sdk-5.0.404-linux-x64.tar.gz not exists")
sh """
dotnet --version
dotnet --list-sdks
cat /proc/version
mkdir dotnet5
pwd
ls
wget https://download.visualstudio.microsoft.com/download/pr/2c1eb8c8-ac05-4dc7-9bef-307b3e450e9d/75e85b3d1662f60afd69572fd5df6884/dotnet-sdk-5.0.404-linux-x64.tar.gz
"""
}
sh "tar -zxf dotnet-sdk-5.0.404-linux-x64.tar.gz -C dotnet5"
}
}
dotnet编译程序
${env.WORKSPACE}/tool/dotnet5/dotnet build -p:Version=1.0.0.123 ./xxxx.csproj
git代码提交
dir ("${env.WORKSPACE}/tm-lw6-bin/svr"){
withCredentials([usernamePassword(credentialsId: "${CREDENTIALSID}",
passwordVariable: 'GIT_PASSWORD',
usernameVariable: 'GIT_USERNAME')]) {
// 配置 Git 工具中仓库认证的用户名、密码
sh 'git config --local credential.helper "!p() { echo username=\\$GIT_USERNAME; echo password=\\$GIT_PASSWORD; }; p"'
// 配置 git 变量 user.name 和 user.emai
sh "git config --global user.name ${USER_NAME}"
sh "git config --global user.email ${USER_EMAIL}"
// 将文件推送到 git 远程仓库
sh """
git add -A .
git commit -m "这里写提交注释"
git push -u origin master
"""
}
}
至此Jenkinsfile的部分,基本就这些,剩下的根据实际需求,查询相关的语法进行编写即可
触发规则
触发规则,即表示在什么情况下触发我们的构建计划进行执行,最简单的就是手动构建,我们通过点击立即构建进行,当然,在触发规则里提供了更丰富的构建规则
如果可以看到,我们可以选择某一个分支被推到后触发、标签推送触发、主分支推送触发、自动填写规则、合并请求符合相关规则时触发,以及定时触发等,这个我们按需设置即可
变量与缓存
变量与缓存,用于设置相应的环境变量,同时也可以设置缓存目录
这里我们缓存了dotnet sdk的安装路径,这样下次构建的时候,无需重新下载dotnet sdk,可以直接使用,提高了构建的效率
通知提醒
通知提醒里面可以设置我们构建成功或者失败的时候,设置相应的人员进行通知。coding这里支持的比较局限吧,我看到其他的项目中有人的构建还进行钉钉消息的通知推送等。