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用户提供了大量的灵活性性和可扩展性,如果脚本中有大量的逻辑处理则推荐使用
本文主要介绍官网主推也较为简单的声明式。 声明式官方文档参考: https://www.jenkins.io/zh/doc/book/pipeline/syntax/
学习Jenkins的时候参考的博客,力推讲的非常好: https://wiki.eryajf.net/pages/3298.html#_1-框架介绍。
声明式Pipeline&多分支
构建部署模式是模仿线上环境,故使用多分支。
主要分为三个文件:
- 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}
方式调用,避免获取不到。
测试了下BRANCH_NAME这个变量,在多分支中好像才能获取到对应的分支。 -- 先遗留这个细节问题,反正多分支没事。
node {
config = readYaml file: 'config.yml'
// 读取config.yml中的配置,调用方式 config.name 获取值
}
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
IMAGE_WHOLE_NAME = "${config.IMAGE_NAME}:${IMAGE_TAG}" // 完整的镜像名称
OUTPUT_URL_TEXT="${env.BUILD_URL}consoleText" // 文本url
}
// 流水线阶段
stages {
stage("下载代码"){
steps{
git credentialsId: 'gitee_admin', url: "${config.GIT_URL}"
}
}
stage("编译"){
steps{
sh "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('构建结果')
echo "${env.PIPELINE_CONFIG}"
}
}
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页面构建图片
触发邮件截图