Jenkins调用withCredentials传不同凭证ID遇序列化错误求助
Jenkins流水线序列化错误:带凭证的自定义函数调用失败
需要在不同云环境、不同订阅的机器上执行命令,使用对应凭证访问。但调用自定义函数并向withCredentials()传递变量时持续报错,尝试过在函数定义顶部添加@NONCPS、将函数置于管道作用域之外、使用$variable/${variable}等变量写法,也参考社区方案调整,均出现类似序列化错误。
示例脚本
def server_inventory='''[ { "agentName": "agent01", "machineName": "dev-agent01", "instanceId": "i-xxxxx", "cloud": "aws", "accountName": "DevQa", "accountId": xxxxxxxx, "directoryId": "", "resourceGoup": "", "region": "us-east-1", "credential": "awsDevQA" }, { "agentName": "agent20", "machineName": "pp-agent20", "instanceId": "", "cloud": "azure", "accountName": "pp", "accountId": xxxxxxxx, "directoryId": "xxxxxx", "resourceGoup": "rg-pp", "region": "eastus", "credential": "azPP" } ]''' def gl_confData = readJSON(text: server_inventory) stages{ stage('agent commands'){ steps{ script{ def startAzureVm = {vmName, resourceGroup, jenkinsCred, tenantId -> withCredentials([usernamePassword(credentialsId: jenkinsCred, passwordVariable: 'AZURE_CLIENT_SECRET', usernameVariable: 'AZURE_CLIENT_ID')]){ sh script: 'az login --service-principal -u $AZURE_CLIENT_ID -p $AZURE_CLIENT_SECRET --tenant $tenantId' sh '<run other commands>' } } def getVmDetails = {agentName -> return gl_confData.find{it.agentName == agentName} } def job = Jenkins.instance.getItemByFullName("${JOB_NAME}") def agentParam = job.getProperty(hudson.model.ParametersDefinitionProperty.class).getParameterDefinition('Agent') def agentChoices = agentParam.getChoices() agentChoices.each{ str -> def agent = getVmDetails(str) if(agent){ if(agent.cloud == 'azure'){ startAzureVm(agent.machineName, agent.resourceGoup, agent.credential, agent.directoryId) } } } } } } }
错误信息
> 异常发生位置: > 字段 org.jenkinsci.plugins.pipeline.modeldefinition.withscript.WithScriptScript.script > 对象 org.jenkinsci.plugins.pipeline.modeldefinition.agent.impl.LabelScript@715df3b8 > 字段 groovy.lang.Closure.delegate > 对象 org.jenkinsci.plugins.workflow.cps.CpsClosure2@4f26f1b8 > 字段 groovy.lang.Closure.delegate > 对象 org.jenkinsci.plugins.workflow.cps.CpsClosure2@36918bf9 > 字段 org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.closures > 对象 org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@7511cc00 > 对象 org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@7511cc00 附加信息: > org.jenkinsci.plugins.workflow.actions.ErrorAction$ErrorId: 085e099e-d54d-4c29-ad88-7805002639d6 原因: java.io.NotSerializableException: hudson.model.Hudson at PluginClassLoader for workflow-support//org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:278) at PluginClassLoader for workflow-support//org.jboss.marshalling.river.BlockMarshaller.doWriteObject(BlockMarshaller.java:65) at PluginClassLoader for workflow-support//org.jboss.marshalling.river.BlockMarshaller.writeObject(BlockMarshaller.java:56) ...(省略后续栈跟踪)
问题根源与修复方案
问题根源
错误核心是java.io.NotSerializableException: hudson.model.Hudson,原因:
Jenkins.instance是不可序列化对象,流水线执行到需要暂停(如等待节点、保存状态)时会序列化整个脚本上下文,该对象被闭包捕获后无法序列化,触发报错。- 自定义闭包默认会捕获外部作用域的变量/对象,若包含不可序列化内容,也会导致序列化失败。
修复步骤
将不可序列化操作移至@NonCPS函数
@NonCPS标记的函数不参与流水线序列化,适合处理Jenkins内部对象操作,注意该函数内不能调用流水线步骤(如sh、withCredentials)。避免闭包捕获不必要的上下文
将自定义闭包改为独立函数,确保只引用可序列化变量(如字符串、JSON对象)。修正脚本语法错误
原脚本存在startAzureVm闭包未闭合、Sh大小写错误、JSON数组未闭合等问题,需先修正。
修正后的完整脚本
def server_inventory='''[ { "agentName": "agent01", "machineName": "dev-agent01", "instanceId": "i-xxxxx", "cloud": "aws", "accountName": "DevQa", "accountId": xxxxxxxx, "directoryId": "", "resourceGoup": "", "region": "us-east-1", "credential": "awsDevQA" }, { "agentName": "agent20", "machineName": "pp-agent20", "instanceId": "", "cloud": "azure", "accountName": "pp", "accountId": xxxxxxxx, "directoryId": "xxxxxx", "resourceGoup": "rg-pp", "region": "eastus", "credential": "azPP" } ]''' def gl_confData = readJSON(text: server_inventory) // 提取不可序列化操作到@NonCPS函数 @NonCPS def getAgentChoices() { def job = Jenkins.instance.getItemByFullName("${JOB_NAME}") def agentParam = job.getProperty(hudson.model.ParametersDefinitionProperty.class).getParameterDefinition('Agent') return agentParam.getChoices() } // 改为独立函数,避免闭包捕获上下文 def startAzureVm(String vmName, String resourceGroup, String jenkinsCred, String tenantId) { withCredentials([usernamePassword(credentialsId: jenkinsCred, passwordVariable: 'AZURE_CLIENT_SECRET', usernameVariable: 'AZURE_CLIENT_ID')]){ sh script: 'az login --service-principal -u $AZURE_CLIENT_ID -p $AZURE_CLIENT_SECRET --tenant $tenantId' sh '<run other commands>' } } def getVmDetails(String agentName) { return gl_confData.find{it.agentName == agentName} } stages{ stage('agent commands'){ steps{ script{ def agentChoices = getAgentChoices() agentChoices.each{ str -> def agent = getVmDetails(str) if(agent){ if(agent.cloud == 'azure'){ startAzureVm(agent.machineName, agent.resourceGoup, agent.credential, agent.directoryId) } } } } } } }
内容的提问来源于stack exchange,提问作者PanzerRage
相关产品推荐
相关产品推荐

