使用Go-Client创建Kubernetes CRD对象失败:Protobuf编码错误
我之前也遇到过一模一样的问题!kubectl能正常操作CRD,但用client-go写代码创建就报这个protobuf的错,原因其实是现在的client-go默认优先使用Protobuf序列化请求,而你的自定义CRD结构体没有实现proto.Message接口——反观Kubernetes的内置资源(比如Pod),官方已经帮它们生成了对应的Protobuf代码和接口实现,所以完全没问题。
下面给你两种可行的解决思路,选一个适合你的就行:
方法一:为CRD生成Protobuf代码(推荐,符合K8s最佳实践)
这是长期来看更规范的方案,让你的CRD完全支持Protobuf序列化:
给Go结构体补全Protobuf标签
你现有的MyEndpoint结构体里,TypeMeta字段缺少Protobuf的tag,补上之后应该是这样:type MyEndpoint struct { metav1.TypeMeta `json:",inline" protobuf:"bytes,0,opt,name=typeMeta"` // Standard object's metadata. // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata // +optional metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` }用controller-gen生成Protobuf代码
如果你是用controller-tools来管理CRD(就像sample-controller那样),在项目根目录运行下面的命令,生成对应的.pb.go文件:controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."要是你没有
hack/boilerplate.go.txt这个文件,直接去掉headerFile参数就行:controller-gen object paths="./..."生成的文件会自动让你的结构体实现
proto.Message接口,彻底解决序列化问题。确保CRD YAML支持Protobuf(可选)
如果想让K8s集群端也支持Protobuf存储,在CRD的YAML定义里给对应版本加上mediaType字段:spec: versions: - name: v1 served: true storage: true mediaType: application/vnd.kubernetes.protobuf # 加上这个 schema: openAPIV3Schema: type: object properties: metadata: type: object
方法二:强制Go Client使用JSON序列化(快速临时方案)
如果不想折腾Protobuf,直接修改client的配置,强制用JSON序列化请求,绕过Protobuf的检查:
在创建你的自定义clientset之前,修改rest.Config的序列化器:
import ( "k8s.io/client-go/rest" "k8s.io/client-go/kubernetes/scheme" ) // 假设你已经通过InClusterConfig或者BuildConfigFromFlags拿到了config config := ... // 你的rest.Config实例 // 替换默认的序列化器为纯JSON的,不带Protobuf支持 config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() // 然后创建你的自定义clientset myclientset, err := myclientset.NewForConfig(config) if err != nil { // 处理错误 }
这样修改后,client-go就只会用JSON发送请求,和kubectl的行为一致,自然就不会报Protobuf的错了。
为什么kubectl能正常工作?
因为kubectl默认使用JSON格式和API Server交互,不会主动尝试Protobuf序列化,所以即使你的CRD没支持Protobuf,kubectl也能正常创建/操作对象。
内容的提问来源于stack exchange,提问作者Toby Xu

