You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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是不可序列化对象,流水线执行到需要暂停(如等待节点、保存状态)时会序列化整个脚本上下文,该对象被闭包捕获后无法序列化,触发报错。
  • 自定义闭包默认会捕获外部作用域的变量/对象,若包含不可序列化内容,也会导致序列化失败。

修复步骤

  1. 将不可序列化操作移至@NonCPS函数
    @NonCPS标记的函数不参与流水线序列化,适合处理Jenkins内部对象操作,注意该函数内不能调用流水线步骤(如sh、withCredentials)。

  2. 避免闭包捕获不必要的上下文
    将自定义闭包改为独立函数,确保只引用可序列化变量(如字符串、JSON对象)。

  3. 修正脚本语法错误
    原脚本存在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 18:47:01