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-框架介绍。
环境准备
首先安装插件 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
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
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}方式调用,避免获取不到。
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 增加配置
# 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配置
其他配置不变


浙公网安备 33010602011771号