Jenkins并行流水线各阶段使用不同用户的问题求助
问题原因分析
你碰到的是Groovy闭包的变量引用捕获特性导致的问题:在each循环中定义的闭包并没有保存循环时WINUSER的当前值,而是持有了这个变量的引用。当整个循环执行完毕,WINUSER已经被更新为最后一次循环的test3,所有并行阶段运行时都会读取这个最终值,所以出现了所有agent都用同一个用户名的情况。
解决方案
要让每个并行阶段获取到对应循环步骤的用户名,核心是让每个闭包绑定独立的变量值,避免共享同一个变量引用。以下是几种实用的修改方案:
方案一:合并循环逻辑(最直观)
把阶段名生成和并行闭包定义合并到同一个计数循环中,每次循环创建独立的局部变量存储当前用户名,确保每个闭包捕获的是独立的变量:
def parallelStagesSoak = [:] pipeline { agent none parameters { string(name: "AGENT_COUNT", defaultValue: "1", trim: true, description: "How many Windows agents to run the test") string(name: "HNAS_PATH_WIN", defaultValue: "\\\\server1\\dir", trim: true, description: "path to share") string(name: "MAP_DRIVE_WIN", defaultValue: "P", trim: true, description: "drive letter to use in Windows") } stages { stage("soak_test") { steps { script { def agentCount = params.AGENT_COUNT.toInteger() // 直接在计数循环中生成每个并行任务 for (int i=1; i<=agentCount; i++) { def currentUser = "test${i}" // 每次循环创建独立变量 def stageName = "win_agent_${i}" parallelStagesSoak[stageName] = { echo "WINUSER in test=${currentUser}" node(label: "soak-client && windows") { stage(stageName) { // 修正原代码参数名错误:PATH_WIN -> HNAS_PATH_WIN bat "net use ${params.MAP_DRIVE_WIN}: ${params.HNAS_PATH_WIN} /user:domain1\\${currentUser} test" } } } } parallel parallelStagesSoak } } } } }
方案二:用索引关联阶段与用户名
如果需要保留先收集阶段名的逻辑,可以通过eachWithIndex获取阶段对应的索引,重新计算对应用户名:
def parallelStagesSoak = [:] def stagesToBuild = [] pipeline { agent none parameters { string(name: "AGENT_COUNT", defaultValue: "1", trim: true, description: "How many Windows agents to run the test") string(name: "HNAS_PATH_WIN", defaultValue: "\\\\server1\\dir", trim: true, description: "path to share") string(name: "MAP_DRIVE_WIN", defaultValue: "P", trim: true, description: "drive letter to use in Windows") } stages { stage("soak_test") { steps { script { def agentCount = params.AGENT_COUNT.toInteger() // 先收集所有阶段名 for (int i=1; i<=agentCount; i++) { stagesToBuild.add("win_agent_${i}") } // 通过索引获取对应用户名 stagesToBuild.eachWithIndex { stageName, index -> def currentUser = "test${index + 1}" // 索引从0开始,加1匹配编号 parallelStagesSoak[stageName] = { echo "WINUSER in test=${currentUser}" node(label: "soak-client && windows") { stage(stageName) { bat "net use ${params.MAP_DRIVE_WIN}: ${params.HNAS_PATH_WIN} /user:domain1\\${currentUser} test" } } } } parallel parallelStagesSoak } } } } }
方案三:闭包参数绑定(进阶用法)
利用Groovy闭包的curry方法,显式为每个闭包绑定当前用户名参数:
def parallelStagesSoak = [:] pipeline { agent none parameters { string(name: "AGENT_COUNT", defaultValue: "1", trim: true, description: "How many Windows agents to run the test") string(name: "HNAS_PATH_WIN", defaultValue: "\\\\server1\\dir", trim: true, description: "path to share") string(name: "MAP_DRIVE_WIN", defaultValue: "P", trim: true, description: "drive letter to use in Windows") } stages { stage("soak_test") { steps { script { def agentCount = params.AGENT_COUNT.toInteger() for (int i=1; i<=agentCount; i++) { def currentUser = "test${i}" def stageName = "win_agent_${i}" // 用curry绑定当前用户值到闭包参数 parallelStagesSoak[stageName] = { user -> echo "WINUSER in test=${user}" node(label: "soak-client && windows") { stage(stageName) { bat "net use ${params.MAP_DRIVE_WIN}: ${params.HNAS_PATH_WIN} /user:domain1\\${user} test" } } }.curry(currentUser) } parallel parallelStagesSoak } } } } }
核心原理
Groovy闭包默认捕获变量的引用而非当前值。原代码中所有闭包都指向同一个WINUSER变量,循环结束后该变量的值是最后一次循环的结果。通过在每次循环中创建独立的局部变量(如currentUser),让每个闭包持有不同的变量引用,就能确保每个阶段获取到正确的用户名。
内容的提问来源于stack exchange,提问作者Ian Mayoh
相关产品推荐
相关产品推荐

