如何为Jenkins PipelineJob添加lockableResources锁避免并发执行?
为Jenkins流水线添加全局锁以避免多实例同时运行
问题背景
需要给Jenkins流水线添加锁,防止同一流水线的多个实例同时执行。已完成前置配置:
- 在
plugins.txt中安装lockable-resources插件 - 在
casc.yaml中声明锁资源:
lockableResourcesManager: declaredResources: - name: "host-1-lock" description: "Lock to run jobs on host 1"
尝试过程与问题
1. 在JobDSL中直接添加锁
尝试在pipelineJob定义里添加锁逻辑,期望锁在整个Job周期内保持:
pipelineJob('Test_Job-2') { quietPeriod(0) triggers { cron('H 6 * * *') } lockableResources('host-1-lock') // 此处报错 parameters { labelParam('Node_Select') { defaultValue('host1') allNodes('allCases', 'IgnoreOfflineNodeEligibility') } } definition { cps { script(readFileFromWorkspace('scripts/mypipeline.jenkinsfile')) } } }
报错信息:
ERROR: (script, line 6) No signature of method: javaposse.jobdsl.dsl.jobs.WorkflowJob.lockableResources() is applicable for argument types: (java.lang.String) values: [host-1-lock]
尝试lock("host-1-lock")语法、将lock()放入definition块内,均无效。
2. 在Jenkinsfile的stages外添加锁
将锁逻辑移至scripts/mypipeline.jenkinsfile,直接写在stages块外:
pipeline { options { ansiColor('xterm') } agent any stages { lock('host-1-lock') // 此处报错lock未定义 stage ('Build 1') { when { allOf { expression { return params.RUN_BUILD } } } steps { catchError(stageResult: 'FAILURE') { retry(count: 7) { timeout(time: 180, unit: 'MINUTES') { script { def result = buildLabJob(STAGE_BUILD) sh "exit $result" } } } } } post { failure { log("warn", "Build failed, skipping remaining stages") script { skipRemainingStages = true } } } } // 其他阶段省略... } }
报错提示lock未定义,查文档得知lock通常在stage内使用,但需求是让锁覆盖多个阶段,保持整个流水线执行期间不释放。
3. 单独设置获取/释放锁阶段
尝试添加专门的阶段来管理锁的获取与释放:
stage('Acquire Lock') { steps { lock(resource: 'Lab240-lock') { echo 'Lock acquired' } } } // 其他业务阶段... stage('Release Lock') { steps { lock(resource: 'Lab240_lock', inversePrecedence: true) { echo 'Lock released' } } }
效果不符合预期:流水线1获取锁后运行时,流水线2启动后也能立即获取锁,两个流水线交替执行阶段,无法实现流水线2完全等待流水线1结束后再运行。
正确解决方案
要实现整个流水线期间持有锁,需将所有业务阶段包裹在一个lock块内,修改后的Jenkinsfile如下:
pipeline { options { ansiColor('xterm') } agent any stages { stage('Run with Global Lock') { steps { lock(resource: 'host-1-lock') { // 所有需要锁保护的阶段逻辑全部移入此块 stage ('Build 1') { when { allOf { expression { return params.RUN_BUILD } } } steps { catchError(stageResult: 'FAILURE') { retry(count: 7) { timeout(time: 180, unit: 'MINUTES') { script { def result = buildLabJob(STAGE_BUILD) sh "exit $result" } } } } } post { failure { log("warn", "Build failed, skipping remaining stages") script { skipRemainingStages = true } } } } stage ('Build Cluster DPU') { when { allOf { expression { return params.RUN_BUILD } } } steps { catchError(stageResult: 'FAILURE') { retry(count: 7) { timeout(time: 360, unit: 'MINUTES') { script { def result = buildLabJob(STAGE_BUILD_JOBS) sh "exit $result" } } } } } post { failure { log("warn", "Build failed, skipping remaining stages") script { skipRemainingStages = true } } } } stage ('Deploy Custom') { when { allOf { expression { !skipRemainingStages } expression { return params.RUN_CUSTOM } } } steps { catchError(stageResult: 'FAILURE') { script { def result = buildLabJob(STAGE_CUSTOM_JOBS) sh "exit $result" } } } post { failure { script { skipRemainingStages = true } } } } } } } } }
原理说明
lock块进入时会获取指定资源锁,直到块内所有逻辑(包括嵌套的所有stage)执行完毕才会释放锁- 所有业务阶段都在锁的保护范围内,同一时间仅允许一个流水线实例执行这些阶段
- 注意:
lock必须放在steps块内,因此需要用一个父stage来包裹所有需要锁保护的内容
额外注意事项
- 确保
lockable-resources插件版本正确,且Jenkins已重启加载配置 - 锁资源名称需与
casc.yaml中声明的完全一致(注意拼写,如之前的Lab240-lock与Lab240_lock拼写不一致会导致锁失效) - 若流水线需在特定节点执行,需确保锁与节点选择逻辑不冲突
内容的提问来源于stack exchange,提问作者user21113865
相关产品推荐
相关产品推荐

