Kubernetes拥有众多资源,每一种资源就是一个资源类型,这些资源需要有统一的注册、存储、查询、管理机制。
目前Kubernetes系统中所有的资源类型都已经注册到Scheme资源注册表中,其实一个内存型的资源注册表,拥有如下特点。
- 支持注册多种资源类型,包括内部版本和外部版本
- 支持多种版本转换机制
- 支持不同资源的序列化和反序列化机制
Scheme支持两种资源类型(Type)的注册,分别是UnversionedType和KnownType资源类型:
- UnversionedType:无版本资源类型,早期Kubernetes系统概念,主要应用于没有版本的资源对象,不需要进行转换。在目前的Kubernetes发型版本中,无版本类型已经被弱化,几乎所有的资源对象都拥有版本。在metav1元数据中还有部分类型既属于meta.k8s.io/v1又属于UnversionedType。例如:metav1.Status、metav1.APIVersion、metav1.APIGroupList、metav1.APIGroup、metav1.APIResourceList。
- 在资源注册表中,通过scheme.AddKnownTypes方法进行注册
- KnownType:是目前Kubernetes最常用的资源类型,也称为“拥有版本的资源类型”
- 在资源注册表中,通过scheme.AddKnownTypes方法进行注册
Scheme资源注册表数据结构
Scheme资源注册表的数据结构主要由4个map组成,如下代码所示:
type Scheme struct {// versionMap allows one to figure out the go type of an object with// the given version and name.// 存储GVK与Type的映射关系gvkToType map[schema.GroupVersionKind]reflect.Type// typeToGroupVersion allows one to find metadata for a given go object.// The reflect.Type we index by should *not* be a pointer.// 存储Type与GVK的映射关系,一个Type会对应一个或多个GVKtypeToGVK map[reflect.Type][]schema.GroupVersionKind// unversionedTypes are transformed without conversion in ConvertToVersion.// 存储UnversionedType与GVK的映射关系unversionedTypes map[reflect.Type]schema.GroupVersionKind// unversionedKinds are the names of kinds that can be created in the context of any group// or version// TODO: resolve the status of unversioned types.// 存储Kind(资源种类)名称与UnversionedType的映射关系unversionedKinds map[string]reflect.Type...}
Scheme资源注册表通过map实现映射关系,高效实现了正向和反向检索,从Scheme资源注册表中检索某个GVK的Type,时间复杂度为O(1)。
Scheme资源注册表在Kubernetes系统中属于非常核心的数据结构,直接阅读源码十分晦涩,通过下面的代码理解Scheme资源注册表。
package mainimport (appsv1 "k8s.io/api/apps/v1"corev1 "k8s.io/api/core/v1"metav1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/apimachinery/pkg/runtime""k8s.io/apimachinery/pkg/runtime/schema")func main() {// KnownType ExternalcoreGV := schema.GroupVersion{Group: "", Version: "v1"}extensionsGV := schema.GroupVersion{Group: "extensions", Version: "v1beta1"}// KnownType internalcoreInternalGV := schema.GroupVersion{Group: "", Version: runtime.APIVersionInternal}// UnversionedTypeunversioned := schema.GroupVersion{Group: "", Version: "v1"}scheme := runtime.NewScheme()scheme.AddKnownTypes(coreGV, &corev1.Pod{})scheme.AddKnownTypes(extensionsGV, &appsv1.DaemonSet{})scheme.AddKnownTypes(coreInternalGV, &corev1.Pod{})scheme.AddUnversionedTypes(unversioned, &metav1.Status{})}
上述代码中,首先定义了两种类型的GV(Group,Version),KnownType类型有coreGV,extensionGV、coreInternalGV对象,其中coreInternalGV属于内部版本(即runtime.APIInternalVersion),而UnversionedType类型有Unversioned对象。
AddKnownTypes和AddUnversionedTypes分别将KnownTypes和UnversionedTypes添加到Scheme。
在上面的代码示例中,我们注入了Pod、DeamonSet、Pod(内部版本)及Status(无版本资源类型)对象,得到以下的映射关系:
gvkToType ------------------------------------------------------| map[schema.GroupVersionKind]reflect.Type |------------------------------------------------------| /v1,Kind=Pod : v1.Pod || extensions/v1beta1,Kind=DeamonSet : v1.DeamonSet || /_internal,Kind=Pod, : v1.Pod || /v1,Kind=Status : v1.Status |------------------------------------------------------typeToGVK ------------------------------------------------------| map[reflect.Type]schema.GroupVersionKind |------------------------------------------------------| v1.Status : [/v1,Kind=Status] || v1.Pod : [/v1,Kind=Pod, /_internal,Kind=Pod] || v1.DeamonSet : [extensions/v1beta1,Kind=DeamonSet] |------------------------------------------------------unversionedTypes ------------------------------------------------------| map[schema.GroupVersionKind]reflect.Type |------------------------------------------------------| v1.Status: /v1,Kind=Status |------------------------------------------------------unversionedKinds ------------------------------------------------------| map[schema.GroupVersionKind]reflect.Type |------------------------------------------------------| v1.Status: /v1,Kind=Status |------------------------------------------------------
GVK在Scheme中以<group>/<version>,Kind=<kind>的形式存在,对于Kind字段,如果在注册时不指定该字段的名称,那么默认使用类型的名称,例如corev1.Pod类型,通过reflect机制获取资源类型的名称,那么它的资源种类Kind=Pod。
资源类型在Scheme资源注册表中以Go reflect.Type形式存在。
另外,UnversionedType类型的对象在通过scheme.AddUnversionedTypes方法注册时,会同时存在于4个map中,代码示例如下:
代码路径:vendor/k8s.io/apimachinery/pkg/runtime/scheme.go
func (s *Scheme) AddUnversionedTypes(version schema.GroupVersion, types ...Object) {s.addObservedVersion(version)s.AddKnownTypes(version, types...) // 加入前两个Mapfor _, obj := range types {t := reflect.TypeOf(obj).Elem()gvk := version.WithKind(t.Name())s.unversionedTypes[t] = gvk // 加入第三个Mapif old, ok := s.unversionedKinds[gvk.Kind]; ok && t != old {panic(fmt.Sprintf("%v.%v has already been registered as unversioned kind %q - kind name must be unique in scheme %q", old.PkgPath(), old.Name(), gvk, s.schemeName))}s.unversionedKinds[gvk.Kind] = t // 加入第四个Map}}
资源注册表注册方法
不同资源的注册方法不同,如下所示:
- scheme.AddUnversionedTypes:注册UnversionedTypes资源类型
- scheme.AddKnownTypes:注册KnownTypes资源类型
- scheme.AddKnownTypesWithName:注册KnownTypes资源类型,须指定资源的Kind资源种类名称
func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {s.addObservedVersion(gv)for _, obj := range types {t := reflect.TypeOf(obj)if t.Kind() != reflect.Ptr {panic("All types must be pointers to structs.")}t = t.Elem()s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)}}
资源注册表查询方法
在运行过程中,Kube-apiserver组件常对Scheme资源注册表进行查询,他提供了如下方法:
scheme.KnownTypes:查询注册表中指定GV下的资源类型
// KnownTypes returns the types known for the given version.func (s *Scheme) KnownTypes(gv schema.GroupVersion) map[string]reflect.Type {types := make(map[string]reflect.Type)for gvk, t := range s.gvkToType {if gv != gvk.GroupVersion() {continue}types[gvk.Kind] = t}return types}
scheme.AllKnownTypes:查询所有GVK下的资源类型 ```go // AllKnownTypes returns the all known types. func (s *Scheme) AllKnownTypes() map[schema.GroupVersionKind]reflect.Type { return s.gvkToType }
- scheme.ObjectKinds:查询资源对象对应的GVK,一个资源对象可能存在多个GVK```go// ObjectKinds returns all possible group,version,kind of the go object, true if the// object is considered unversioned, or an error if it's not a pointer or is unregistered.func (s *Scheme) ObjectKinds(obj Object) ([]schema.GroupVersionKind, bool, error) {// Unstructured objects are always considered to have their declared GVKif _, ok := obj.(Unstructured); ok {// we require that the GVK be populated in order to recognize the objectgvk := obj.GetObjectKind().GroupVersionKind()if len(gvk.Kind) == 0 {return nil, false, NewMissingKindErr("unstructured object has no kind")}if len(gvk.Version) == 0 {return nil, false, NewMissingVersionErr("unstructured object has no version")}return []schema.GroupVersionKind{gvk}, false, nil}v, err := conversion.EnforcePtr(obj)if err != nil {return nil, false, err}t := v.Type()gvks, ok := s.typeToGVK[t]if !ok {return nil, false, NewNotRegisteredErrForType(s.schemeName, t)}_, unversionedType := s.unversionedTypes[t]return gvks, unversionedType, nil}
scheme.New:查询GVK所对应的资源对象
// New returns a new API object of the given version and name, or an error if it hasn't// been registered. The version and kind fields must be specified.func (s *Scheme) New(kind schema.GroupVersionKind) (Object, error) {if t, exists := s.gvkToType[kind]; exists {return reflect.New(t).Interface().(Object), nil}if t, exists := s.unversionedKinds[kind.Kind]; exists {return reflect.New(t).Interface().(Object), nil}return nil, NewNotRegisteredErrForKind(s.schemeName, kind)}
scheme.IsGroupRegistered:判断资源组是否已经注册
// IsGroupRegistered returns true if types for the group have been registered with the schemefunc (s *Scheme) IsGroupRegistered(group string) bool {for _, observedVersion := range s.observedVersions {if observedVersion.Group == group {return true}}return false}
scheme.IsVersionRegistered:判断指定的GV是否注册
// IsVersionRegistered returns true if types for the version have been registered with the schemefunc (s *Scheme) IsVersionRegistered(version schema.GroupVersion) bool {for _, observedVersion := range s.observedVersions {if observedVersion == version {return true}}return false}
scheme.Recognizes:判断指定的GVK是否已经注册
// Recognizes returns true if the scheme is able to handle the provided group,version,kind// of an object.func (s *Scheme) Recognizes(gvk schema.GroupVersionKind) bool {_, exists := s.gvkToType[gvk]return exists}
scheme.IsUnversioned:判断指定的资源对象是否属于UnversionedType类型
func (s *Scheme) IsUnversioned(obj Object) (bool, bool) {v, err := conversion.EnforcePtr(obj)if err != nil {return false, false}t := v.Type()if _, ok := s.typeToGVK[t]; !ok {return false, false}_, ok := s.unversionedTypes[t]return ok, true}
