• kube-apiserver资源注册


    kube-apiserver进程会启动三种apiserver: aggregatorserver,kubeapiserver和apiExtensionsServer,其中kubeapiserver启动时会通过go的import和init机制将k8s支持的资源注册到资源注册表中,这样后面的逻辑才能从scheme资源注册表拿到资源信息并启动apiserver。import和init机制如下:

    SchemeBuilder

    在资源注册过程中用到了一个辅助结构SchemeBuilder,先介绍一下它,其定义如下

    #k8s.io/apimachinery/pkg/runtime/scheme_builer.go
    // SchemeBuilder collects functions that add things to a scheme. It's to allow
    // code to compile without explicitly referencing generated types. You should
    // declare one in each package that will have generated deep copy or conversion
    // functions.
    //SchemeBuilder定义,是个函数数组
    type SchemeBuilder []func(*Scheme) error

    可看到它就是一个数组,每个数组元素都是个函数指针

    1. 创建SchemeBuilder,同时可以传递几个函数,进行注册

    // NewSchemeBuilder calls Register for you.
    func NewSchemeBuilder(funcs ...func(*Scheme) error) SchemeBuilder {
        var sb SchemeBuilder
        sb.Register(funcs...)
        return sb
    }

    2. 创建完SchemeBuilder后,可以调用Register注册函数 

     // Register adds a scheme setup function to the list.
    func (sb *SchemeBuilder) Register(funcs ...func(*Scheme) error) {
        for _, f := range funcs {
            *sb = append(*sb, f)
        }
    }

     3. 调用AddToScheme时,执行数组中注册的所有函数

    // AddToScheme applies all the stored functions to the scheme. A non-nil error
    // indicates that one function failed and the attempt was abandoned.
    func (sb *SchemeBuilder) AddToScheme(s *Scheme) error {
        for _, f := range *sb {
            if err := f(s); err != nil {
                return err
            }
        }
        return nil
    }

    kubeapiserver资源注册过程

    kube-apiserver进程来说,其导入了legacyscheme和controlplane包。 


    #cmd/kube-apiserver/app/server.go:
        "k8s.io/kubernetes/pkg/api/legacyscheme" //创建资源注册表
        "k8s.io/kubernetes/pkg/controlplane" //注册资源到资源注册表

    1. legacyscheme包创建了资源注册表,编解码器等

    #pkg/api/legacyscheme:
    var (
        // Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered.
        // NOTE: If you are copying this file to start a new api group, STOP! Copy the
        // extensions group instead. This Scheme is special and should appear ONLY in
        // the api group, unless you really know what you're doing.
        // TODO(lavalamp): make the above error impossible.
        Scheme = runtime.NewScheme()

        // Codecs provides access to encoding and decoding for the scheme
        Codecs = serializer.NewCodecFactory(Scheme)

        // ParameterCodec handles versioning of objects that are converted to query parameters.
        ParameterCodec = runtime.NewParameterCodec(Scheme)
    )

    2.  注册资源

    controlplane包里的import_known_versions.go文件导入了kubeapiserver支持的所有资源

    #pkg/controlplane/import_known_versions.go:
    import (
        // These imports are the API groups the API server will support.
        _ "k8s.io/kubernetes/pkg/apis/admission/install"
        _ "k8s.io/kubernetes/pkg/apis/admissionregistration/install"
        _ "k8s.io/kubernetes/pkg/apis/apiserverinternal/install"
        _ "k8s.io/kubernetes/pkg/apis/apps/install"
        _ "k8s.io/kubernetes/pkg/apis/authentication/install"
        _ "k8s.io/kubernetes/pkg/apis/authorization/install"
        _ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
        _ "k8s.io/kubernetes/pkg/apis/batch/install"
        _ "k8s.io/kubernetes/pkg/apis/certificates/install"
        _ "k8s.io/kubernetes/pkg/apis/coordination/install"
        _ "k8s.io/kubernetes/pkg/apis/core/install"        //core代表的是无分组资源,比如pod
        _ "k8s.io/kubernetes/pkg/apis/discovery/install"
        _ "k8s.io/kubernetes/pkg/apis/events/install"
        _ "k8s.io/kubernetes/pkg/apis/extensions/install"
        _ "k8s.io/kubernetes/pkg/apis/flowcontrol/install"
        _ "k8s.io/kubernetes/pkg/apis/imagepolicy/install"
        _ "k8s.io/kubernetes/pkg/apis/networking/install"
        _ "k8s.io/kubernetes/pkg/apis/node/install"
        _ "k8s.io/kubernetes/pkg/apis/policy/install"
        _ "k8s.io/kubernetes/pkg/apis/rbac/install"
        _ "k8s.io/kubernetes/pkg/apis/scheduling/install"
        _ "k8s.io/kubernetes/pkg/apis/storage/install"

    拿其中一类资源(core核心无分组资源)举例来看更详细的过程。

    在install.go里注册了核心无分组资源的内部版本和V1版本到资源注册表

    #pkg/apis/core/install/install.go:
    import (
        "k8s.io/apimachinery/pkg/runtime"
        utilruntime "k8s.io/apimachinery/pkg/util/runtime"
        "k8s.io/kubernetes/pkg/api/legacyscheme" //导入legacyscheme包,为的是获取全局资源注册表legacyscheme.Scheme
        "k8s.io/kubernetes/pkg/apis/core" //导入core包,里面创建了SchemeBuilder
        "k8s.io/kubernetes/pkg/apis/core/v1" //导入core/v1包,里面创建了SchemeBuilder
    )

    func init() {
        Install(legacyscheme.Scheme)
    }

    // Install registers the API group and adds types to a scheme
    func Install(scheme *runtime.Scheme) {
        utilruntime.Must(core.AddToScheme(scheme)) //注册核心无分组资源的内部版本到资源注册表
        utilruntime.Must(v1.AddToScheme(scheme))   //注册核心无分组资源的V1版本到资源注册表
        utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion)) //设置核心无分组资源版本优先级,V1优先级最高
    }

    导入core包时,会初始化register.go里的全局变量,创建出SchemeBuilder

    #pkg/apis/core/register.go:
    // GroupName is the group name use in this package
    const GroupName = ""

    //APIVersionInternal = "__internal"
    // SchemeGroupVersion is group version used to register these objects
    var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}

    var (
        // SchemeBuilder object to register various known types
        //创建SchemeBuilder,同时注册addKnownTypes到SchemeBuilder中
        SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)

        // AddToScheme represents a func that can be used to apply all the registered
        // funcs in a scheme
        AddToScheme = SchemeBuilder.AddToScheme
    )

    当执行AddToScheme时,会调用注册的addKnownTypes,将所有核心无分组资源注册到资源注册表
    func addKnownTypes(scheme *runtime.Scheme) error {
        if err := scheme.AddIgnoredConversionType(&metav1.TypeMeta{}, &metav1.TypeMeta{}); err != nil {
            return err
        }
        scheme.AddKnownTypes(SchemeGroupVersion,
            &Pod{},
            &PodList{},
            &PodStatusResult{},
            &PodTemplate{},
            &PodTemplateList{},
            &ReplicationControllerList{},
            &ReplicationController{},
            &ServiceList{},
            &Service{},
            ...)
    }

    导入v1/core包时,会初始化register.go里的全局变量

    #pkg/apis/core/v1/register.go:
    import (
        "k8s.io/api/core/v1" //又导入了包k8s.io/api/core/v1,创建出SchemeBuilder
        "k8s.io/apimachinery/pkg/runtime/schema"
    )

    var (
        localSchemeBuilder = &v1.SchemeBuilder
        AddToScheme        = localSchemeBuilder.AddToScheme
    )

    func init() {
        // We only register manually written functions here. The registration of the
        // generated functions takes place in the generated files. The separation
        // makes the code compile even when the generated files are missing.
        //将addDefaultingFuncs和addConversionFuncs注册到SchemeBuilder
        localSchemeBuilder.Register(addDefaultingFuncs, addConversionFuncs)
    }


    //注册默认函数,用于给资源字段设置默认值
    #pkg/apis/core/v1/defaults.go:
    func addDefaultingFuncs(scheme *runtime.Scheme) error {
        return RegisterDefaults(scheme)
    }

    #pkg/apis/core/v1/zz_generated.defaults.go:
    func RegisterDefaults(scheme *runtime.Scheme) error {
        scheme.AddTypeDefaultingFunc(&v1.ConfigMap{}, func(obj interface{}) { SetObjectDefaults_ConfigMap(obj.(*v1.ConfigMap)) })
        scheme.AddTypeDefaultingFunc(&v1.ConfigMapList{}, func(obj interface{}) { SetObjectDefaults_ConfigMapList(obj.(*v1.ConfigMapList)) })
        ...
    }

    //注册版本转换函数
    #pkg/apis/core/v1/conversion.go:
    func addConversionFuncs(scheme *runtime.Scheme) error {
        // Add field conversion funcs.
        err := scheme.AddFieldLabelConversionFunc(SchemeGroupVersion.WithKind("Pod"),
            func(label, value string) (string, string, error) {
                switch label {
                case "metadata.name",
                    "metadata.namespace",
                    "spec.nodeName",
                    "spec.restartPolicy",
                    "spec.schedulerName",
                    "spec.serviceAccountName",
                    "status.phase",
                    "status.podIP",
                    "status.podIPs",
                    "status.nominatedNodeName":
                    return label, value, nil
                // This is for backwards compatibility with old v1 clients which send spec.host
                case "spec.host":
                    return "spec.nodeName", value, nil
                default:
                    return "", "", fmt.Errorf("field label not supported: %s", label)
                }
            },
        )
        ...
    }

    //注册自动生成的版本转换函数
    #pkg/apis/core/v1/zz_generated.conversion.go:
    func init() {
        localSchemeBuilder.Register(RegisterConversions)
    }

    // RegisterConversions adds conversion functions to the given scheme.
    // Public to allow building arbitrary schemes.
    func RegisterConversions(s *runtime.Scheme) error {
        if err := s.AddGeneratedConversionFunc((*v1.AWSElasticBlockStoreVolumeSource)(nil), (*core.AWSElasticBlockStoreVolumeSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
            return Convert_v1_AWSElasticBlockStoreVolumeSource_To_core_AWSElasticBlockStoreVolumeSource(a.(*v1.AWSElasticBlockStoreVolumeSource), b.(*core.AWSElasticBlockStoreVolumeSource), scope)
        }); err != nil {
            return err
        }
        if err := s.AddGeneratedConversionFunc((*core.AWSElasticBlockStoreVolumeSource)(nil), (*v1.AWSElasticBlockStoreVolumeSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
            return Convert_core_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource(a.(*core.AWSElasticBlockStoreVolumeSource), b.(*v1.AWSElasticBlockStoreVolumeSource), scope)
        }); err != nil {
            return err
        }
        ...
    }

    核心无组资源V1版本,创建NewSchemeBuilder 

    #k8s.io/api/core/v1/register.go
    // SchemeGroupVersion is group version used to register these objects
    var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}

    // Resource takes an unqualified resource and returns a Group qualified GroupResource
    func Resource(resource string) schema.GroupResource {
        return SchemeGroupVersion.WithResource(resource).GroupResource()
    }

    var (
        // We only register manually written functions here. The registration of the
        // generated functions takes place in the generated files. The separation
        // makes the code compile even when the generated files are missing.
        //创建SchemeBuilder,同时注册addKnownTypes到SchemeBuilder中
        SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
        AddToScheme   = SchemeBuilder.AddToScheme
    )

    当执行AddToScheme时,会调用注册的addKnownTypes,将所有核心无分组资源注册到资源注册表
    // Adds the list of known types to the given scheme.
    func addKnownTypes(scheme *runtime.Scheme) error {
        scheme.AddKnownTypes(SchemeGroupVersion,
            &Pod{},
            &PodList{},
            &PodStatusResult{},
            ...)
    }

    示意图

    上述整个资源注册流程可以使用下面示意图表示

    apiextensions-apiserver资源注册

    apiextensions-apiserver资源注册流程和上面是类似的,不过是注册到另一个全局资源注册表中。

    #k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go

    import "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install"

    var (

        //创建资源注册表
        Scheme = runtime.NewScheme()
        Codecs = serializer.NewCodecFactory(Scheme)

        // if you modify this, make sure you update the crEncoder
        unversionedVersion = schema.GroupVersion{Group: "", Version: "v1"}
        unversionedTypes   = []runtime.Object{
            &metav1.Status{},
            &metav1.WatchEvent{},
            &metav1.APIVersions{},
            &metav1.APIGroupList{},
            &metav1.APIGroup{},
            &metav1.APIResourceList{},
        }
    )

    func init() {
        install.Install(Scheme)

        // we need to add the options to empty v1
        metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Group: "", Version: "v1"})

        Scheme.AddUnversionedTypes(unversionedVersion, unversionedTypes...)
    }

    #k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install/install.go
    import (
        "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
        v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
        "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
    )

    // Install registers the API group and adds types to a scheme
    func Install(scheme *runtime.Scheme) {
        utilruntime.Must(apiextensions.AddToScheme(scheme))
        utilruntime.Must(v1beta1.AddToScheme(scheme))
        utilruntime.Must(v1.AddToScheme(scheme))
        utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion))
    }

    kube-aggregator资源注册

    同理,不再做过多介绍

    #k8s.io/kube-aggregator/pkg/apiserver/scheme/scheme.go
    var (
        // Scheme defines methods for serializing and deserializing API objects.
        Scheme = runtime.NewScheme()
        // Codecs provides methods for retrieving codecs and serializers for specific
        // versions and content types.
        Codecs = serializer.NewCodecFactory(Scheme)
    )

    func init() {
        AddToScheme(Scheme)
        install.Install(Scheme)
    }

    // AddToScheme adds the types of this group into the given scheme.
    func AddToScheme(scheme *runtime.Scheme) {
        utilruntime.Must(v1beta1.AddToScheme(scheme))
        utilruntime.Must(v1.AddToScheme(scheme))
        utilruntime.Must(apiregistration.AddToScheme(scheme))
    }

    #k8s.io/kube-aggregator/pkg/apis/apiregistration/install/install.go
    // Install registers the API group and adds types to a scheme
    func Install(scheme *runtime.Scheme) {
        utilruntime.Must(apiregistration.AddToScheme(scheme))
        utilruntime.Must(v1.AddToScheme(scheme))
        utilruntime.Must(v1beta1.AddToScheme(scheme))
        utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion))
    }

    #k8s.io/kube-aggregator/pkg/apiserver/apiserver.go
    func init() {
        // we need to add the options (like ListOptions) to empty v1
        metav1.AddToGroupVersion(aggregatorscheme.Scheme, schema.GroupVersion{Group: "", Version: "v1"})

        unversioned := schema.GroupVersion{Group: "", Version: "v1"}
        aggregatorscheme.Scheme.AddUnversionedTypes(unversioned,
            &metav1.Status{},
            &metav1.APIVersions{},
            &metav1.APIGroupList{},
            &metav1.APIGroup{},
            &metav1.APIResourceList{},
        )
     

  • 相关阅读:
    springboot使用@Validated校验不生效
    git 的功能使用(三)
    springboot基础及上传组件封装
    Python----函数中的说明文档
    谷粒商城 (十五) --------- 商品服务 API 品牌管理 ① 逆向工程生成前后端代码 与 前端效果优化
    ASP.NET Core框架探索之主机搭建与运行
    2023-9-12 完全背包问题
    Django 数据库的创建、链接、操作表、操作表中数据
    阿里云物联网平台app端一直上线下线
    【一起学Rust】Rust的Hello Rust详细解析
  • 原文地址:https://blog.csdn.net/fengcai_ke/article/details/126690299