【k8s深入理解之 Scheme 补充-1】理解 Scheme 中资源的注册以及 GVK 和 go 结构体的映射
代码阅读引出的问题 —— 附录是详解
-
addKnownTypes、AddKnownTypeWithName、AddToGroupVersion 都是什么?
-
了解资源注册流程
- 下面代码是以 apps/v1 为例
- addKnownTypes 注册多个资源,实际上是调用 AddKnownTypeWithName 进行单个资源注册
- AddKnownTypeWithName 会利用反射,获取 go 结构体名称作为 Kind,建立 GVK 和 go 结构体的映射
- addKnownTypes 一般还会包含 AddToGroupVersion,注册一些无版本资源(可以理解为辅助资源,如Status 等)以及相关的转换、默认值填充函数等
- 下面的附录是详细介绍
// 路径-1 mod/k8s.io/api@v0.29.0/apps/v1/register.go
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
// 注册多个资源类型,跳转 路径-2
scheme.AddKnownTypes(SchemeGroupVersion,
&Deployment{},
&DeploymentList{},
&StatefulSet{},
&StatefulSetList{},
&DaemonSet{},
&DaemonSetList{},
&ReplicaSet{},
&ReplicaSetList{},
&ControllerRevision{},
&ControllerRevisionList{},
)
// 注册 GroupVersion,跳转 路径-3
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
// 路径-2 mod/k8s.io/apimachinery@v0.29.0/pkg/runtime/scheme.go
// AddKnownTypes registers all types passed in 'types' as being members of version 'version'.
// All objects passed to types should be pointers to structs. The name that go reports for
// the struct becomes the "kind" field when encoding. Version may not be empty - use the
// APIVersionInternal constant if you have a type that does not have a formal version.
func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {
s.addObservedVersion(gv)
for _, obj := range types {
// 获取 obj 的类型,此处可能是指针类型 如 *Deployment
t := reflect.TypeOf(obj)
if t.Kind() != reflect.Pointer {
panic("All types must be pointers to structs.")
}
// 获取指针指向的类型,如 Deployment 结构体
t = t.Elem()
// .Name 返回的是,结构体名称,也就是 Deployment —— 所以一般来说结构体名称就是 GVK 中的 Kind
s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
}
}
// 路径-3 mod/k8s.io/apimachinery@v0.29.0/pkg/apis/meta/v1/register.go
// 注册一些 无版本资源(可以理解为 稳定不易变化的版本,公共资源,其他 Group 都可以使用,主要用于辅助记录和查询作用 如 Status)
// 并注册一些,这些无版本资源或一些基础资源 的转换函数和默认值填充函数
// AddToGroupVersion registers common meta types into schemas.
func AddToGroupVersion(scheme *runtime.Scheme, groupVersion schema.GroupVersion) {
scheme.AddKnownTypeWithName(groupVersion.WithKind(WatchEventKind), &WatchEvent{})
scheme.AddKnownTypeWithName(
schema.GroupVersion{Group: groupVersion.Group, Version: runtime.APIVersionInternal}.WithKind(WatchEventKind),
&InternalEvent{},
)
// Supports legacy code paths, most callers should use metav1.ParameterCodec for now
scheme.AddKnownTypes(groupVersion, optionsTypes...)
// Register Unversioned types under their own special group
scheme.AddUnversionedTypes(Unversioned,
&Status{},
&APIVersions{},
&APIGroupList{},
&APIGroup{},
&APIResourceList{},
)
// register manually. This usually goes through the SchemeBuilder, which we cannot use here.
utilruntime.Must(RegisterConversions(scheme))
utilruntime.Must(RegisterDefaults(scheme))
}
附录1 | AddKnownTypes 和 AddKnownTypeWithName 和 AddToGroupVersion 有什么关系
在 Kubernetes 的代码中,AddKnownTypes
、AddKnownTypeWithName
和 AddToGroupVersion
是与 runtime.Scheme
相关的函数,主要用于注册自定义资源或类型,但它们关注的粒度和作用不同。
1. AddKnownTypes
- 功能:批量注册某个
GroupVersion
下的资源类型到runtime.Scheme
。 - 内部实现:会将所有提供的类型注册到
GroupVersionKind
(GVK) 中,其中Kind
默认是类型的结构体名。 - 调用场景:一般用于 API 类型的注册,比如注册 CRD 的 Go 类型。
- 特点
- 简化了多个类型的注册。
- 默认使用结构体名作为类型的
Kind
。
- 底层关联:
AddKnownTypes
内部会调用AddKnownTypeWithName
来完成注册。
2. AddKnownTypeWithName
- 功能:注册单个
runtime.Object
类型到特定的GroupVersionKind
。 - 特点
- 允许为类型指定自定义的
Kind
名称。 - 是更底层的实现,提供更精细的控制。
- 允许为类型指定自定义的
- 调用场景:需要为单个类型注册自定义的
Kind
时。 - 内部关联:
AddKnownTypes
只是对多个AddKnownTypeWithName
调用的封装。
3. AddToGroupVersion
-
功能:为一个
GroupVersion
添加元信息(GroupVersion
本身的注册),并且是类型注册的入口点。 -
典型实现
func AddToGroupVersion(scheme *runtime.Scheme, gv schema.GroupVersion) error { return scheme.AddGeneratedDeepCopyFuncs( deepCopyFuncs... ) }
-
作用
- 用于声明某个
GroupVersion
。 - 通常用于为 API 组注册基础信息(比如默认版本和元数据支持)。
- 用于声明某个
-
调用场景
- 用于 API 组的初始配置,确保该
GroupVersion
被声明并可以被进一步扩展(如通过AddKnownTypes
注册资源类型)。
- 用于 API 组的初始配置,确保该
三者之间的关系
函数名 | 粒度 | 主要功能 | 调用链关系 |
---|---|---|---|
AddToGroupVersion | GroupVersion 级别 | 注册 GroupVersion 的元信息,为资源类型的注册提供入口点。 | 一般最先调用(但也不是必须在第一位),用于初始化 GroupVersion |
AddKnownTypes | 批量类型级别 | 注册某个 GroupVersion 下的多个类型资源到 runtime.Scheme ,简化注册流程。 | 内部调用 AddKnownTypeWithName |
AddKnownTypeWithName | 单个类型级别 | 注册单个类型资源到 runtime.Scheme ,允许为资源指定自定义的 Kind 名称。 | 最底层的注册实现 |
示例:三者配合
假设你有一个自定义 API 组 example.com/v1
,其中包含一种资源 MyCustomResource
。
-
使用
AddToGroupVersion
:-
声明
example.com/v1
的元信息:
func AddToScheme(scheme *runtime.Scheme) error { gv := schema.GroupVersion{Group: "example.com", Version: "v1"} return AddToGroupVersion(scheme, gv) }
-
-
使用
AddKnownTypes
:-
注册所有相关资源类型:
func AddToScheme(scheme *runtime.Scheme) error { gv := schema.GroupVersion{Group: "example.com", Version: "v1"} scheme.AddKnownTypes(gv, &MyCustomResource{}, &MyCustomResourceList{}) return nil }
-
-
使用
AddKnownTypeWithName
:-
在需要时为资源指定自定义
Kind
名称:func AddToScheme(scheme *runtime.Scheme) error { gv := schema.GroupVersion{Group: "example.com", Version: "v1"} scheme.AddKnownTypeWithName(gv.WithKind("SpecialResource"), &MyCustomResource{}) return nil }
-
总结
AddToGroupVersion
是对 APIGroupVersion
的初始化,通常是注册流程的起点。AddKnownTypes
是高层次的封装,用于批量注册资源类型。AddKnownTypeWithName
是低层次的实现,用于更精细地注册单个资源类型,特别是需要自定义Kind
的场景。
附录2 | reflect.TypeOf(obj).Elem()获取指针指向的go类型
-
reflect.TypeOf(obj).Elem().Name()
获取的是obj
指向的实际类型的名称。具体来说,它返回的是 指针所指向的类型的名称。解释:
reflect.TypeOf(obj)
获取的是obj
的反射类型,如果obj
是指针类型,reflect.TypeOf(obj)
返回的是指针类型。.Elem()
用来获取指针所指向的元素类型,即返回指针所指向的实际类型。.Name()
返回该类型的名称。
举个例子:
假设你有以下代码:
package main import ( "fmt" "reflect" ) type MyStruct struct { Name string } func main() { obj := &MyStruct{Name: "K8s"} // 获取 obj 的反射类型,即 *MyStruct 类型 ptrType := reflect.TypeOf(obj) // 获取 obj 指针所指向的元素类型,即 MyStruct 类型 elemType := ptrType.Elem() // 获取元素类型的名称 fmt.Println("elemType.Name():", elemType.Name()) // 输出 MyStruct }
输出:
elemType.Name(): MyStruct
解释:
reflect.TypeOf(obj)
获取的是*MyStruct
类型。Elem()
获取的是MyStruct
类型(即*MyStruct
指向的类型)。Name()
获取的是MyStruct
的类型名称,因此输出MyStruct
。
总结:
reflect.TypeOf(obj).Elem().Name()
获取的是指针类型所指向的元素类型的名称,通常用于获取结构体类型的名称。