Jenkins Pipeline 脚本优化实践:从繁琐到简洁

引言

在持续集成的过程中,Jenkins Pipeline 是非常关键的一环。它定义了如何自动编译、测试和部署代码。随着项目的不断发展,Pipeline 的复杂性也在不断上升,这就需要我们持续优化 Pipeline 脚本,以提高代码的可读性和维护性。本文将介绍一次从繁琐Pipeline脚本到精简Pipeline脚本的转化过程,以及这种转化所带来的好处。

原始的 Pipeline 脚本:

image.png
注: loader其实已经废弃了

pipeline {agent { label  "build01" }stages {stage("GetCode"){agent { label  "build01" }steps{script{println("下载代码 --> 分支: ${env.branchName}")checkout([$class: 'GitSCM', branches: [[name: "${env.branchName}"]],doGenerateSubmoduleConfigurations: false,extensions: [[$class: 'CloneOption', depth: 1, noTags: false, reference: '', shallow: true]],submoduleCfg: [],userRemoteConfigs: [[credentialsId: 'xxxxx',url: "${env.gitHttpURL}"]]])}}}    stage('docker build dataloader-game-ucenter') {agent { label  "build01" }when {environment name: 'dataloader', value: 'true'}steps {sh ''' cd dataloaderdocker build  --build-arg NODE_ENV=game-ucenter  -t swr.cn-north-4.myhuaweicloud.com/master-metaspace/dataloader-game-ucenter:$data .'''withCredentials([usernamePassword(credentialsId: 'hw-registry', passwordVariable: 'dockerPassword', usernameVariable: 'dockerUser')]) {sh "docker login -u ${dockerUser} -p ${dockerPassword} swr.cn-north-4.myhuaweicloud.com"sh "docker push swr.cn-north-4.myhuaweicloud.com/master-metaspace/dataloader-game-ucenter:$data"}}}stage('docker build datawriter-game-ucenter') {agent { label  "build01" }when {environment name: 'datawriter', value: 'true'}steps {sh ''' cd datawriter-game-ucenterdocker build  --build-arg NODE_ENV=game-ucenter  -t swr.cn-north-4.myhuaweicloud.com/master-metaspace/datawriter-game-ucenter:$data .'''withCredentials([usernamePassword(credentialsId: 'hw-registry', passwordVariable: 'dockerPassword', usernameVariable: 'dockerUser')]) {sh "docker login -u ${dockerUser} -p ${dockerPassword} swr.cn-north-4.myhuaweicloud.com"sh "docker push swr.cn-north-4.myhuaweicloud.com/master-metaspace/datawriter-game-ucenter:$data"}}}   stage('docker build game-ucenter') {agent { label  "build01" }when {environment name: 'game-ucenter', value: 'true'}steps {sh ''' cd game-ucenterdocker build  --build-arg NODE_ENV=game-ucenter  -t swr.cn-north-4.myhuaweicloud.com/master-metaspace/game-ucenter:$data .'''withCredentials([usernamePassword(credentialsId: 'hw-registry', passwordVariable: 'dockerPassword', usernameVariable: 'dockerUser')]) {sh "docker login -u ${dockerUser} -p ${dockerPassword} swr.cn-north-4.myhuaweicloud.com"sh "docker push swr.cn-north-4.myhuaweicloud.com/master-metaspace/game-ucenter:$data"}}}              stage('develop') {parallel {stage("develop datawriter-game-ucenter") {when {environment name: 'datawriter-game-ucenter', value: 'true'}agent { label  "huaweiyun-xx" }steps {sh "sed -e 's/{data}/$data/g' /home/jenkins/workspace/yaml/master-metaspace/datawriter-game-ucenter.tpl > /home/jenkins/workspace/yaml/master-metaspace/datawriter-game-ucenter.yaml"sh "sudo kubectl apply -f /home/jenkins/workspace/yaml/master-metaspace/datawriter-game-ucenter.yaml --namespace=master-metaspace --context=master"}}stage("develop dataloader-game-ucenter") {when {environment name: 'dataloader', value: 'true'}agent { label  "huaweiyun-xx" }steps {sh "sed -e 's/{data}/$data/g' /home/jenkins/workspace/yaml/master-metaspace/dataloader-game-ucenter.tpl > /home/jenkins/workspace/yaml/master-metaspace/dataloader-game-ucenter.yaml"sh "sudo kubectl apply -f /home/jenkins/workspace/yaml/master-metaspace/dataloader-game-ucenter.yaml --namespace=master-metaspace --context=master"}}stage("develop game-ucenter") {when {environment name: 'game-ucenter', value: 'true'}agent { label  "huaweiyun-xx" }steps {sh "sed -e 's/{data}/$data/g' /home/jenkins/workspace/yaml/master-metaspace/game-ucenter.tpl > /home/jenkins/workspace/yaml/master-metaspace/game-ucenter.yaml"sh "sudo kubectl apply -f /home/jenkins/workspace/yaml/master-metaspace/game-ucenter.yaml --namespace=master-metaspace --context=master"}}                                  }}}
}

在优化之前,我们的 Jenkins Pipeline 脚本中包含了多个独立定义的 stage,每个 stage 中都有重复的结构和指令:

  1. 明确指定了 agent。
  2. 在每个 stage 的 steps 中,都使用了类似的脚本来操作 git、构建 docker 镜像和部署到 Kubernetes。
  3. 使用了冗长的 shell 脚本来绑定变量和执行部署。

这种写法虽然直观,但存在以下问题:

  • 代码冗余:相同的任务(例如构建与部署)重复编写了多次。
  • 低效的修改:一旦需要调整构建或部署流程,需要同时修改多个类似的代码段。
  • 可读性差:过于细碎的脚本内容使得新成员阅读和理解这个 Pipeline 变得困难。

转化的原因与过程

简化 agent 的声明

pipeline {agent none // Use none at the top level, each stage will define its own agent.}

在原始脚本中,每个 stage 都重复指定相同的 agent,这是不必要的。转化后,我们在 pipeline 的顶层使用 agent none 声明,表示不在这一层级指定执行者,这样各个 stage 就可以根据需求独立地声明自己的 agent。

将环境变量统一管理

我们创建一个 environment 部分来集中定义环境变量,简化了变量的管理,并且当需要修改时只要在一个地方进行调整即可。

    environment {REGISTRY = "swr.cn-north-4.myhuaweicloud.com/master-metaspace"KUBE_CONFIG = "--namespace=master-metaspace --context=master"KUBE_YAML_PATH = "/home/jenkins/workspace/yaml/master-metaspace"// Assume that 'data' is defined elsewhere or injected as a parameter.}

使用共用方法减少代码重复

对于 Docker 镜像的构建和推送操作,每个应用几乎执行相同的步骤。我们提取了一个方法 buildAndPushDockerImage 来代替在每个 stage 里重复定义的步骤,这样不但减小了脚本体积,也提升了代码的复用性。

def buildAndPushDockerImage(String imageName, String tag) {sh "cd $imageName && docker build --build-arg NODE_ENV=$imageName -t $REGISTRY/$imageName:$tag ."withCredentials([usernamePassword(credentialsId: 'hw-registry', passwordVariable: 'dockerPassword', usernameVariable: 'dockerUser')]) {sh "docker login -u $dockerUser -p $dockerPassword $REGISTRY"sh "docker push $REGISTRY/$imageName:$tag"}
}

同理,部署到 Kubernetes 的指令也被提取到了 deployToKubernetes 方法中,进一步去重。

def deployToKubernetes(String kubernetesComponent) {String templateFile = "${KUBE_YAML_PATH}/${kubernetesComponent}.tpl"String outputFile = "${KUBE_YAML_PATH}/${kubernetesComponent}.yaml"sh "sed -e 's/{data}/$data/g' $templateFile > $outputFile"sh "sudo kubectl apply -f $outputFile $KUBE_CONFIG"
}

结构的并行化

将 Docker 构建步骤组合为一个并行执行的 stage,这样不仅可以缩短整个流水线的执行时间,还可以使得结构更加清晰。

        stage("Docker Builds") {parallel {stage('Build dataloader-game-ucenter') {agent { label "build01" }when { environment name: 'dataloader', value: 'true' }steps {buildAndPushDockerImage("dataloader-game-ucenter", env.data, env.BASE_WORKSPACE)}}stage('Build datawriter-game-ucenter') {agent { label "build01" }when { environment name: 'datawriter', value: 'true' }steps {buildAndPushDockerImage("datawriter-game-ucenter", env.data, env.BASE_WORKSPACE)}}stage('Build game-ucenter') {agent { label "build01" }when { environment name: 'game-ucenter', value: 'true' }steps {buildAndPushDockerImage("game-ucenter", env.data, env.BASE_WORKSPACE)}}}}

转化后的好处

转换后的pipeline如下:

pipeline {agent none // Use none at the top level, each stage will define its own agent.environment {REGISTRY = "swr.cn-north-4.myhuaweicloud.com/master-metaspace"KUBE_CONFIG = "--namespace=master-metaspace --context=master"KUBE_YAML_PATH = "/home/jenkins/workspace/yaml/master-metaspace"// Assume that 'data' is defined elsewhere or injected as a parameter.BASE_WORKSPACE = "xxxxxxx" // 定义一个基础工作空间路径}stages {stage("GetCode") {agent { label "build01" }steps {script {checkout scm: [$class: 'GitSCM',branches: [[name: env.branchName]],extensions: [[$class: 'CloneOption', depth: 1, noTags: false, shallow: true]],userRemoteConfigs: [[credentialsId: 'xxxx', url: env.gitHttpURL]]]}}}stage("Docker Builds") {parallel {stage('Build dataloader-game-ucenter') {agent { label "build01" }when { environment name: 'dataloader', value: 'true' }steps {buildAndPushDockerImage("dataloader-game-ucenter", env.data, env.BASE_WORKSPACE)}}stage('Build datawriter-game-ucenter') {agent { label "build01" }when { environment name: 'datawriter', value: 'true' }steps {buildAndPushDockerImage("datawriter-game-ucenter", env.data, env.BASE_WORKSPACE)}}stage('Build game-ucenter') {agent { label "build01" }when { environment name: 'game-ucenter', value: 'true' }steps {buildAndPushDockerImage("game-ucenter", env.data, env.BASE_WORKSPACE)}}}}stage('Development Deployment') {parallel {stage("Deploy datawriter-game-ucenter") {when { environment name: 'datawriter-game-ucenter', value: 'true' }agent { label  "huaweiyun-xx" }steps {deployToKubernetes("datawriter-game-ucenter")}}stage("Deploy dataloader-game-ucenter") {when { environment name: 'dataloader', value: 'true' }agent { label  "huaweiyun-xx" }steps {deployToKubernetes("dataloader-game-ucenter")}}stage("Deploy game-ucenter") {when { environment name: 'game-ucenter', value: 'true' }agent { label  "huaweiyun-xx" }steps {deployToKubernetes("game-ucenter")}}}}}
}// Define methods outside pipeline to avoid repetitiondef buildAndPushDockerImage(String imageName, String tag, String workspacePath) {sh "cd ${workspacePath} && echo 'Current directory: \$(pwd)'" // 使用基础工作空间变量sh "cd ${workspacePath}/${imageName}&& docker build --build-arg NODE_ENV=$imageName -t $REGISTRY/$imageName:$tag ."withCredentials([usernamePassword(credentialsId: 'hw-registry', passwordVariable: 'dockerPassword', usernameVariable: 'dockerUser')]) {sh "docker login -u $dockerUser -p $dockerPassword $REGISTRY"sh "docker push $REGISTRY/$imageName:$tag"}
}def deployToKubernetes(String kubernetesComponent) {String templateFile = "${KUBE_YAML_PATH}/${kubernetesComponent}.tpl"String outputFile = "${KUBE_YAML_PATH}/${kubernetesComponent}.yaml"sh "sed -e 's/{data}/$data/g' $templateFile > $outputFile"sh "sudo kubectl apply -f $outputFile $KUBE_CONFIG"
}
  • 代码结构清晰:每个阶段的职责更加明确,整个 Pipeline 结构变得简单易懂。
  • 维护成本降低:通用操作被封装成方法,避免了重复代码,当流程需要修改时,只需在一个地方更新。
  • 执行效率提升:使用 parallel 来并行构建 Docker 镜像降低了整体构建时间。
  • 可扩展性增加:如果将来要增加新的镜像构建或者 Kubernetes 部署,只需很少的修改即可。

总结来说,通过优化 Jenkins Pipeline 脚本,我们不仅提高了流程的效率,还大大减小了维护成本。在持续集成和持续部署的实践中,拥有一个简洁和高效的 Pipeline 是至关重要的。

特意强调的

environment 添加了BASE_WORKSPACE,why?
在Jenkins中,当你在一个Pipeline中使用并行步骤执行作业时,默认情况下,每个并行的分支都会创建一个新的工作空间目录,格式一般为workspace@2workspace@3等,这样做是为了避免各个分支之间产生文件系统上的冲突。
这样带来的后果就是:
image.png
构建过程中就会出现No such file or directory这样的报错:
image.png
所以这里引入了BASE_WORKSPACE

    environment {REGISTRY = "swr.cn-north-4.myhuaweicloud.com/master-metaspace"KUBE_CONFIG = "--namespace=master-metaspace --context=master"KUBE_YAML_PATH = "/home/jenkins/workspace/yaml/master-metaspace"// Assume that 'data' is defined elsewhere or injected as a parameter.BASE_WORKSPACE = "/home/jenkins2/workspace/test6" // 定义一个基础工作空间路径}

build过程中引入了:

sh "cd ${workspacePath} && echo 'Current directory: \$(pwd)'"

确保并行步骤中引用到正确的工作空间目录!

注: 以上脚本代码转换后的pipeline使用chatgpt生成,并多次修订!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/279044.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PE硅芯管产品由白色加蓝色条、黑色加蓝色条、颜色可定做

PE硅芯管的产品设计独特,其表面有白色加蓝色条纹或黑色加蓝色条纹等多种颜色可选。这些不同颜色的选择使得管道更加美观,同时也可以根据不同的使用场合和需求进行定制。 除了常规的白色和黑色之外,PE硅芯管还可以根据客户的需求定制其他颜色…

docker部署go gin框架 Windows环境

目录 文章目的是什么 环境介绍 Windows 环境下 docker 部署 go gin 详细步骤 运行容器时因为挂载文件可能会出现的问题 直接部署gin(跳过运行容器时因为挂载文件可能会出现的问题) 文章目的是什么 假设我们学习了 go 语言,在 Windows(本…

uniGUI学习之UniHTMLMemo1富文本编辑器

1]系统自带的富文本编辑器 2]jQueryBootstarp富文本编辑器插件summernote.js 1]系统自带的富文本编辑器 1、末尾增加<p> 2、增加字体 3、解决滚屏问题 4、输入长度限制问题 5、显示 并 编辑 HTML源代码(主要是图片处理) 1、末尾增加<p> UniHTMLMemo1.Lines…

C# WPF上位机开发(crc校验)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 为了验证数据传输的过程中有没有发生翻转&#xff0c;我们在传输报文的同时一般还会添加一个crc校验。对于modbus协议也是一样&#xff0c;它在数据…

最好的猫粮排行榜前十名有哪些品牌?质量好的主食冻干猫粮分享

为什么越来越多人推荐冻干猫粮喂养呢&#xff1f;主食冻干猫粮究竟是最适应猫饮食习惯的喂养方式还是消费陷阱&#xff1f; 作为一个6年的宠物营养师&#xff0c;我以前接触过很多不同品种的猫咪&#xff0c;一只健康又漂亮的猫咪从表面上就能看出来&#xff01;体型匀称刚好、…

springboot升级到3.2导致mybatis-plus启动报错

在springboot升级到3.2时&#xff0c;服务启动报错 java.lang.IllegalArgumentException: Invalid value type for attribute ‘factoryBeanObjectType’: java.lang.String&#xff1a; java.lang.IllegalArgumentException: Invalid value type for attribute factoryBeanOb…

基于ssm金旗帜文化培训学校网站的设计与开发论文

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对培训学校展示信息管理混乱&#xff0c;出错率高&#xff0c;信息安全…

数据标注公司如何确保数据安全?景联文科技多维度提供保障

数据标注公司通常拥有大量的AI数据和用户数据&#xff0c;保护数据安全是数据标注公司的重要任务。 数据标注公司确保标注数据的安全可以从制度、人员、工具等多个方面入手&#xff0c;建立完善的安全管理体系和审计机制&#xff0c;加强应急预案和备份机制的建立&#xff0c;以…

【LeetCode刷题】--157.用Read4读取N个字符

157.用Read4读取N个字符 /*** The read4 API is defined in the parent class Reader4.* int read4(char[] buf4);*/public class Solution extends Reader4 {/*** param buf Destination buffer* param n Number of characters to read* return The number of actual…

风速预测(四)基于Pytorch的EMD-Transformer模型

目录 前言 1 风速数据EMD分解与可视化 1.1 导入数据 1.2 EMD分解 2 数据集制作与预处理 2.1 先划分数据集&#xff0c;按照8&#xff1a;2划分训练集和测试集 2.2 设置滑动窗口大小为7&#xff0c;制作数据集 3 基于Pytorch的EMD-Transformer模型预测 3.1 数据加载&am…

现代雷达车载应用——第2章 汽车雷达系统原理 2.6节 雷达设计考虑

经典著作&#xff0c;值得一读&#xff0c;英文原版下载链接【免费】ModernRadarforAutomotiveApplications资源-CSDN文库。 2.6 雷达设计考虑 上述部分给出了汽车雷达基本原理的简要概述。在雷达系统的设计中&#xff0c;有几个方面是必不可少的&#xff0c;它们决定了雷达系…

redis:四、双写一致性的原理和解决方案(延时双删、分布式锁、异步通知MQ/canal)、面试回答模板

双写一致性 场景导入 如果现在有个数据要更新&#xff0c;是先删除缓存&#xff0c;还是先操作数据库呢&#xff1f;当多个线程同时进行访问数据的操作&#xff0c;又是什么情况呢&#xff1f; 以先删除缓存&#xff0c;再操作数据库为例 多个线程运行的正常的流程应该如下…