jenkins流水线
之前采用Jenkins的自由风格构建的项目,每个步骤流程都要通过不同的方式设置,并且构建过程中整体流程是不可见的,无法确认每个流程花费的时间,并且问题不方便定位问题。
Jenkins的Pipeline可以让项目的发布整体流程可视化,明确执行的阶段,可以快速的定位问题。并且整个项目的生命周期可以通过一个Jenkinsfile文件管理,而且Jenkinsfile文件是可以放在项目中维护。
所以Pipeline相对自由风格或者其他的项目风格更容易操作。
一、构建Jenkins流水线任务
1.1、构建任务
1.2、Groovy脚本
Groovy脚本基础语法
// 所有脚本命令包含在pipeline{}中
pipeline {
// 指定任务在哪个节点执行(Jenkins支持分布式),any 代表任意节点
agent any
// 配置全局环境,指定变量名=变量值信息
environment{
key = 'value'
}
// 存放所有任务的合集
stages {
// 单个任务
stage('任务1') {
// 实现任务的具体流程
steps {
echo 'do something'
}
}
// 单个任务
stage('任务2') {
// 实现任务的具体流程
steps {
echo 'do something'
}
}
// ……
}
}
jenkins流水线测试模板
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'
}
}
}
}
在jenkins流水线上执行
查看构建后的状态
1.3、jenkins-pipeline语法自动生成工具
通过流水线语法生成Checkout代码的脚本
流水线语法–>片段生成器
1.3.1、拉取git仓库代码
checkout: check out from version control
将脚本中的 */master更改为${release},release为代码版本/标签
pipeline {
agent any
stages {
stage('拉取Git代码') {
steps {
checkout scmGit(branches: [[name: '${release}']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.12.104:9980/root/mytest.git']])
}
}
}
}
新建参数变量,参考下图
${release}为版本号/gitlab中打的标签,需要自定义变量,参考下图
<---------------------废弃-------------------->
/var/jenkins_home/maven/bin/mvn clean package -DskipTests
新建3个参数,一个git,两个docker容器相关
jenkins引用gitlab中的Jenkinsfile文件
Jenkins — 项目名称 — 流水线 --定义-- SCM --> GIT,配置好之后会自动识别加载识别Jenkinsfile文件的。
<---------------------以上内容废弃-------------------->
1.3.2、通过maven构建代码
使用shell命令,参数:sh: Shell Script
pipeline文件内容如下
/var/jenkins_home/maven/bin/mvn clean package -DskipTests
stage('通过maven构建代码') {
steps {
sh '/var/jenkins_home/maven/bin/mvn clean package -DskipTests'
}
}
1.3.3、代码质量检测
代码质量检测,参数:sh: Shell Script
${JOB_NAME} 变量指项目名称
stage('通过SonarQube做代码质量检测') {
steps {
sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.source=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=./target/ -Dsonar.login=0ab1a777144b459db09cdd0cfc06a300887c54cb'
}
}
1.3.4、自定义镜像
自定义镜像,参数:sh: Shell Script
注意:$release不能加{},引号会导致变量失效mv target/*.jar docker/
docker build -t ${JOB_NAME}:$release docker/
stage('通过docker制作自定义镜像') {
steps {
sh '''mv target/*.jar docker/
docker build -t ${JOB_NAME}:$release docker/'''
}
}
开始构建,选择标签,下图中的标签上同步gitlab中的tag
1.3.5、镜像上传到harbor
上传到harbor,参数:sh: Shell Script
需要自定义环境变量docker login -u ${harborUser} -p ${harborPwd} ${harborUrl}
docker tag ${JOB_NAME}:${release} ${harborUrl}/${harborRepo}/${JOB_NAME}:${release}
docker push ${harborUrl}/${harborRepo}/${JOB_NAME}:${release}
docker image prune -f
pipeline {
// 指定任务在哪个集群节点中执行
agent any
// 声明全局变量,方便后期使用
environment {
harborUser = 'admin'
harborPwd = '123456'
harborRepo = 'repo2'
harborUrl = '192.168.12.104:180'
}
stages {
stage('将自定义镜像推送到harbor上') {
steps {
sh '''docker login -u ${harborUser} -p ${harborPwd} ${harborUrl}
docker tag ${JOB_NAME}:latest ${harborUrl}/${harborRepo}/${JOB_NAME}:latest
docker push ${harborUrl}/${harborRepo}/${JOB_NAME}:latest
docker image prune -f'''
}
}
}
}
1.3.6、通知目标服务器执行
通知目标服务器,Publish Over SSH脚本
deploy.sh脚本已放到目标服务器的全局变量中,脚本内容如下
# 脚本内容
#/bin/bash
harbor_url=$1
harbor_repo=$2
project=$3
version=$4
host_port=$5
container_port=$6
imageName=$harbor_url/$harbor_repo/$project:$version
containerId=`docker ps -a | grep ${project} | awk '{print $1}'`
if [ "$containerId" != "" ] ; then
docker stop $containerId
docker rm $containerId
echo "Delete Container Success"
fi
imageId=`docker images | grep ${project} | awk '{print $3}'`
if [ "$imageId" != "" ] ; then
docker rmi -f $imageId
echo "Delete Image Success"
fi
docker login -u admin -p 123456 $harbor_url
docker pull $imageName
docker run -d -p $host_port:$container_port --name $project $imageName
echo "Start Container Success"
echo $project
执行命令为:
deploy.sh $harborUrl $harborRepo $JOB_NAME $release $host_port $container_port
$harborUrl $harborRepo 为pipeline中配置的全局变量
$JOB_NAME,为项目名称变量
$release,gitlab中定义的版本变量
$host_port $container_port,需要新加2个参数jenkins流水线参数:
sshPublisher: Send build artifacts over SSH
jenkins中新加参数:$host_port $container_port
pipeline内容如下, 其中自定义语法生成的是单引号,要改成双引号。
pipeline {
agent any
environment {
harborUser = 'admin'
harborPwd = '123456'
harborRepo = 'repo2'
harborUrl = '192.168.12.104:180'
}
stages {
stage('通过publish over ssh通知目标服务器') {
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'mydemo-92', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "deploy.sh $harborUrl $harborRepo $JOB_NAME $release $host_port $container_port", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
}
构建
1.4、使用JenkinsFile方式执行流水线
在gitlab中新建Jenkinsfile文件,
Jenkinsfile文件内容如下
pipeline {
// 指定任务在哪个集群节点中执行
agent any
// 声明全局变量,方便后期使用
environment {
harborUser = 'admin'
harborPwd = '123456'
harborRepo = 'repo2'
harborUrl = '192.168.12.104:180'
}
stages {
stage('拉取git仓库代码') {
steps {
checkout scmGit(branches: [[name: '${release}']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.12.104:9980/root/mytest.git']])
}
}
stage('通过maven构建项目') {
steps {
sh '/var/jenkins_home/maven/bin/mvn clean package -DskipTests'
}
}
stage('通过SonarQube做代码质量检测') {
steps {
sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.source=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=./target/ -Dsonar.login=0ab1a777144b459db09cdd0cfc06a300887c54cb'
}
}
stage('通过docker制作自定义镜像') {
steps {
sh '''mv ./target/*.jar ./docker/
docker build -t ${JOB_NAME}:${release} ./docker/'''
}
}
stage('将自定义镜像推送到harbor上') {
steps {
sh '''docker login -u ${harborUser} -p ${harborPwd} ${harborUrl}
docker tag ${JOB_NAME}:${release} ${harborUrl}/${harborRepo}/${JOB_NAME}:${release}
docker push ${harborUrl}/${harborRepo}/${JOB_NAME}:${release}
docker image prune -f'''
}
}
// 此处 execCommand后的命令要跟双引号,但引号会让变量失效,${harborUrl}等要改成 $harborUrl
stage('通过publish over ssh通知目标服务器') {
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'mydemo-92', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "deploy.sh $harborUrl $harborRepo $JOB_NAME $release $host_port $container_port", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
}
引用gitlab中的Jenkinsfile
1.5、告警到钉钉群
安装dingtalk插件
配置钉钉插件
jenkins中项目中添加钉钉机器人告警
pipeline语法配置,新加post请求
pipeline {
// 指定任务在哪个集群节点中执行
agent any
// 声明全局变量,方便后期使用
environment {
harborUser = 'admin'
harborPwd = '123456'
harborRepo = 'repo'
harborUrl = '192.168.12.104:180'
}
stages {
stage('拉取git仓库代码') {
steps {
checkout scmGit(branches: [[name: '${release}']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.12.104:9980/root/mytest.git']])
}
}
stage('通过maven构建项目') {
steps {
sh '/var/jenkins_home/maven/bin/mvn clean package -DskipTests'
}
}
stage('通过SonarQube做代码质量检测') {
steps {
sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.source=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=./target/ -Dsonar.login=0ab1a777144b459db09cdd0cfc06a300887c54cb'
}
}
stage('通过docker制作自定义镜像') {
steps {
sh '''mv ./target/*.jar ./docker/ \
docker build -t ${JOB_NAME}:${release} ./docker/'''
}
}
stage('将自定义镜像推送到harbor上') {
steps {
sh '''docker login -u ${harborUser} -p ${harborPwd} ${harborUrl}
docker tag ${JOB_NAME}:${release} ${harborUrl}/${harborRepo}/${JOB_NAME}:${release}
docker push ${harborUrl}/${harborRepo}/${JOB_NAME}:${release}
docker image prune -f'''
}
}
stage('通过publish over ssh通知目标服务器') {
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'mydemo-92', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "deploy.sh $harborUrl $harborRepo $JOB_NAME $release $host_port $container_port", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
// 如下post内容为新加,
post {
success {
dingtalk (
robot: 'jenkins_dingtalk', // 系统配置里边的 dingtalk ID
type:'MARKDOWN',
title: "success: ${JOB_NAME}",
text: ["- 成功构建:${JOB_NAME}项目!\n- 版本:${release}\n- 持续时间:${currentBuild.durationString}\n- 任务:#${JOB_NAME}"]
)
}
failure {
dingtalk (
robot: 'jenkins_dingtalk',
type:'MARKDOWN',
title: "fail: ${JOB_NAME}",
text: ["- 失败构建:${JOB_NAME}项目!\n- 版本:${release}\n- 持续时间:${currentBuild.durationString}\n- 任务:#${JOB_NAME}"]
)
}
}
}
二、Jenkins整合kubernetes
2.1、将k8s的yaml文件配置到gitlab中
在gitlab中新建pipeline.yaml,内容如下
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: test
name: pipeline
labels:
app: pipeline
spec:
replicas: 2
selector:
matchLabels:
app: pipeline
template:
metadata:
labels:
app: pipeline
spec:
containers:
- name: pipeline
image: 192.168.12.104:180/repo/pipeline:v3.0.0
imagePullPolicy: Always
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
namespace: test
name: pipeline
labels:
app: pipeline
spec:
selector:
app: pipeline
ports:
- port: 8081
targetPort: 8080
type: NodePort
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: test
name: pipeline
spec:
ingressClassName: ingress
rules:
- host: mashibing.pipeline.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: pipeline
port:
number: 8081
2.2、设置目标服务器
jenkins --> 系统管理 --> 系统配置 --> ssh-server
2.3、在gitlab中修改Jenkinsfile,重新设置流水线任务脚本,并测试结果
修改后的jenkinsfile文件内容如下
1、新增 将yaml文件传送到k8smaster上,脚本语法见下图 sshPublisher: Send build artifacts over SSH
2、新增在k8smaster节点上执行yaml文件,sh: shell scripts
pipeline {
// 指定任务在哪个集群节点中执行
agent any
// 声明全局变量,方便后期使用
environment {
harborUser = 'admin'
harborPwd = '123456'
harborRepo = 'repo'
harborUrl = '192.168.12.104:180'
}
stages {
stage('拉取git仓库代码') {
steps {
checkout scmGit(branches: [[name: '${release}']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.12.104:9980/root/mytest.git']])
}
}
stage('通过maven构建项目') {
steps {
sh '/var/jenkins_home/maven/bin/mvn clean package -DskipTests'
}
}
stage('通过SonarQube做代码质量检测') {
steps {
sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.source=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=./target/ -Dsonar.login=0ab1a777144b459db09cdd0cfc06a300887c54cb'
}
}
stage('通过docker制作自定义镜像') {
steps {
sh '''mv ./target/*.jar ./docker/
docker build -t ${JOB_NAME}:${release} ./docker/'''
}
}
stage('将自定义镜像推送到harbor上') {
steps {
sh '''docker login -u ${harborUser} -p ${harborPwd} ${harborUrl}
docker tag ${JOB_NAME}:${release} ${harborUrl}/${harborRepo}/${JOB_NAME}:${release}
docker push ${harborUrl}/${harborRepo}/${JOB_NAME}:${release}
docker image prune -f'''
}
}
stage('将yaml文件上传到目标服务器上') {
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'k8smaster', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'pipeline.yaml')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
stage('在k8smaster节点上执行yaml文件') {
steps {
sh 'ssh root@192.168.12.120 kubectl apply -f /demo/k8s/pipeline.yaml'
}
}
}
post {
success {
dingtalk (
robot: 'jenkins_dingtalk',
type:'MARKDOWN',
title: "success: ${JOB_NAME}",
text: ["- 成功构建:${JOB_NAME}项目!\n- 版本:${release}\n- 持续时间:${currentBuild.durationString}\n- 任务:#${JOB_NAME}"]
)
}
failure {
dingtalk (
robot: 'jenkins_dingtalk',
type:'MARKDOWN',
title: "fail: ${JOB_NAME}",
text: ["- 失败构建:${JOB_NAME}项目!\n- 版本:${release}\n- 持续时间:${currentBuild.durationString}\n- 任务:#${JOB_NAME}"]
)
}
}
}
2.3、由于命令是中jenkins容器中执行的,所以要配置jenkins可以无密码连接k8smaster
在jenkins内部生成公私钥,然后执行 ssh-copy-id
2.4、查看执行效果。
构建 --> 选择版本
三、CI自动部署
3.1、gitlab中添加webhook地址,实现研发上传代码后自动构建执行部署
首先,启用jenkins的构建触发器,并拷贝webhook地址
webhook地址:http://192.168.12.110:8082/project/pipeline
其次,设置gitlab出站请求
在gitlab中添加jenkins的webhook地址,
测试,初次状态玛为 403,原因是,jenkins拒绝连接,
修改jenkins中的gitlab连接配置,系统管理 --> 系统设置 --> gitlab
gitlab再测试连接,状态码 200
3.2、修改jenkins配置文件,修改内容如下
1、修改所有 ${release} 为 latest,修改版本通过标签获取版本号,为制定版本号latest
2、其中,拉取git仓库代码这块,将${release} 修改为 */main
3、删除 通过publish over ssh通知目标服务器。
pipeline {
// 指定任务在哪个集群节点中执行
agent any
// 声明全局变量,方便后期使用
environment {
harborUser = 'admin'
harborPwd = '123456'
harborRepo = 'repo'
harborUrl = '192.168.12.104:180'
}
stages {
stage('拉取git仓库代码!') {
steps {
checkout scmGit(branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.12.104:9980/root/mytest.git']])
}
}
stage('通过maven构建项目') {
steps {
sh '/var/jenkins_home/maven/bin/mvn clean package -DskipTests'
}
}
stage('通过SonarQube做代码质量检测') {
steps {
sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.source=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=./target/ -Dsonar.login=0ab1a777144b459db09cdd0cfc06a300887c54cb'
}
}
stage('通过docker制作自定义镜像') {
steps {
sh '''mv ./target/*.jar ./docker/
docker build -t ${JOB_NAME}:latest ./docker/'''
}
}
stage('将自定义镜像推送到harbor上') {
steps {
sh '''docker login -u ${harborUser} -p ${harborPwd} ${harborUrl}
docker tag ${JOB_NAME}:latest ${harborUrl}/${harborRepo}/${JOB_NAME}:latest
docker push ${harborUrl}/${harborRepo}/${JOB_NAME}:latest
docker image prune -f'''
}
}
stage('将yaml文件传送到k8smaster上') {
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'k8smaster', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'pipeline.yaml')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
stage('在k8smaster节点上执行yaml文件') {
steps {
sh '''ssh root@192.168.12.120 kubectl apply -f /demo/k8s/pipeline.yaml
ssh root@192.168.12.120 kubectl -n test rollout restart deployment pipeline'''
}
}
}
post {
success {
dingtalk (
robot: 'jenkins_dingtalk',
type:'MARKDOWN',
title: "success: ${JOB_NAME}",
text: ["- 成功构建:${JOB_NAME}项目!\n- 版本:latest\n- 持续时间:${currentBuild.durationString}\n- 任务:#${JOB_NAME}"]
)
}
failure {
dingtalk (
robot: 'jenkins_dingtalk',
type:'MARKDOWN',
title: "fail: ${JOB_NAME}",
text: ["- 失败构建:${JOB_NAME}项目!\n- 版本:latest\n- 持续时间:${currentBuild.durationString}\n- 任务:#${JOB_NAME}"]
)
}
}
}
3.3、修改代码文件,测试