Jenkins初识Pipeline之声明式-项目构建

Jenkins是Devops技术栈的核心之一。CI/CD离不开编写Pipeline脚本。 Pipeline分为 声明式、脚本式。

二者的选择

Jenkins是使用Java实现的,所以在很早的时候就引入了groovy作为DSL,管理员可以使用groovy来实现一些自动化和高级的管理功能。因为groovy引擎已经集成到Jenkins,所以在pipeline一开始很自然就使用groovy来编写Jenkinsfile。但是groovy毕竟是一种语言,对于没有太多编程经验的小白学习成本有些高,这个时候声明式的pipeline就出现了,主要是为了降低入门的难度
区别如下:

  • 声明式pipeline,官方鼓励声明式编程模型,比较适合没有编程经验的初学者.
  • 脚本式pipeline,是基于groovy的DSL语言实现的,为Jenkins用户提供了大量的灵活性性和可扩展性,如果脚本中有大量的逻辑处理则推荐使用

声明式Pipeline&多分支

构建部署模式是模仿线上环境,故使用多分支。

本文主要介绍官网主推也较为简单的声明式。 声明式官方文档参考: https://www.jenkins.io/zh/doc/book/pipeline/syntax/
学习Jenkins的时候参考的博客,力推讲的非常好: https://wiki.eryajf.net/pages/3298.html#_1-%E6%A1%86%E6%9E%B6%E4%BB%8B%E7%BB%8D%E3%80%82

环境准备

首先安装插件 **Generic Webhook Trigger **,Pipeline 安装后重启Jenkins。

配置Jenkins发邮件功能。

系统管理->系统设置->Jenkins Location

  • 首先配置系统管理员邮箱地址名称
  • 配置发送邮箱验证信息,点击最底下可以测试。

可以发送出测试邮箱就配置完毕了。为脚本中发送构建通知做准备。

配置Jenkins WebHook功能。

功能背景: 在Dev环境,开发同学会频繁在本地修改代码并提交到git,提交后就要部署到服务器上。但是每次手工点太过麻烦,而且有的开发同学对Jenkins也没有绝对的权限,运维一般也不会管dev环境。 WebHook功能代码提交后,触发git的webhook调用Jenkins完成构建。

配置Jenkins

不同Jenkins界面可能存在差异
系统管理-> 管理用户 -> 点击超级管理员(guopeihua) -> 设置 添加 Api Token
把token复制下来,在gitee上会用

系统管理->安全全局配置->跨站请求伪造保护 不勾选

配置Gitee

仓库页 -> 管理->webhook

添加webhook
http://api-bj.top:8080/jenkins/generic-webhook-trigger/invoke?token=hello-word

  • Jenkins外网ip地址
  • generic-webhook-trigger/invoke 固定的webhook触发地址
  • ?token=xxxxxx token值与jenkins项目中Pipeline的值对应,否则会触发失败。

Jenkins项目配置

项目主要分为三个文件:

  • Dockerfile # build image 使用
  • config.yml # 定义jenkins构建时使用的变量,使得jenkinsfile更加灵活
  • Jenkinsfile # 具体构建部署的逻辑步骤

Jenkins Job配置截图如下:

Dockerfile

1
2
3
FROM adoptopenjdk/openjdk11:alpine-jre
ADD target/spring-boot-helloworld-*-SNAPSHOT.jar /applications/spring-boot-helloworld.jar
ENTRYPOINT ["/bin/sh","-c","/opt/java/openjdk/bin/java -jar /applications/spring-boot-helloworld.jar --server.port=80"]

config.yml

1
2
3
4
GIT_URL: "https://gitee.com/guopeihua/spring-boot-hello-world.git" # git地址
DEPLOY_NAME: "hello-deploy" # 控制器名称
CONTAINER_NAME: "hello" # 容器名称
IMAGE_NAME: "img_hello" # 镜像前缀

Jenkinsfile

这里的一些系统级别的变量尽量使用${env.BRANCH_NAME}方式调用,避免获取不到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
def project_name="${JOB_NAME}".split("/")[1]  // 多分支的项目名称
def branch_name="${JOB_NAME}".split("/")[2] // 多分支的分支名称
def scmUrl = scm.getUserRemoteConfigs()[0].getUrl() // 获取项目 git地址

pipeline {
// 任意主机运行pipeline
agent any

// 编译的工具
tools {
maven 'mvn-3.3.9'
}

// 定义全局变量
environment {
// PIPELINE_CONFIG
// BRANCH_NAME 分支名称
// JOB_BASE_NAME job名称
// JOB_NAME job 全称
// WORKSPACE 构建目录
// BUILD_NUMBER 构建次数
// BUILD_URL 构建的url 加上 consoleText 直达文本页面
BUILD_DATE = sh (script: 'date "+%m%d"', returnStdout: true).trim() // 获取今天的时间 月日
IMAGE_TAG = "${env.BRANCH_NAME}.${BUILD_DATE}.${env.BUILD_NUMBER}" // 镜像tag
OUTPUT_URL_TEXT="${env.BUILD_URL}consoleText" // 文本url
}

triggers {
GenericTrigger (
// 构建时的标题
causeString: 'Triggered by $ref',
// 获取POST参数中的变量,key指的是变量名,通过$ref来访问对应的值,value指的是JSON匹配值(参考Jmeter的JSON提取器)
// ref指的是推送的分支,格式如:refs/heads/master
genericVariables: [[key: 'ref', value: '$.ref'],[key: 'repositoryURL', value: '$.repositoryURL'], [key: 'branch', value: '$.branch']],
// 打印获取的变量的key-value,此处会打印如:ref=refs/heads/master
printContributedVariables: true,
// 打印POST传递的参数
printPostContent: true,
// regexpFilterExpression与regexpFilterExpression成对使用
// 当两者相等时,会触发对应分支的构建
regexpFilterExpression: '^refs/heads/(master|production)$',
regexpFilterText: '$ref',
// 与webhook中配置的token参数值一致
token: "${project_name}"
)
}

// 流水线阶段
stages {
stage("下载代码"){
steps{
git credentialsId: 'gitee_admin', url: "${scmUrl}"
script {
config = readYaml file: 'config.yml' // 读取config.yml
IMAGE_WHOLE_NAME = "${config.IMAGE_NAME}:${IMAGE_TAG}" // 定义完整的镜像名称
}
}
}

stage("编译"){
steps{
sh "/usr/local/maven/bin/mvn clean package -Dfile.encoding=UTF-8 -DskipTests=true;"
}
}

stage("构建镜像"){
steps {
sh """
cd ${WORKSPACE}
docker build -t ${IMAGE_WHOLE_NAME} .
"""
}
}

stage("部署镜像"){
steps {
sh """
kubectl set image deployment/${config.DEPLOY_NAME} ${config.CONTAINER_NAME}=${IMAGE_WHOLE_NAME}
sleep 5;
"""
}
}


stage('判断镜像触发') {
steps {
script {
_image = sh(script: "kubectl get deploy ${config.DEPLOY_NAME} -o jsonpath='{..image}'", returnStdout: true).trim()
if ( _image == "${IMAGE_WHOLE_NAME}" ) {
echo "${_image} Trigger successfully."
// mail bcc: '', body: "${IMAGE_WHOLE_NAME} image触发成功!", cc: '', from: 'guopeihua@ele-cloud.com', replyTo: '', subject: '镜像触发成功', to: '17611443879@163.com'
}else{
echo "${_image} Triggered failed."
mail bcc: '', body: "${IMAGE_WHOLE_NAME} image触发失败 退出构建!", cc: '', from: 'guopeihua@ele-cloud.com', replyTo: '', subject: '镜像触发失败', to: '17611443879@163.com'
sh 'exit 1'
}
}
}
}

}

post{
always{
// 始终都会执行
script{
println('构建结果')
}
}
success{
mail bcc: '', body: "job ${env.JOB_NAME}第${env.BUILD_NUMBER}次构建成功! \n deploy_name: ${config.DEPLOY_NAME} \n URL: ${OUTPUT_URL_TEXT} ", cc: '', from: 'guopeihua@ele-cloud.com', replyTo: '', subject: '[构建成功]', to: '17611443879@163.com'
script{
println('success--构建成功!')
echo "项目构建地址为: ${OUTPUT_URL_TEXT}"
}
}
failure{
mail bcc: '', body: "job ${env.JOB_NAME}第${env.BUILD_NUMBER}次构建失败! \n deploy_name: ${config.DEPLOY_NAME} \n URL: ${OUTPUT_URL_TEXT} ", cc: '', from: 'guopeihua@ele-cloud.com', replyTo: '', subject: '[构建失败]', to: '17611443879@163.com'
script{
println('failure--构建失败!')
}
}
aborted{
mail bcc: '', body: "job ${env.JOB_NAME}第${env.BUILD_NUMBER}次构建终止! \n deploy_name: ${config.DEPLOY_NAME} \n URL: ${OUTPUT_URL_TEXT}", cc: '', from: 'guopeihua@ele-cloud.com', replyTo: '', subject: '[构建终止]', to: '17611443879@163.com'
script{
println('aborted--构建终止!')
}
}
}
}

构建的结果

Jenkins页面构建图片

触发邮件截图

Pipeline 补充 公共Jenkinsfile

整体思路: Jenkins 修改pipeline读取类型,gitee创一个库专门存放Jenkinsfile逻辑
操作如下:

config.yml 增加配置

1
2
3
4
5
6
# Jenkinsfile 存放的公共库路径
pipeline_template: docs/jenkins/Jenkinsfile
GIT_URL: "https://gitee.com/guopeihua/spring-boot-hello-world.git" # git地址
DEPLOY_NAME: "hello-deploy" # 控制器名称
CONTAINER_NAME: "hello" # 容器名称
IMAGE_NAME: "img_hello" # 镜像前缀

Jenkinsfile gitee路径如下:

Jenkins Job配置

其他配置不变


Jenkins初识Pipeline之声明式-项目构建
https://blog.api-bj.top/2024/09/30/Jenkins初识Pipeline之声明式-项目构建/
作者
郭培华
发布于
2024年9月30日
许可协议