devops学习笔记-jenkins pipeline流水线发布

时间:2022-10-08 18:18:45

jenkins pipeline

介绍

要实现CD,先要实现CI。CD Pipeline就是一个代码文件,里面把你项目业务场景都通过Groovy代码和Pipeline语法实现,一个一个业务串联起来,全部实现自动化,从代码仓库到生产环境完成部署的自动化流水线。这个过程就是一个典型的CD Pipeline。官网建议我们把Pipeline代码放在一个名称为Jenkinsfile的文本文件中,并且把这个文件放在项目代码的根目录,采用版本管理工具管理。

一个Jenkinsfile或者一个Pipeline代码文件,我们可以使用两个脚本模式去写代码,这两种分类叫:Declarative Pipeline 和 Scripted Pipeline.

​Declarative相对于Scripted有两个优点。

1)、第一个是提供更丰富的语法功能;

2)、第二个是写出来的脚本可读性和维护性更好;​

jenkins是一个非常著名的CI服务器平台,支持很多不同第三方(插件的形式)集成自动化测试。Jenkins UI 配置已经满足不了这么复杂的自动化需求,加入Pipeline功能之后,Jenkins 表现更强大。

​Pipeline主要有一下特点

  • Code代码:Pipeline是用代码去实现,并且支持check in到代码仓库,这样项目团队人员就可以修改,更新Pipeline脚本代码,支持代码迭代。
  • Durable耐用:Pipeline支持在Jenkins master(主节点)上计划之内或计划外的重启下也能使用。
  • Pausable可暂停:Pipeline支持可选的停止和恢复或者等待批准之后再跑Pipeline代码。
  • Versatile丰富功能:Pipeline支持复杂和实时的CD需求,包括循环,拉取代码,和并行执行的能力。
  • Extensible可扩展性:Pipeline支持DSL的自定义插件扩展和支持和其他插件的集成。

jenkins pipline流程图

devops学习笔记-jenkins pipeline流水线发布

​第1步.开发(IDE)提交代码到项目仓库服务器(gitlab);

第2步.jenkins开始执行Pipeline代码文件,开始从(gitlab)仓库git clone代码;

第3步.jenkins启动Pipeline里面第一个stage(阶段);

第4步.图里面第一个Stage从仓库检出(Checkout)代码;

第5步.接着进入第二Stage构建(Build)检出的代码;

第6步.然后进入测试(Test)的阶段,执行各种自动化测试验证;

第7步.然后测试结束,到运维的部署(Deploy)阶段;

第8步.部署结束,输出报告,整个自动化流程工作完成;

等待触发构建,开始重复下一轮1到8步骤。

jenkins pipline概念

  • 流水线pipline

流水线是用户定义的一个CD流水线模型 。流水线的代码定义了整个的构建过程, 他通常包括构建, 测试和交付应用程序的阶段 。

  • 节点(node)

节点是一个机器,它是jenkins环境的一部分。

  • 阶段(stage)

stage 块定义了在整个流水线的执行任务的概念性地不同的的子集(比如 "Build", "Test" 和 "Deploy" 阶段), 它被许多插件用于可视化 或Jenkins流水线目前的 状态/进展.

  • 步骤(step)

step为一个单一任务,它告诉jenkins在待定的时间点要做什么

pipline流水线语句说明

在声明式流水线语法中, pipeline 块定义了整个流水线中完成的所有的工作。

Jenkinsfile (Declarative Pipeline)
pipeline {
agent any //在任何可用的代理上,执行流水线或者它的任何阶段
stages {
stage('Build') { //定义Build阶段
steps {
// 执行Build阶段的相关步骤
}
}
stage('Test') { //定义Test阶段
steps {
// 执行Test阶段的相关步骤
}
}
stage('Deploy') { //定义Deploy阶段
steps {
// 执行deploy阶段的相关步骤
}
}
}
}


关于流水线的具体语法,参考官方手册​​https://www.jenkins.io/zh/doc/book/pipeline/syntax/​

jenkins上使用pipeline

在我之前的jenkins上,使用pipeline方式重新建立一个ITEM。

流程如下:

devops学习笔记-jenkins pipeline流水线发布

1 拉取gitlab仓库代码

2 通过mvn构建项目

3 通过sonarqube做代码质量检测,本次实验暂时不涉及。

4 通过docker制作自定义镜像

5 将自定义镜像推送到harbor仓库,此时需要编写一个脚本,来判断镜像容器是否存在

准备jenkinsfile

在gitlab上,新增一个Jenkinsfile,注意大小写,不然jenkins识别不到。也可以通过IDE的VCS版本控制将编写好的Jenkinsfile推送到gitlab中。

devops学习笔记-jenkins pipeline流水线发布


devops学习笔记-jenkins pipeline流水线发布

Jenkinsfile准备好的文件内容如下,当前指明步骤,没有填充步骤的详细内容。pipline脚本的语法基本上跟groovy一致。

//所有的脚本命令都放在pipeline中
pipeline {
//指定任务在哪个集群节点中执行
agent any

//申明全局变量,后期使用
environment {
key ='value'
}
stages {
stage('拉取git仓库代码') {
steps {
echo '拉取git仓库代码-SUCCESS'
}
}
stage('通过maven构建项目') {
steps {
echo '通过maven构建项目-SUCCESS'
}
}
stage('通过SonarQube做代码质量检测') {
steps {
echo '通过SonarQube做代码质量检测-SUCCESS'
}
}
stage('通过docker制作自定义镜像') {
steps {
echo '通过docker制作自定义镜像-SUCCESS'
}
}
stage('将自定义镜像推送到Harbor') {
steps {
echo '将自定义镜像推送到Harbor-SUCCESS'
}
}
stage('通过Publish Over SSH 通知目标服务器') {
steps {
echo '通过Publish Over SSH 通知目标服务器-SUCCESS'
}
}
}

}

完成简单Jenkinsfile编写后,在gitlab上保存即可。

jenkins上新建pipeline

在jenkins上新建一个ITEM,名称为mytest3。

devops学习笔记-jenkins pipeline流水线发布

job类型选择Pipeline

devops学习笔记-jenkins pipeline流水线发布

配置参数

勾选"This project is parameterized",添加git参数,如下所示:

devops学习笔记-jenkins pipeline流水线发布

名称为tag,这个参数将作为jenkins的全局参数使用。参数类型为表标签,代表从gitlab使用不同的标签拉取代码。默认值为origin/master,代表默认从gitlab的master分支拉取代码。

配置流水线

找到pipeline选项卡,按照如下填写:

devops学习笔记-jenkins pipeline流水线发布

说明:

1.推荐使用 Pipeline script from SCM,代表从gitlab上读取jenkinsfile;

2.需要填写正确的gitlab仓库地址和账号密码,同时要确保gitlab上仓库中的根目录要存在Jenkinsfile

3.指定在gitlab仓库中jenkinsfile的目录路径和文件名称。

点击应用保存,完成流水线配置。

测试流水线

因为的我们的jenkinsfile中虽然写明了构建步骤,但是没有写构建步骤的内容与动作,我们可以先测试下目前流水线是否能够正常构建。

devops学习笔记-jenkins pipeline流水线发布

构建完成后,我们等清楚的看到每个流水线步骤执行结果,花费时间等,看能看到每个步骤的日志。

devops学习笔记-jenkins pipeline流水线发布

注意,流水线步骤是串行顺序执行,如果前一个步骤执行报错失败,那么后面的步骤将不会执行。

配置流水线步骤

完成流水线的基本结构的jenkinsfile编写和测试后,需要按照之前的构建步骤填写jenkinsfile中步骤。

由于我们的执行步骤都是在jenkins上或者目标服务器上执行脚本或者使用插件动作,所以我们需要使用jenkins的流水线语法工具,将我们定义的步骤转换为jenkins的流水线步骤语法。

devops学习笔记-jenkins pipeline流水线发布

devops学习笔记-jenkins pipeline流水线发布

定义全局变量

使用environment定义全局变量,这些变量可以给后面的pipeline脚本的调用。

    //申明全局变量,后期使用
environment {
user_name='user' //harbor仓库用户名
harbor_password='Harbor12345' //harbor仓库密码
harbor_addr='192.168.85.3:8090' //harbor仓库地址
harbor_repo='test' // harbor仓库名称
container_port='8080' //容器暴露端口
host_port='8092' //宿主机暴露端口
}

拉取git代码

在示例步骤中选择"checkout:Check out from version control",表示jenkins从gitlab拉取代码。

devops学习笔记-jenkins pipeline流水线发布

填写完成后,点击生成流水线脚本。将生成的流水线脚本,添加补充到我们的jenkinsfile对应的步骤中。

stage('拉取git仓库代码') {
steps {
checkout([$class: 'GitSCM', branches: [[name: '${tag}']], extensions: [], userRemoteConfigs: [[credentialsId: 'fc7dffd0-2911-45da-ba46-7a9f7e00a41a', url: 'http://192.168.85.6/root/mytest.git']]]) //此处为流水线脚本生成
echo '拉取git仓库代码-SUCCESS'
}
}

通过maven构建项目

同样使用流水线语法生成脚本,不过这次使用”sh:Shell Script"方式,表示在jenkins上使用maven构建打包并生成的jar包。

/usr/local/apache-maven-3.8.6/bin/mvn clean package -DskipTest #mvn打包命令

devops学习笔记-jenkins pipeline流水线发布

将生成的流水线语法添加到jenkinsfile中。

stage('通过maven构建项目') {
steps {
sh '/usr/local/apache-maven-3.8.6/bin/mvn clean package -DskipTest'
echo '通过maven构建项目-SUCCESS'
}

通过SonarQube代码检测

本次流水线配置不涉及此处(我没想好。。。。)

通过docker制作自定义镜像

在jenkins上,将maven构建jar包移动到docker目录下,然后使用docker命令构建镜像。

mv target/*.jar ./docker/   #将jenkins中target目录生成的jar包移动到当前docker目录下
docker build -t ${JOB_NAME}:${tag} ./docker/ #docker构建镜像,${JOB_NAME}为jenkins
#全局变量,本次为mytest3
# ${tag}为jenkins自定义git参数,读取gitlab
#仓库中的tag

使用流水线语法生成脚本

devops学习笔记-jenkins pipeline流水线发布

将生成的流水线脚本添加到jenkinsfile中

    stage('通过docker制作自定义镜像') {
steps {
sh '''mv target/*.jar ./docker/
docker build -t ${JOB_NAME}:${tag} ./docker/'''
echo '通过docker制作自定义镜像-SUCCESS'
}
}

将自定义镜像推送的到Harbor

采用shell脚本的方式,登录harbor仓库,本地打上镜像标签,将制作的docker镜像推送到harbor仓库

docker login -u ${user_name} -p ${harbor_password} ${harbor_addr}   #变量为前面enivronment定义的全局变量
docker tag ${JOB_NAME}:${tag} ${harbor_addr}/${harbor_repo}/${JOB_NAME}:${tag} #${JOB_NAME}为jenkins内置的全局变量
docker push ${harbor_addr}/${harbor_repo}/${JOB_NAME}:${tag} #${tag}为自定义git参数的变量

使用流水线语法生成脚本

devops学习笔记-jenkins pipeline流水线发布

将生成的脚本添加到 jenkinsfile中。

    stage('将自定义镜像推送到Harbor') {
steps {
sh '''docker login -u ${user_name} -p ${harbor_password} ${harbor_addr}
docker tag ${JOB_NAME}:${tag} ${harbor_addr}/${harbor_repo}/${JOB_NAME}:${tag}
docker push ${harbor_addr}/${harbor_repo}/${JOB_NAME}:${tag}'''
echo '将自定义镜像推送到Harbor-SUCCESS'
}
}


通过publish over ssh通知目标服务器

需要通过jenkins的publish over ssh插件,通知目标服务器从harbor行拉取镜像运行容器。拉取镜像和运行容器,要满足以下逻辑:

  • 告知目标服务器拉取哪个镜像
  • 判断当前服务器是否正在运行容器,有的话需要删除
  • 如果目标服务器已经存在当前镜像,有的话需要删除
  • 目标服务器拉取harbor上的镜像
  • 将拉取下来的镜像运行成容器

编写shell脚本,并将脚本传输到目标服务器上,由于是实验环境,目标服务器和jenkins其实就是一台机器,所以不用将脚本文件传送。

vim deploy.sh

#!/usr/bin/bash
#script_name:deploy.sh
#部署脚本
#1.告知目标服务器拉取哪个镜像
#2.判断当前服务器是否正在运行容器,需要删除
#3.如果目标服务器已经存在当前镜像,需要删除
#4.目标服务器拉取harbor上的镜像
#5.将拉取下来的惊醒运行成容器
#定义部署信息参数

harbor_addr=$1
harbor_repo=$2
project=$3
version=$4
host_port=$5
container_port=$6

#测试部署的信息
ImageName=${harbor_addr}/${harbor_repo}/${project}:${version}
echo ${ImageName}

#判断当前服务器上是否存在docker镜像,存在则停止并删除容器
ContainerId=$(docker ps -a | grep ${project} | awk '{print $1}')
echo ${ContainerId}
ImageId=$(docker images | grep ${harbor_addr}/${harbor_repo}/${project} | awk '{print $3}')

if [ "${ContainerId}" != "" ];then
docker stop ${ContainerId}
docker rm ${ContainerId}
fi


#Image=$( docker images | grep ${harbor_addr}/${harbor_repo}/${project} | awk '{print $1":"$2}')
Tag=$( docker images | grep ${project} | awk '{print $2}')

echo ${Tag}

#判断版本,如果存在当前版本,则删除该版本的镜像
if [[ "${Tag}" =~ "${version}" ]] ;then #判断是否包含版本
docker rmi -f ${ImageId} #如果存在当前版本,则删除镜像
fi

docker login -u user -p Harbor12345 ${harbor_addr} #登录harbor仓库
docker pull ${ImageName} #拉取镜像

#运行镜像
docker run -d -p ${host_port}:${container_port} --name ${project} ${ImageName}

echo "SUCESS!!"

在本地测试脚本,保证脚本运行没有问题。

devops学习笔记-jenkins pipeline流水线发布

在流水线语法中,使用”sshPublisher:Send build artifacts over SSH",生成脚本

cd /root/jenkins_shell/ && sh -x deploy.sh ${harbor_addr} ${harbor_repo} ${JOB_NAME} ${tag} ${host_port} ${container_port} #执行脚本,脚本参数使用前面environment定义的全局变量

devops学习笔记-jenkins pipeline流水线发布

以下为生成的脚本:

devops学习笔记-jenkins pipeline流水线发布

注意要将生成的脚本中的'''符号改为“, 不然脚本无法通过jenkins去执行

sshPublisher(publishers: [sshPublisherDesc(configName: 'localhost', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: ”cd /root/jenkins_shell/ && sh -x deploy.sh ${harbor_addr} ${harbor_repo} ${JOB_NAME} ${tag} ${host_port} ${container_port}“, execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])

将生成的脚本添加到jenkinsfile中。

    stage('通过Publish Over SSH 通知目标服务器') {
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'localhost', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "cd /root/jenkins_shell/ && sh -x deploy.sh ${harbor_addr} ${harbor_repo} ${JOB_NAME} ${tag} ${host_port} ${container_port}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
echo '通过Publish Over SSH 通知目标服务器-SUCCESS'
}
}

最终的jenkinsfile

完成了以上的步骤后,最终的jenkinsfile内容如下:

//所有的脚本命令都放在pipeline中
pipeline {
//指定任务在哪个集群节点中执行
agent any

//申明全局变量,后期使用
environment {
user_name='user'
harbor_password='Harbor12345'
harbor_addr='192.168.85.3:8090'
harbor_repo='test'
container_port='8080'
host_port='8092'
}
stages {
stage('拉取git仓库代码') {
steps {
checkout([$class: 'GitSCM', branches: [[name: '${tag}']], extensions: [], userRemoteConfigs: [[credentialsId: 'fc7dffd0-2911-45da-ba46-7a9f7e00a41a', url: 'http://192.168.85.6/root/mytest.git']]])
echo '拉取git仓库代码-SUCCESS'
}
}
stage('通过maven构建项目') {
steps {
sh '/usr/local/apache-maven-3.8.6/bin/mvn clean package -DskipTest'
echo '通过maven构建项目-SUCCESS'
}
}
stage('通过SonarQube做代码质量检测') {
steps {
//不好弄
echo '通过SonarQube做代码质量检测-SUCCESS'
}
}
stage('通过docker制作自定义镜像') {
steps {
sh '''mv target/*.jar ./docker/
docker build -t ${JOB_NAME}:${tag} ./docker/'''
echo '通过docker制作自定义镜像-SUCCESS'
}
}
stage('将自定义镜像推送到Harbor') {
steps {
sh '''docker login -u ${user_name} -p ${harbor_password} ${harbor_addr}
docker tag ${JOB_NAME}:${tag} ${harbor_addr}/${harbor_repo}/${JOB_NAME}:${tag}
docker push ${harbor_addr}/${harbor_repo}/${JOB_NAME}:${tag}'''
echo '将自定义镜像推送到Harbor-SUCCESS'
}
}
stage('通过Publish Over SSH 通知目标服务器') {
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'localhost', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "cd /root/jenkins_shell/ && sh -x deploy.sh ${harbor_addr} ${harbor_repo} ${JOB_NAME} ${tag} ${host_port} ${container_port}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
echo '通过Publish Over SSH 通知目标服务器-SUCCESS'
}
}
}

}

在gitlab上编写jenkinsfile的时候,有时候它会自动换行,添加不必要的换行符\n,从而导致jenkins读取报错。可以尝试用以下方式避免。

devops学习笔记-jenkins pipeline流水线发布

运行pipeline

完成jenkinsfile的编写后,在jenkins运行构建。如下所示:

devops学习笔记-jenkins pipeline流水线发布

devops学习笔记-jenkins pipeline流水线发布

构建过程

devops学习笔记-jenkins pipeline流水线发布

构建输出日志

devops学习笔记-jenkins pipeline流水线发布

流水线构建成功

devops学习笔记-jenkins pipeline流水线发布

harbor上检查效果

检查harbor上是否完成上传了镜像

devops学习笔记-jenkins pipeline流水线发布

devops学习笔记-jenkins pipeline流水线发布

devops学习笔记-jenkins pipeline流水线发布

确认通过流水线,jenkins生成镜像并上传到harbor

目标服务器上检查效果

devops学习笔记-jenkins pipeline流水线发布

devops学习笔记-jenkins pipeline流水线发布

目标服务器上确认了从docker拉取了镜像,并生成了容器运行。

访问业务

devops学习笔记-jenkins pipeline流水线发布

确认容器业务征程运行。


总结

1 pipeline流水线语法有一定的学习曲线,需要不停参考官方指导才能指导。

2 官方提供了pipeline构建流程参考示例,需要耐心研究。

3 实现生产上jenkins,sonarqube,harbor等最好不要用容器化部署,不太容易与jenkins集成,本次没有配置sonarqube代码检测,也是这个原因