Jenkins Pipeline共享库跨阶段传递Groovy对象报错问题咨询
问题原因
这个报错由Jenkins Pipeline的CPS引擎序列化机制限制导致:
- 自定义Groovy类实例跨Pipeline阶段传递时,Jenkins会尝试将对象序列化后持久化,阶段恢复时再反序列化。你的Image类构造时传入了Pipeline上下文(也就是实例化时传的
this,即当前WorkflowScript实例),这个上下文本身不可序列化,序列化后上下文引用断裂,CPS引擎尝试绑定闭包时就会抛出$()方法找不到的异常。 - 你在Jenkinsfile中没有提前声明
base_python变量,直接在stage的script块中定义全局变量,也会放大跨阶段对象恢复的失败概率。
可行解决方案
方案1:修改自定义类兼容序列化(推荐,可正常跨阶段传对象)
- 让Image类实现
Serializable接口,将持有Pipeline上下文的成员变量标记为transient(瞬态,不参与序列化流程),同时增加反序列化钩子,在对象恢复时重新绑定上下文。
共享库Image类修改示例:package com class Image implements Serializable { // 标记pipeline步骤上下文为瞬态,不参与序列化 private transient def steps private String imageName private String imageVersion private String buildContext private String fullTag Image(def steps, String imageName, String imageVersion, String buildContext) { this.steps = steps this.imageName = imageName this.imageVersion = imageVersion this.buildContext = buildContext } def build() { steps.sh "docker build -t ${imageName}:${imageVersion} ${buildContext}" } def tag(String targetName, String registryAddr) { this.fullTag = "${registryAddr}/${targetName}" steps.sh "docker tag ${imageName}:${imageVersion} ${fullTag}" // tag方法返回当前实例本身,不要返回闭包/非序列化对象 return this } // 反序列化时自动执行的钩子方法,重新绑定pipeline上下文 private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject() this.steps = Script.current() } } - 修改Jenkinsfile,在pipeline块外部提前声明对象变量,明确变量类型:
// Jenkinsfile顶部提前声明变量 com.Image base_python pipeline { agent any // 其余pipeline配置保持不变 stages { stage('building base container image with python') { when { anyOf { expression {return params.BuildBaseImages} } } steps { script{ base_python = new Image(this, "${params.PYTHON_DOCKER_IMAGE_NAME}","${params.PYTHON_VERSION}", "centos7_baseimage_python/") base_python.build() alert.success "${params.PYTHON_DOCKER_IMAGE_NAME} builded" } } post { failure { error 'base镜像构建失败' } } } stage('tag base image') { when { anyOf { expression {return params.BuildBaseImages} } } steps { script { base_python = base_python.tag("${DOCKER_IMAGE_FOLDER}${params.PYTHON_DOCKER_IMAGE_NAME}${params.PYTHON_VERSION}","${DOCKER_REGISTRY}") } } } } }
方案2:跨阶段只传基础类型参数(兼容性最高,无需修改共享库)
如果不想调整自定义类结构,可以直接放弃跨阶段传递对象,只在环境变量中存储字符串类型的镜像核心参数,到对应阶段时重新实例化Image对象即可,完全规避序列化问题:
pipeline { agent any stages { stage('building base container image with python') { when { anyOf { expression {return params.BuildBaseImages} } } steps { script{ def base_python = new Image(this, "${params.PYTHON_DOCKER_IMAGE_NAME}","${params.PYTHON_VERSION}", "centos7_baseimage_python/") base_python.build() // 核心参数存入环境变量,环境变量仅支持字符串,天然可序列化 env.IMG_NAME = params.PYTHON_DOCKER_IMAGE_NAME env.IMG_VERSION = params.PYTHON_VERSION env.IMG_CONTEXT = "centos7_baseimage_python/" alert.success "${params.PYTHON_DOCKER_IMAGE_NAME} builded" } } post { failure { error 'base镜像构建失败' } } } stage('tag base image') { when { anyOf { expression {return params.BuildBaseImages} } } steps { script { // 重新实例化对象调用方法 def base_python = new Image(this, env.IMG_NAME, env.IMG_VERSION, env.IMG_CONTEXT) base_python.tag("${DOCKER_IMAGE_FOLDER}${env.IMG_NAME}${env.IMG_VERSION}","${DOCKER_REGISTRY}") } } } } }
注意事项
- 自定义Groovy类中如果定义了闭包类型、线程/流类型的成员变量,必须加
transient修饰,否则一定会触发序列化失败。 - 类中的业务方法不要返回非序列化对象,尽量返回当前实例或者基础数据类型。
内容的提问来源于stack exchange,提问作者b3n3w
相关产品推荐
相关产品推荐

