Jenkins-pipeline进阶使用
1852 字
9 分钟
Jenkins-pipeline进阶使用
[!jenkins文章分为三部分]
第一部分为docker安装Jenkins与常用插件使用,[[Jenkins安装]]
第二部分为运行pipeline并通过k8s pod打包 [[Jenkins-pipeline使用]]
第三部分为,pipeline高阶使用,如map,共享库,此文章
1 复杂情况 dockerfile不通用
如果buildDockerImage输入dockerfile,使用输入的dockerfile,输入使用,默认全局的DOCKER_DOCKERFILE_ID 同事,这里加了个map,根据不同应用名配置不通jar包路径
pipeline { stages { stage('镜像制作') { steps { container('docker') { script { // 定义map,根据不同应用名配置不通jar包路径 def jarPath = [ 'app1': 'path/to/app1.jar', 'app2': 'path/to/app2.jar', ] jarPathbuildDockerImage("${jarPath[APP_NAME]}") } } } } }}
def buildDockerImage(dockerfileContent = null) { if (dockerfileContent) { writeFile file: 'Dockerfile', text: dockerfileContent } else { configFileProvider([configFile(fileId: "${DOCKER_DOCKERFILE_ID}", targetLocation: "dockerfile")]){ // 替换dockerfile文件中的变量 def docker = readFile encoding: "UTF-8", file: "dockerfile" def dockerfile = docker.replaceAll("#APP_NAME","${APP_NAME}") .replaceAll("#PROJECT","${PROJECT}") .replaceAll("#SW_OAP","${SW_OAP}") .replaceAll("#jarPath","${jarPath}") // 生成新的 dockerfile 部署文件 writeFile encoding: 'UTF-8', file: './Dockerfile', text: "${dockerfile}" } } sh "cat Dockerfile"}2 共享存储库
[!done] 正常情况有好多pipeline,有些步骤都是重复的,下面简单介绍下如何复用pipeline
2.1 pipeline简单拆分
pipeline { stages { stage('拉取代码') { steps { script { checkoutCode() } } } }}// 迁移到共享库只需要checkoutCode为文件名,函数名换成calldef checkoutCode() { git(credentialsId: "${GIT_ID}", url: "$GIT_URL", branch: "${BRANCH_NAME}", changelog: true, poll: false)}2.2 拆分部分放到共享存储库
目录结构
jenkins-shared-libraries/├── src/ # 公共函数方法,供其他地方调用。│ └── global.groovy├── vars/ # 构建流程主逻辑│ └── checkoutCode.groovy└── (其他如README.md等)2.3 vars 下例子
# ${xxx}引用的变量对应pipeline里变量cat /vars/checkoutCode.groovydef call() { git(credentialsId: "${GIT_ID}", url: "$GIT_URL", branch: "${BRANCH_NAME}", changelog: true, poll: false)}2.4 src 下例子
[!info] 大部分情况 pipeline 脚本放到 vars 目录就可以 简单介绍下 src 目录使用场景: 比如我 pipeline 用了好几个 template 模板文件, 如 dockerfile, deployment, 我每个脚本都要替换里面的变量, 替换变量的操作就可以放到 src 下实现, 如下
2.5 使用共享存储库

pipeline里引用
@Library('jenkins-shared-libraries@main') _pipeline { agent any parameters { choice choices: ['app1', 'app2'], description: '选择发布程序', name: 'APP_NAME' } options { buildDiscarder(logRotator(numToKeepStr: '10', artifactNumToKeepStr: '5')) } environment { BRANCH_NAME = '环境名(分支名)' GIT_ID = 'git凭据名称' GIT_URL = "https://git.xxx.com/${APP_NAME}.git(git的url)" } stages { stage('拉取代码') { steps { script { checkoutCode() } } } }}3 多选
[!success] 有的公司会把所有微服务代码都放到一个仓库,如果每个都进行一次mvn打包时间非常长,这是一个支持一个大仓库根据应用名,打包不同服务的例子
大概内容就是应用名称为目录名,但使用docker部署,不同服务部署到对应节点
需要的插件:
- Publish Over SSH
- Extended Choice Parameter
- Config File Provider
pipeline { parameters { // 配置多选 extendedChoice name: 'APP_NAME', type: 'PT_CHECKBOX', description: '请勾选所要发布的项目模块', quoteValue: false, saveJSONParameterToFile: false, value: 'schedule,web-township-background', visibleItemCount: 2, multiSelectDelimiter: ',', defaultValue: 'account-server' } options { buildDiscarder(logRotator(numToKeepStr: '10', artifactNumToKeepStr: '5')) } environment { PROJECT = '项目名' BRANCH_NAME = '环境名(分支名)' HARBOR_NAMESPACE = 'docker仓库凭据名称' GIT_ID = 'git凭据名称' GIT_URL = "https://git.xxx.com/${APP_NAME}.git(git的url)" REGISTRY = '镜像仓库地址' DOCKER_COMPOSE_ID = 'global-docker-compose-file' # docker-compose模板名称 DOCKER_DOCKERFILE_ID = 'global-dockerfile-file' # dockerfile模板 SW_OAP = 'skywalking OPA地址' ROBOT_ID = '机器人id' } agent any tools { maven 'maven' jdk 'jdk8' }......这部分跟其他的一样,省略...... stages { stage('镜像制作') { steps { script { // for 上面的多选变量,','分割 for (appName in APP_NAME.tokenize(',')) { // 按应用名称区分目录,进入到对应的应用目录 dir("${appName}") { // 进行打包 buildDockerImage(appName) } } } } } stage('更新版本') { steps { script { // 生成字典,分别是应用名,部署节点,应用端口 def App = [ 'app1': ['host': 'host-231', 'port': '8080'], 'app2': ['host': 'host-241', 'port': '8081'], ] for (appName in APP_NAME.tokenize(',')) { // 根据应用名称与暴露端口生成对应的docker-ompose.yaml dockerCompose(appName, App[appName]['port']) // 发送生成的docker-ompose.yaml文件到对应的节点,然后启动 sshDeploy(appName, App[appName]['host']) } } } } }}// 为了省事,方便理解直接写到下面// 制作镜像函数def buildDockerImage(String appName, String port = '') { // 设置 Docker 镜像名称 def dockerImageName = "${REGISTRY}/${appName}:1.${BUILD_NUMBER}" if ("${REGISTRY}" == '') { dockerImageName = "${REGISTRY}/${appName}:1.${BUILD_NUMBER}" } // 读取 docker-compose 部署文件 configFileProvider([configFile(fileId: "${DOCKER_COMPOSE_ID}", targetLocation: "docker-compose")]){ // 替换docker-compose文件中的变量 def dockercompose = readFile encoding: "UTF-8", file: "docker-compose" def dockercomposefile = dockercompose.replaceAll("#APP_NAME","${appName}") .replaceAll("#APP_IMAGE_NAME","${dockerImageName}") .replaceAll("#APP_PORT","${port}") // 生成新的 docker-compose 部署文件 writeFile encoding: 'UTF-8', file: 'docker-compose.yml', text: "${dockercomposefile}" }}// ssh远程更新函数def sshDeploy(String appName,String hostName) { sshPublisher(publishers: [sshPublisherDesc(configName: "${hostName}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: """ docker-compose -f /data/app/${appName}/docker-compose.yml up -d """, execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: "${appName}/", remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'docker-compose.yml')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])}4 审批支持
4.1 最简单方式,点击才能继续执行pipeline
pipeline { agent any stages { stage('拉取代码') { steps { input id: 'approve' , message: '是否同意更新生产环境', ok: 'Yes' } } }}4.2 飞书接收验证号,验证码匹配个更新
pipeline { options { buildDiscarder(logRotator(numToKeepStr: '10', artifactNumToKeepStr: '5')) timeout(time:12, unit:'HOURS') skipDefaultCheckout() } environment { ROBOT_ID = '机器人id' // 生成随机数 max = 100000 randomToken = "${Math.abs(new Random().nextInt(max+1))}" } agent any stages { stage('审批') { steps { sh 'echo ${randomToken}' script { tokenApprove(randomToken) } } } }}
def tokenApprove(String randomToken) { larkrandomToken(env.randomToken) def isAbort = false //取消按钮 timeout(time:12, unit:'HOURS'){ //等待审批人审批,并通过timeout设置任务过期时间,防止任务永远挂起 try { def token = input( id: 'inputap', message: "xx申请更新服务", ok:"同意", , parameters: [ [$class: 'StringParameterDefinition',defaultValue: "", name: 'token',description: '请输入发布的秘钥' ] ]) if ( "${token}" == env.randomToken) { } else { error '秘钥错误,任务已终止' } }catch(e) { // input false throw e } }}def larkrandomToken(String randomToken) { lark( robot: "${ROBOT_ID}", type: "CARD", title: "📣 xx更新生产环境审批", text: [ "🔑 **密钥是**: <font color='red'>${randomToken}</font>", "<at id=all></at>" ], buttons: [ [ title: "更改记录", url: "${BUILD_URL}changes" ], [ title: "审批地址", type: "danger", url: "${JOB_DISPLAY_URL}" ] ] )}5 完整配置
pipeline配置,代码整洁多了
@Library('jenkins-shared-libraries@main') _//// 配置多选,与单选二选一//properties([// parameters([// [// $class: 'ChoiceParameter',// choiceType: 'PT_CHECKBOX', // 复选// name: 'APP_NAME',// description: '选择应用',// script: [// $class: 'GroovyScript', // Groovy脚本// script: [// classpath: [],// sandbox: true,// // 要返回的结果,写在一行// script: "return ['app1', 'app2']"// ]// ]// ]// ])// ])pipeline { parameters { choice choices: ['app1', 'app2'], description: '选择发布程序', name: 'APP_NAME' } environment { PROJECT = '项目名' BRANCH_NAME = '环境名(分支名)' HARBOR_NAMESPACE = 'docker仓库凭据名称' GIT_ID = 'git凭据名称' GIT_URL = "https://git.xxx.com/${APP_NAME}.git(git的url)" REGISTRY = '镜像仓库地址' KUBECONFIG_ID = 'kubeconfig id' KUBERNETES_APP_REPLICAS = 'deployment副本数' KUBERNETES_DEPLOYMENT_ID = 'global-kubernetes-deployment' # deployment模板名称 DOCKER_DOCKERFILE_ID = 'global-dockerfile-file' # dockerfile模板 SW_OAP = 'skywalking OPA地址' ROBOT_ID = '机器人id' max = 100000 randomToken = "${Math.abs(new Random().nextInt(max+1))}" } agent { kubernetes { inheritFrom 'mypod' } } options { buildDiscarder(logRotator(numToKeepStr: '10', artifactNumToKeepStr: '5')) timeout(time:12, unit:'HOURS') skipDefaultCheckout() } stages { stage('拉取代码') { steps { script { checkoutCode() } } post { failure { echo "❌ 代码拉取失败" failedsendNotification("拉取代码", "❌失败") } } } stage('编译打包') { steps { script { buildPackage() } } post { failure { echo "❌ 打包失败" failedsendNotification("maven打包", "❌失败") } } } stage('镜像制作') { steps { container('docker') { script { // 定义map,根据不同应用名配置不通jar包路径 def jarPath = [ 'app1': 'path/to/app1.jar', 'app2': 'path/to/app2.jar', ] jarPathbuildDockerImage("${jarPath[APP_NAME]}") } } } post { failure { echo "❌ 镜像制作" failedsendNotification("镜像制作", "❌失败") } } } stage('审批') { steps { sh 'echo ${randomToken}' script { tokenApprove(randomToken) } } } stage('更新版本') { steps { script { deployToKubernetes() } } post { failure { echo "❌ 更新版本失败" failedsendNotification("更新版本", "❌失败") } } } } post { success { successsendNotification() } }}文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
相关文章 智能推荐
1
Jenkins pipeline使用
💨DevOps jenkins文章分为三部分,此文章是第二部分
2
Jenkins安装
💨DevOps jenkins文章分为三部分,此文章是第一部分
3
Jenkins权限管理
💨DevOps Jenkins权限管理,保证权限最小化
4
ArgoCD+Jenkins持续集成
💨DevOps Argo 与 Jenkins 结合 CI/CD 快速入门
5
OpenClaw安装
💻Linux 龙虾安装笔记
随机文章 随机推荐