经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Kubernetes » 查看文章
Kubernetes:kube-apiserver 之 scheme(一)
来源:cnblogs  作者:lubanseven  时间:2023/10/18 8:53:21  对本文有异议

0. 前言

在进入 kube-apiserver 源码分析前,有一个非常重要的概念需要了解甚至熟悉的:资源注册表(scheme)。

Kubernetes 中一切皆资源,管理的是资源,创建、更新、删除的是资源。如何对庞杂的资源进行管理就成了一件大事。Kubernetes 通过引入 scheme 资源注册表,将资源信息注册到资源注册表,各个组件通过索引资源注册表实现资源的管理。

1. 介绍

直接开始阅读 kube-apiserver scheme 部分的源码是比较困难的,这里举代码示例如下:

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

示例中有几点需要关注。

  1. 通过 runtime.NewScheme 创建 scheme 实例:
  1. func NewScheme() *Scheme {
  2. s := &Scheme{
  3. gvkToType: map[schema.GroupVersionKind]reflect.Type{},
  4. typeToGVK: map[reflect.Type][]schema.GroupVersionKind{},
  5. unversionedTypes: map[reflect.Type]schema.GroupVersionKind{},
  6. unversionedKinds: map[string]reflect.Type{},
  7. fieldLabelConversionFuncs: map[schema.GroupVersionKind]FieldLabelConversionFunc{},
  8. defaulterFuncs: map[reflect.Type]func(interface{}){},
  9. versionPriority: map[string][]string{},
  10. schemeName: naming.GetNameFromCallsite(internalPackages...),
  11. }
  12. s.converter = conversion.NewConverter(nil)
  13. ...
  14. return s
  15. }

Scheme 的结构体有四个 field 需要重点关注:

  • gvkToType: 记录的是 schema.GroupVersionKind 和资源类型的映射关系。
  • typeToGVK: 记录的是资源类型和 schema.GroupVersionKind 的映射关系。
  • unversionedTypes: 记录的是无版本资源类型和 schema.GroupVersionKind 的映射关系。
  • unversionedKinds: 记录的是资源种类 Kind 和无版本资源类型的映射关系。

还有一个 Scheme.converter 也挺重要,这里先跳过不讲。

  1. 调用 SchemeAddKnownTypesAddUnversionedTypes 方法建立资源 Group/Version/Kind 和资源类型的相互映射关系。

AddKnownTypes 为例,函数通过反射 reflect.TypeOf(obj) 获得对象 runtime.Object 的资源类型。这里,对象是资源的接口,可实现资源和对象的相互转换。

  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.Pointer {
  6. panic("All types must be pointers to structs.")
  7. }
  8. t = t.Elem()
  9. s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
  10. }
  11. }
  12. func (gv GroupVersion) WithKind(kind string) GroupVersionKind {
  13. return GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}
  14. }
  15. func (s *Scheme) AddKnownTypeWithName(gvk schema.GroupVersionKind, obj Object) {
  16. ...
  17. t := reflect.TypeOf(obj)
  18. s.gvkToType[gvk] = t
  19. }

然后调用 gv.WithKind(t.Name()) 转换为 GroupVersionKind,可以看出上面反射的资源类型作为 kind 赋给 GroupVersionKind

最后调用 AddKnownTypeWithNameschema.GroupVersionKind 和资源类型写入 Scheme.gvkToType

执行代码结果如下:

  1. {
  2. map[
  3. { __internal Pod}:0xb46180
  4. { v1 Pod}:0xb46180
  5. { v1 Status}:0xb56d40
  6. {extensions v1beta1 DaemonSet}:0xb44e40
  7. ]
  8. map[
  9. 0xb44e40:[{extensions v1beta1 DaemonSet}]
  10. 0xb46180:[{ v1 Pod} { __internal Pod}]
  11. 0xb56d40:[{ v1 Status}]
  12. ]
  13. map[
  14. 0xb56d40:{ v1 Status}
  15. ]
  16. map[
  17. Status:0xb56d40
  18. ]
  19. map[]
  20. map[]
  21. 0xc000124888
  22. map[]
  23. [{ v1} {extensions v1beta1}]
  24. pkg/runtime/scheme.go:100
  25. }
  26. map[Pod:v1.Pod Status:v1.Status]
  1. Kubernetes 资源分为外部资源和内部资源。外部资源是对外可访问的,其版本为 v1/v1beta1 等,内部资源是 Kubernetes 内部访问的资源,其版本为 __internal

为什么会有内部版本呢?
为了适配版本间的 convert

试想,如果 v1v1apha1v1beta1v1beta2 要相互转换需要建立十二种转换关系:

  1. v1 ----> v1alpha1
  2. v1 ----> v1beta1
  3. v1 ----> v1beta2
  4. v1alpha1 ----> v1
  5. v1alpha1 ----> v1beta1
  6. v1alpha1 ----> v1beta2
  7. v1beta1 ----> v1
  8. v1beta1 ----> v1alpha1
  9. v1beta1 ----> v1beta2
  10. v1beta2 ----> v1
  11. v1beta2 ----> v1alpha1
  12. v1beta2 ----> v1beta1

可以看到这种转换已经比较复杂了,随着资源增多,这种资源版本之间的转换会越来越复杂且难以维护。Kubernetes 通过引入内部版本 __internl 建立每种资源到 __internal 的转换,从而实现不同资源版本的相互转换。

  1. v1 ----> __internal
  2. __internal ----> v1
  3. v1alpha1 ----> __internal
  4. __internal ----> v1alpha1
  5. v1beta1 ----> __internal
  6. __internal ----> v1beta1
  7. v1beta2 ----> __internal
  8. __internal ----> v1beta2

转换示意图如下:
image

可以看到,相当干净,清爽。

2. kube-apiserver scheme

第一节介绍了 scheme 结构及部分特性。继续看 kube-apiserverscheme 的应用。

2.1 注册资源

kube-apiserver 中资源的注册通过导入包的形式实现。Kubernetes 将资源拆分为三类,并且由三种 HTTP Server 负责处理这三类资源,架构如下。

image

关于这幅图,这里不多讲,后续会逐层介绍。
要知道的是,三类资源分别注册到 extensionsapiserver.Schemelegacyscheme.Schemeaggregatorscheme.Scheme 三种资源注册表中。

legacyscheme.Scheme 资源注册表为例。
kube-apiserver 的启动 app 包中导入 "k8s.io/kubernetes/pkg/api/legacyscheme""k8s.io/kubernetes/pkg/controlplane"包:

  1. package app
  2. import (
  3. "k8s.io/kubernetes/pkg/api/legacyscheme"
  4. "k8s.io/kubernetes/pkg/controlplane"
  5. )

legacyscheme 包中创建了 Scheme 资源注册表,Codecs 编码器和 ParameterCodec 参数编码器。

  1. # kubernetes/pkg/api/legacyscheme/scheme.go
  2. package legacyscheme
  3. var (
  4. Scheme = runtime.NewScheme()
  5. Codecs = serializer.NewCodecFactory(Scheme)
  6. ParameterCodec = runtime.NewParameterCodec(Scheme)
  7. )

controlplane 包中导入需要注册的资源组:

  1. # kubernetes/pkg/controlplane/import_known_versions.go
  2. package controlplane
  3. import (
  4. _ "k8s.io/kubernetes/pkg/apis/apps/install"
  5. )

以注册 apps 资源组为例:

  1. # kubernetes/pkg/apis/apps/install/install.go
  2. package install
  3. import (
  4. "k8s.io/apimachinery/pkg/runtime"
  5. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  6. "k8s.io/kubernetes/pkg/api/legacyscheme"
  7. "k8s.io/kubernetes/pkg/apis/apps"
  8. "k8s.io/kubernetes/pkg/apis/apps/v1"
  9. "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
  10. "k8s.io/kubernetes/pkg/apis/apps/v1beta2"
  11. )
  12. func init() {
  13. Install(legacyscheme.Scheme)
  14. }
  15. func Install(scheme *runtime.Scheme) {
  16. utilruntime.Must(apps.AddToScheme(scheme))
  17. utilruntime.Must(v1beta1.AddToScheme(scheme))
  18. utilruntime.Must(v1beta2.AddToScheme(scheme))
  19. utilruntime.Must(v1.AddToScheme(scheme))
  20. utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1beta1.SchemeGroupVersion))
  21. }

资源组中调用 apps.AddToSchemev1beta1.AddToScheme,v1beta2.AddToSchemev1.AddToScheme 函数将同一资源组不同版本的资源注册到 legacyscheme.Scheme 资源注册表中。

其中 apps.AddToScheme 注册的是内部版本的资源:

  1. package apps
  2. var (
  3. SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
  4. AddToScheme = SchemeBuilder.AddToScheme
  5. )
  6. const GroupName = "apps"
  7. var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
  8. func addKnownTypes(scheme *runtime.Scheme) error {
  9. scheme.AddKnownTypes(SchemeGroupVersion,
  10. &DaemonSet{},
  11. &DaemonSetList{},
  12. &Deployment{},
  13. &DeploymentList{},
  14. &DeploymentRollback{},
  15. &autoscaling.Scale{},
  16. &StatefulSet{},
  17. &StatefulSetList{},
  18. &ControllerRevision{},
  19. &ControllerRevisionList{},
  20. &ReplicaSet{},
  21. &ReplicaSetList{},
  22. )
  23. return nil
  24. }

v1beta1.AddToScheme,v1beta2.AddToSchemev1.AddToScheme 注册的是外部版本的资源,以 v1.AddToScheme 为例:

  1. package v1
  2. const GroupName = "apps"
  3. var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
  4. var (
  5. SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
  6. localSchemeBuilder = &SchemeBuilder
  7. AddToScheme = localSchemeBuilder.AddToScheme
  8. )
  9. func addKnownTypes(scheme *runtime.Scheme) error {
  10. scheme.AddKnownTypes(SchemeGroupVersion,
  11. &Deployment{},
  12. &DeploymentList{},
  13. &StatefulSet{},
  14. &StatefulSetList{},
  15. &DaemonSet{},
  16. &DaemonSetList{},
  17. &ReplicaSet{},
  18. &ReplicaSetList{},
  19. &ControllerRevision{},
  20. &ControllerRevisionList{},
  21. )
  22. metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
  23. return nil
  24. }

需要注意的是,内部版本和外部版本注册的资源并不一样。比如 Deployment 资源,外部版本引用的是 kubernetes/vendor/k8s.io/api/apps/v1/types.go 中的资源定义:

  1. type Deployment struct {
  2. metav1.TypeMeta `json:",inline"`
  3. metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
  4. Spec DeploymentSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
  5. Status DeploymentStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
  6. }

资源声明了 jsonprotobuf tag,以便于外部访问的序列化,反序列化操作。

内部版本引用的是 kubernetes/pkg/apis/apps/types.go 中的资源定义:

  1. type Deployment struct {
  2. metav1.TypeMeta
  3. metav1.ObjectMeta
  4. Spec DeploymentSpec
  5. Status DeploymentStatus
  6. }

内部资源不需要被外部访问,没有 tag 声明。

到这里,kube-apiserver 关于资源的注册就差不多了。下一篇将进一步介绍 schemeconvertkube-apiserver 中如何使用 schemeschemeobject 相互转换等内容。


原文链接:https://www.cnblogs.com/xingzheanan/p/17771090.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号