在之前的环境中我们实现了基本的持续集成和持续部署(CI/CD) 但整个实现的过程是非常繁琐的,经常容易忘记怎么操作,而且当报错的时候排查很艰难 在jenkins中提供了一套流水线作业pipeline 来简化这些流程
- 项目名称: pipeline
-
- 项目风格: 流水线
我们在流水线项目中点击右侧的hello World会自动生成流水线代码片段
当我们手动构建后可以在屏幕中间的任务列表中看到我们项目构建所消耗的时间、是否成功、日志信息等
我们上面生成的hello world的语句片段我们拿过来用一下
- pipeline { // 所有的脚本信息都是放在pipeline中的
- agent any //构建时任务交给具体那个jenkins节点,(集群模式下可选node)
-
-
- environment { //演示代码里没有,这里加一下
- key = 'value' //全局变量配置,自定义
- }
-
- stages { //任务组,所有要跑的任务都放在这个组下
-
- stage('Hello') { //第一个任务,任务构建的顺序是从上到下的,可以定义任务名称
- steps { //第一个任务的具体操作
- echo 'Hello World' //具体的shell命令
- }
- }
- }
- }
也就是说,我们要添加一个子任务就需要在stages下添加一组stage的配置
我们在这里先将大致框架定出来,设置每一步大概要做什么,具体内容先用输出代替
- pipeline {
-
- agent any
-
- environment {
- key = 'value'
- }
-
- stages {
-
- stage('1、 拉取gitlab上的代码到jenkins主机 ') {
- steps() {
- echo '拉取git仓库代码 - SUCCESS'
- }
- }
- stage('2、通过jenkins本机的maven+jdk来实现编译打包') {
- steps() {
- echo '通过maven构建项目 - SUCCESS'
- }
- }
- stage('3、通过SonarQube做代码质量检测') {
- steps() {
- echo '通过SonarQube做代码质量检测 - SUCCESS'
- }
- }
- stage('4、通过jenkins主机构建docker镜像') {
- steps() {
- echo '通过jenkins主机构建docker镜像 - SUCCESS'
- }
- }
- stage('5、发送Chart包到K8master主机') {
- steps() {
- echo '发送Chart包到K8master主机 - SUCCESS'
- }
- }
- stage('6、通过Publish Over SSH通知master 部署/更新helm - SUCCESS') {
- steps() {
- echo '通过Publish Over SSH通知master 部署/更新helm - SUCCESS'
- }
- }
- }
- }
把上面的流水线配置贴到 pipeline中测试一下,我们能看到就有了很多任务
这里我们没有像之前那种图形化配置,不过pipeline提供了生成流水线的配置
- //点击流水线案例,找到如下配置
- checkout Check out from version control
-
-
- //git地址
- http://101.43.4.210:30001/root/mytest.git
生成的语句
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[url: 'http://101.43.4.210:30001/root/mytest.git']]])
添加到我们的语法中
- pipeline {
-
- agent any
-
- environment {
- key = 'value'
- }
-
- stages {
-
- stage('1、 拉取gitlab上的代码到jenkins主机 ') {
- steps() {
- echo '拉取git仓库代码 - SUCCESS'
- checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[url: 'http://101.43.4.210:30001/root/mytest.git']]])
- }
- }
- stage('2、通过jenkins本机的maven+jdk来实现编译打包') {
- steps() {
- echo '通过maven构建项目 - SUCCESS'
- }
- }
- stage('3、通过SonarQube做代码质量检测') {
- steps() {
- echo '通过SonarQube做代码质量检测 - SUCCESS'
- }
- }
- stage('4、通过jenkins主机构建docker镜像') {
- steps() {
- echo '通过jenkins主机构建docker镜像 - SUCCESS'
- }
- }
- stage('5、发送Chart包到K8master主机') {
- steps() {
- echo '发送Chart包到K8master主机 - SUCCESS'
- }
- }
- stage('6、通过Publish Over SSH通知master 部署/更新helm - SUCCESS') {
- steps() {
- echo '通过Publish Over SSH通知master 部署/更新helm - SUCCESS'
- }
- }
- }
- }
测试构建
- //这里我们新建的流水线项目pipeline的目录位置,构建完就可以看到拉取的代码了
- cd /apps/devops_setup/data/jenkins/data/workspace/pipeline/
我们每次都去项目中配置修改很麻烦,流水线提供了一种文件形式的定义方式
我们在gitlab代码中定义一个jenkinsfile的文件,在构建的时候他会去拉取这个文件运行,打开idea
我这里当时搞的时候整错了,idea添加时写的jenkinsfile首字母是小写,这里在配置拉取jenkinsfile的时候最下面的关键字也要改成小写
如上,可知这个jenkinsfile文件名称不固定,根据自己的使用定义
构建完的操作和之前在项目中定义的是完全一样的
在我们的pipeline项目中定义一个全局字符tag ,让我们项目可以通过tag去构建,下图中我们设置的默认值意思是,当我们不选择构建的tag版本时直接点构建会去拉取master分支的代码
我们在上面构建默认是走的master分支,但这使得我们发布特定版本的时候很不方便
这里我们稍作修改,将master分支改成我们在构建时gitlab传过来的分支信息
- steps() {
- echo '拉取git仓库代码 - SUCCESS'
- checkout([$class: 'GitSCM', branches: [[name: '$tag']], extensions: [], userRemoteConfigs: [[url: 'http://101.43.4.210:30001/root/mytest.git']]])
- }
-
-
- //这里将*/master 修改为$tag 表示以gitlab传过来的标签作为分支切换
注意,使用jenkinsfile内部调用全局变量是${xxx},而在jenkins项目中调用jenkinsfile的变量是$xxx
此时jenkinsfile是去拿master分支的配置,克隆的应该是V4版本
构建测试
根据上图显示我们也可以指定不同的版本来跑构建了,接下来继续完善这个流程
直接在流水线语法那里指定为shell语法后,将编译的语法转换为流水线语法即可
- //脚本语法
- sh: shell script
-
-
- //编译的命令,绝对路径
- /var/jenkins_home/maven/bin/mvn clean package -DskipTests
返回流水线语法
sh '/var/jenkins_home/maven/bin/mvn clean package -DskipTests'
这里加一段就全量跑一下太麻烦了,我们把剩下的几段都找出来再添加
这个和上面的操作方法一致,这里就不放图了
- //脚本语法
- sh: shell script
-
- //代码质检命令
- /var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsoanr.sources=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=./target -Dsonar.login=squ_3005d10215abc1ac559457cc90ba804c6c477c0c
-
- //这里是直接shell跑的需要带上token值,最后一段那个
- //如果不记得你自己的token了,去sonarQube上 (我的账户--安全--生成token-用户令牌)获取
返回
sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsoanr.sources=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=./target -Dsonar.login=squ_3005d10215abc1ac559457cc90ba804c6c477c0c'
我们在设置镜像的时候,不希望写死镜像仓库地址,根据测试或功能环境不同需要重新指定仓库,这里做全局变量
通过jenkinsfile添加
- //添加全局变量
- environment {
- harborUser = 'admin'
- harborPasswd = 'Harbor12345'
- harborAddress = '101.43.4.210:30007'
- harborRepo = 'repo'
- }
指定镜像构建和上传到镜像仓库的部分
通过流水线语法添加
- //脚本语法
- sh: shell script
-
- //镜像上传命令
- mv ./target/*.jar docker/
- docker build -t "${JOB_NAME}:${tag}" ./docker/
- docker login ${harborAddress} -u${harborUser} -p${harborPasswd}
- docker tag ${JOB_NAME}:${tag} ${harborAddress}/${harborRepo}/${JOB_NAME}:${tag}
- docker push ${harborAddress}/${harborRepo}/${JOB_NAME}:${tag}
返回值
- sh '''mv ./target/*.jar docker/
- docker build -t "${JOB_NAME}:${tag}" ./docker/
- docker login ${harborAddress} -u${harborUser} -p${harborPasswd}
- docker tag ${JOB_NAME}:${tag} ${harborAddress}/${harborRepo}/${JOB_NAME}:${tag}
- docker push ${harborAddress}/${harborRepo}/${JOB_NAME}:${tag}'''
- //生成方法
- sshPublisher: Send build artifacts over SSH
-
-
- //要拷贝的源文件
- helm/mytest/* helm/mytest/templates/*
- //要执行的命令
- helm upgrade -i ${JOB_NAME} --set containers.image=$harborAddress/$harborRepo/${JOB_NAME}:${tag} --set name=${JOB_NAME} /usr/local/test/helm/mytest/
返回
sshPublisher(publishers: [sshPublisherDesc(configName: 'test', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'helm upgrade -i ${JOB_NAME} --set containers.image=$harborAddress/$harborRepo/${JOB_NAME}:${tag} /usr/local/test/helm/mytest/', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'helm/mytest/* helm/mytest/templates/* ')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
这里要注意一个问题
- execCommand: 'helm upgrade -i ${JOB_NAME} --set containers.image=$harborAddress/$harborRepo/${JOB_NAME}:${tag} /usr/local/test/helm/mytest/'
-
-
- //命令中execCommand: 后面的命令是 单引号 ''
- //这样会使得我们的$harborAddress/$harborRepo 这两个变量不生效
- //但是这个变量是取自jenkinsfile的我们无法通过${}的方式获取
- //所以这里的单引号要改成双引号
修改后的代码如下
sshPublisher(publishers: [sshPublisherDesc(configName: 'test', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "helm upgrade -i ${JOB_NAME} --set containers.image=$harborAddress/$harborRepo/${JOB_NAME}:${tag} /usr/local/test/helm/mytest/", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'helm/mytest/* helm/mytest/templates/* ')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
这样一来我们就把之前的项目中所有步骤的流水线代码都拿到了,下面开始整理一下
- pipeline {
-
- agent any
-
- environment {
- harborUser = 'admin'
- harborPasswd = 'Harbor12345'
- harborAddress = '101.43.4.210:30007'
- harborRepo = 'repo'
- }
- stages {
-
- stage('1、 拉取gitlab上的代码到jenkins主机 ') {
- steps() {
- echo '拉取git仓库代码 - SUCCESS'
- checkout([$class: 'GitSCM', branches: [[name: '$tag']], extensions: [], userRemoteConfigs: [[url: 'http://101.43.4.210:30001/root/mytest.git']]])
- }
- }
- stage('2、通过jenkins本机的maven+jdk来实现编译打包') {
- steps() {
- echo '通过maven构建项目 - SUCCESS'
- sh '/var/jenkins_home/maven/bin/mvn clean package -DskipTests'
- }
- }
- stage('3、通过SonarQube做代码质量检测') {
- steps() {
- echo '通过SonarQube做代码质量检测 - SUCCESS'
- sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsoanr.sources=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=./target -Dsonar.login=squ_3005d10215abc1ac559457cc90ba804c6c477c0c' }
- }
- stage('4、通过jenkins主机构建docker镜像') {
- steps() {
- echo '通过jenkins主机构建docker镜像 - SUCCESS'
- sh '''mv ./target/*.jar docker/
- docker build -t "${JOB_NAME}:${tag}" ./docker/
- docker login ${harborAddress} -u${harborUser} -p${harborPasswd}
- docker tag ${JOB_NAME}:${tag} ${harborAddress}/${harborRepo}/${JOB_NAME}:${tag}
- docker push ${harborAddress}/${harborRepo}/${JOB_NAME}:${tag}'''
-
- }
- }
- stage('5、发送Chart包到K8master主机 通过helm部署, 这里多写了个第6步已经删了') {
- steps() {
- echo '发送Chart包到K8master主机 - SUCCESS'
- sshPublisher(publishers: [sshPublisherDesc(configName: 'test', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "helm upgrade -i ${JOB_NAME} --set containers.image=$harborAddress/$harborRepo/${JOB_NAME}:${tag} --set name=${JOB_NAME} /usr/local/test/helm/mytest/", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'helm/mytest/* helm/mytest/templates/* ')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
- }
- }
- }
- }
这里我们修改一下idea上代码输出的信息为5.0.0版本,然后上传到gitlab上,打一个tag为V5.0.0的版本
修改输出代码并上传
gitlab添加V5.0.0版本标签
这里因为我们之前已经部署了mytest的服务,这里先清理一下,因为job名称不同,他不会替换掉,但是因为30008端口已经被占用,这里会冲突(或者在helm的地方更新下nodePort端口)
helm delete mytest
通过以上步骤实现了通过流水线去完成我们ci/cd的持续集成和部署,现在我们可以选择版本去发布了(≧∇≦)ノ