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组成,如下代码所示:

  1. type Scheme struct {
  2. // versionMap allows one to figure out the go type of an object with
  3. // the given version and name.
  4. // 存储GVK与Type的映射关系
  5. gvkToType map[schema.GroupVersionKind]reflect.Type
  6. // typeToGroupVersion allows one to find metadata for a given go object.
  7. // The reflect.Type we index by should *not* be a pointer.
  8. // 存储Type与GVK的映射关系,一个Type会对应一个或多个GVK
  9. typeToGVK map[reflect.Type][]schema.GroupVersionKind
  10. // unversionedTypes are transformed without conversion in ConvertToVersion.
  11. // 存储UnversionedType与GVK的映射关系
  12. unversionedTypes map[reflect.Type]schema.GroupVersionKind
  13. // unversionedKinds are the names of kinds that can be created in the context of any group
  14. // or version
  15. // TODO: resolve the status of unversioned types.
  16. // 存储Kind(资源种类)名称与UnversionedType的映射关系
  17. unversionedKinds map[string]reflect.Type
  18. ...
  19. }

Scheme资源注册表通过map实现映射关系,高效实现了正向和反向检索,从Scheme资源注册表中检索某个GVK的Type,时间复杂度为O(1)。

Scheme资源注册表在Kubernetes系统中属于非常核心的数据结构,直接阅读源码十分晦涩,通过下面的代码理解Scheme资源注册表。

  1. package main
  2. import (
  3. appsv1 "k8s.io/api/apps/v1"
  4. corev1 "k8s.io/api/core/v1"
  5. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  6. "k8s.io/apimachinery/pkg/runtime"
  7. "k8s.io/apimachinery/pkg/runtime/schema"
  8. )
  9. func main() {
  10. // KnownType External
  11. coreGV := schema.GroupVersion{Group: "", Version: "v1"}
  12. extensionsGV := schema.GroupVersion{Group: "extensions", Version: "v1beta1"}
  13. // KnownType internal
  14. coreInternalGV := schema.GroupVersion{Group: "", Version: runtime.APIVersionInternal}
  15. // UnversionedType
  16. unversioned := schema.GroupVersion{Group: "", Version: "v1"}
  17. scheme := runtime.NewScheme()
  18. scheme.AddKnownTypes(coreGV, &corev1.Pod{})
  19. scheme.AddKnownTypes(extensionsGV, &appsv1.DaemonSet{})
  20. scheme.AddKnownTypes(coreInternalGV, &corev1.Pod{})
  21. scheme.AddUnversionedTypes(unversioned, &metav1.Status{})
  22. }

上述代码中,首先定义了两种类型的GV(Group,Version),KnownType类型有coreGV,extensionGV、coreInternalGV对象,其中coreInternalGV属于内部版本(即runtime.APIInternalVersion),而UnversionedType类型有Unversioned对象。
AddKnownTypes和AddUnversionedTypes分别将KnownTypes和UnversionedTypes添加到Scheme。

在上面的代码示例中,我们注入了Pod、DeamonSet、Pod(内部版本)及Status(无版本资源类型)对象,得到以下的映射关系:

  1. gvkToType ------------------------------------------------------
  2. | map[schema.GroupVersionKind]reflect.Type |
  3. ------------------------------------------------------
  4. | /v1,Kind=Pod : v1.Pod |
  5. | extensions/v1beta1,Kind=DeamonSet : v1.DeamonSet |
  6. | /_internal,Kind=Pod, : v1.Pod |
  7. | /v1,Kind=Status : v1.Status |
  8. ------------------------------------------------------
  9. typeToGVK ------------------------------------------------------
  10. | map[reflect.Type]schema.GroupVersionKind |
  11. ------------------------------------------------------
  12. | v1.Status : [/v1,Kind=Status] |
  13. | v1.Pod : [/v1,Kind=Pod, /_internal,Kind=Pod] |
  14. | v1.DeamonSet : [extensions/v1beta1,Kind=DeamonSet] |
  15. ------------------------------------------------------
  16. unversionedTypes ------------------------------------------------------
  17. | map[schema.GroupVersionKind]reflect.Type |
  18. ------------------------------------------------------
  19. | v1.Status: /v1,Kind=Status |
  20. ------------------------------------------------------
  21. unversionedKinds ------------------------------------------------------
  22. | map[schema.GroupVersionKind]reflect.Type |
  23. ------------------------------------------------------
  24. | v1.Status: /v1,Kind=Status |
  25. ------------------------------------------------------

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

  1. func (s *Scheme) AddUnversionedTypes(version schema.GroupVersion, types ...Object) {
  2. s.addObservedVersion(version)
  3. s.AddKnownTypes(version, types...) // 加入前两个Map
  4. for _, obj := range types {
  5. t := reflect.TypeOf(obj).Elem()
  6. gvk := version.WithKind(t.Name())
  7. s.unversionedTypes[t] = gvk // 加入第三个Map
  8. if old, ok := s.unversionedKinds[gvk.Kind]; ok && t != old {
  9. 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))
  10. }
  11. s.unversionedKinds[gvk.Kind] = t // 加入第四个Map
  12. }
  13. }

资源注册表注册方法

不同资源的注册方法不同,如下所示:

  • scheme.AddUnversionedTypes:注册UnversionedTypes资源类型
  • scheme.AddKnownTypes:注册KnownTypes资源类型
  • scheme.AddKnownTypesWithName:注册KnownTypes资源类型,须指定资源的Kind资源种类名称
  1. func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {
  2. s.addObservedVersion(gv)
  3. for _, obj := range types {
  4. t := reflect.TypeOf(obj)
  5. if t.Kind() != reflect.Ptr {
  6. panic("All types must be pointers to structs.")
  7. }
  8. t = t.Elem()
  9. s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
  10. }
  11. }

资源注册表查询方法

在运行过程中,Kube-apiserver组件常对Scheme资源注册表进行查询,他提供了如下方法:

  • scheme.KnownTypes:查询注册表中指定GV下的资源类型

    1. // KnownTypes returns the types known for the given version.
    2. func (s *Scheme) KnownTypes(gv schema.GroupVersion) map[string]reflect.Type {
    3. types := make(map[string]reflect.Type)
    4. for gvk, t := range s.gvkToType {
    5. if gv != gvk.GroupVersion() {
    6. continue
    7. }
    8. types[gvk.Kind] = t
    9. }
    10. return types
    11. }
  • scheme.AllKnownTypes:查询所有GVK下的资源类型 ```go // AllKnownTypes returns the all known types. func (s *Scheme) AllKnownTypes() map[schema.GroupVersionKind]reflect.Type { return s.gvkToType }

  1. - scheme.ObjectKinds:查询资源对象对应的GVK,一个资源对象可能存在多个GVK
  2. ```go
  3. // ObjectKinds returns all possible group,version,kind of the go object, true if the
  4. // object is considered unversioned, or an error if it's not a pointer or is unregistered.
  5. func (s *Scheme) ObjectKinds(obj Object) ([]schema.GroupVersionKind, bool, error) {
  6. // Unstructured objects are always considered to have their declared GVK
  7. if _, ok := obj.(Unstructured); ok {
  8. // we require that the GVK be populated in order to recognize the object
  9. gvk := obj.GetObjectKind().GroupVersionKind()
  10. if len(gvk.Kind) == 0 {
  11. return nil, false, NewMissingKindErr("unstructured object has no kind")
  12. }
  13. if len(gvk.Version) == 0 {
  14. return nil, false, NewMissingVersionErr("unstructured object has no version")
  15. }
  16. return []schema.GroupVersionKind{gvk}, false, nil
  17. }
  18. v, err := conversion.EnforcePtr(obj)
  19. if err != nil {
  20. return nil, false, err
  21. }
  22. t := v.Type()
  23. gvks, ok := s.typeToGVK[t]
  24. if !ok {
  25. return nil, false, NewNotRegisteredErrForType(s.schemeName, t)
  26. }
  27. _, unversionedType := s.unversionedTypes[t]
  28. return gvks, unversionedType, nil
  29. }
  • scheme.New:查询GVK所对应的资源对象

    1. // New returns a new API object of the given version and name, or an error if it hasn't
    2. // been registered. The version and kind fields must be specified.
    3. func (s *Scheme) New(kind schema.GroupVersionKind) (Object, error) {
    4. if t, exists := s.gvkToType[kind]; exists {
    5. return reflect.New(t).Interface().(Object), nil
    6. }
    7. if t, exists := s.unversionedKinds[kind.Kind]; exists {
    8. return reflect.New(t).Interface().(Object), nil
    9. }
    10. return nil, NewNotRegisteredErrForKind(s.schemeName, kind)
    11. }
  • scheme.IsGroupRegistered:判断资源组是否已经注册

    1. // IsGroupRegistered returns true if types for the group have been registered with the scheme
    2. func (s *Scheme) IsGroupRegistered(group string) bool {
    3. for _, observedVersion := range s.observedVersions {
    4. if observedVersion.Group == group {
    5. return true
    6. }
    7. }
    8. return false
    9. }
  • scheme.IsVersionRegistered:判断指定的GV是否注册

    1. // IsVersionRegistered returns true if types for the version have been registered with the scheme
    2. func (s *Scheme) IsVersionRegistered(version schema.GroupVersion) bool {
    3. for _, observedVersion := range s.observedVersions {
    4. if observedVersion == version {
    5. return true
    6. }
    7. }
    8. return false
    9. }
  • scheme.Recognizes:判断指定的GVK是否已经注册

    1. // Recognizes returns true if the scheme is able to handle the provided group,version,kind
    2. // of an object.
    3. func (s *Scheme) Recognizes(gvk schema.GroupVersionKind) bool {
    4. _, exists := s.gvkToType[gvk]
    5. return exists
    6. }
  • scheme.IsUnversioned:判断指定的资源对象是否属于UnversionedType类型

    1. func (s *Scheme) IsUnversioned(obj Object) (bool, bool) {
    2. v, err := conversion.EnforcePtr(obj)
    3. if err != nil {
    4. return false, false
    5. }
    6. t := v.Type()
    7. if _, ok := s.typeToGVK[t]; !ok {
    8. return false, false
    9. }
    10. _, ok := s.unversionedTypes[t]
    11. return ok, true
    12. }