kubernetes源码分析-kube-apiserver

功能介绍

API 网关

kube-apiserver 充当 Kubernetes 集群的 API 网关,接收和处理所有 API 请求。这些请求包括创建、读取、更新和删除(CRUD)操作,涉及集群中的各种资源,如 Pods、Services、Deployments 等。

身份验证与权限控制

kube-apiserver 负责对用户和服务进行身份验证和授权。它支持多种身份验证机制,包括客户端证书、Bearer Token、用户名和密码等。授权方面,kube-apiserver 使用基于角色的访问控制(RBAC)或准入控制插件来决定是否允许请求执行。

资源调度和协调

kube-apiserver 与其他核心组件(如 kube-schedulerkube-controller-manager)协作,以调度和管理集群资源。它将调度请求传递给 kube-scheduler,并协调控制器以确保集群处于预期状态。

数据存储

kube-apiserver 通过 etcd 存储集群的所有数据,包括配置信息、状态和元数据。所有资源的创建、更新和删除操作都会记录在 etcd 中,确保数据的一致性和持久性。

API 版本控制

Kubernetes 不断演进,kube-apiserver 提供了 API 版本控制,以支持向后兼容性。它同时支持多个 API 版本(如 v1、v1beta1),允许用户和服务逐步迁移到新的 API 版本。

准入控制

kube-apiserver 使用准入控制插件来拦截请求并对其进行修改或拒绝。这些插件可以执行各种策略,如资源配额、限制命名空间使用等。常见的准入控制插件包括 NamespaceLifecycle、ResourceQuota、PodSecurityPolicy 等。

负载均衡与高可用

为了提高可用性和可靠性,kube-apiserver 通常以集群方式部署,使用负载均衡器来分配请求。这样,即使某个 kube-apiserver 实例发生故障,其他实例仍然可以继续处理请求。

日志与监控

kube-apiserver 提供详细的日志记录功能,帮助管理员监控和排查集群问题。它还支持与 Prometheus 集成,以便进行更高级的监控和警报设置。

整体架构

整体结构

整个Kubernetes技术体系由声明式API以及Controller构成,而kube-apiserver是Kubernetes的声明式api server,并为其它组件交互提供了桥梁。因此加深对kube-apiserver的理解就显得至关重要

kube-apiserver作为整个Kubernetes集群操作etcd的唯一入口,负责Kubernetes各资源的认证&鉴权,校验以及CRUD等操作,提供RESTful APIs,供其它组件调用:

kube-apiserver包含三种APIServer:

  • aggregatorServer:负责处理 apiregistration.k8s.io 组下的APIService资源请求,同时将来自用户的请求拦截转发给aggregated server(AA)

  • kubeAPIServer:负责对请求的一些通用处理,包括:认证、鉴权以及各个内建资源(pod, deployment,service and etc)的REST服务等

  • apiExtensionsServer:负责CustomResourceDefinition(CRD)apiResources以及apiVersions的注册,同时处理CRD以及相应CustomResource(CR)的REST请求(如果对应CR不能被处理的话则会返回404),也是apiserver Delegation的最后一环

另外还包括bootstrap-controller,主要负责Kubernetes default apiserver service的创建以及管理。

API调用流程

kube-apiserver 与其他组件交互主要是通过对外提供 API 来实现的,可以通过调用 kube-apiserver 的接口$ curl -k https://<masterIP>:6443或者通过其提供的 swagger-ui来获取,主要有以下三个 API。

  • 核心群体:主要在/api/v1.

  • 命名组:其路径为/apis/$NAME/$VERSION.

  • 一些暴露系统状态的 API:如/metrics,/healthz等。

API 的 URL 大致由 组成/apis/group/version/namespaces/my-ns/myresource,其中 API 的结构大致如下图所示。

请求的完整流程如下图

此处显示了 POST 请求的示例。当请求到达kube-apiserver时,kube-apiserver首先执行http过滤器链中注册的过滤器链,该过滤器链进行一系列过滤操作,主要是鉴权、鉴权等校验操作。当过滤器链完成后,请求被路由到相应的处理程序,该处理程序主要与 etcd 交互。处理程序中的主要操作如下

Decoder

kubernetes 中的大多数资源都会有一个internal version,因为一个资源在整个开发过程中可能有多个版本。例如,部署将具有extensions/v1beta1, apps/v1. 为了避免问题,kube-apiserver 必须知道如何在每对版本之间进行转换(例如,v1⇔v1alpha1、v1⇔v1beta1、v1beta1⇔v1alpha1)。因此它使用了一个特殊的internal version,它是一个通用的版本,它包含了版本的所有字段并且具有所有版本的功能。解码器会先将 creater 对象转换为internal version,然后再转换为storage version,这是存储在 etcd 中时的另一个版本。

解码时,首先从HTTP路径中获取期望的版本,然后使用scheme创建一个与正确版本匹配的空对象,并使用JSON或protobuf解码器进行转换,在转换的第一步,如果用户省略了一些字段,解码器将它们设置为默认值。

Admission

解码完成后,您需要通过验证集群的全局约束并根据集群配置设置默认值来检查是否可以创建或更新对象。您可以在目录中看到所有 kube-apiserver 可以使用的全局约束插件k8s.io/kubernetes/plugin/pkg/admission。kube-apiserver 通过将-enable-admission-plugins参数设置为 -ValidatingAdmissionWebhookor -MutatingAdmissionWebhook 来启动。

Validation

主要检查对象中字段的合法性。

在handler中执行完以上操作后,会执行最后一个与etcd相关的操作,POST操作会将数据写入etcd。以上在handler中的主要处理流程如下图所示。

 

v1beta1 ⇒ internal ⇒ | ⇒ | ⇒ v1 ⇒ json/yaml ⇒ etcd admission validation

kube-apiserver 中的组件

kube-apiserver 由 3 个组件(Aggregator、KubeAPIServer、APIExtensionServer)组成,它们通过委托依次处理请求。

  • Aggregator:暴露类似七层负载均衡的功能,除了对核心业务数据进行转发,拦也截对用户注册的扩展服务请求转发到其他用户服务器,并负责整个APIServer的Discovery功能。

  • KubeAPIServer:负责一些通用的请求处理、认证、鉴权等,以及处理每个内置资源的 REST 服务。

  • APIExtensionServer:主要处理CustomResourceDefinition(CRD)和CustomResource(CR)REST请求,是Delegation的最后一个环节,如果对应的CR不能处理,返回404。

Aggregator 和 APIExtensionsServer 分别对应扩展 APIServer 资源的两种主要方式,即 AA 和 CRD。

Aggregator 聚合器

Aggregator 通过将 APIServices 对象关联到一个 Service 来转发请求,而与之关联的 Service 类型进一步决定了请求转发的形式。聚合器由一个GenericAPIServer和一个控制器组成,控制器保持自己的状态。主要处理组下APIService资源的GenericAPIServer请求apiregistration.k8s.io

Aggregator 除了处理资源请求外,还包含几个控制器:

  • apiserviceRegistrationController:负责APIServices中资源的注册和删除。

  • availableConditionController:维护APIServices的可用性状态,包括其引用的Service是否可用等。

  • autoRegistrationController:用于维护 API 中存在的一组特定 APIService。

  • crdRegistrationController:负责将 CRD GroupVersions 自动注册到 APIServices 中。

  • openAPIAggregationController:将 APIServices 资源的更改同步到提供的 OpenAPI 文档。

Kubernetes 中的一些附加组件,例如 metrics-server 通过 Aggregator 的方式进行扩展,可以在真实环境中使用apiserver-builder工具轻松创建自定义资源作为 Aggregator 扩展。

启用 API 聚合

需要在 kube-apiserver 中添加以下配置以启用 API 聚合。

 

--proxy-client-cert-file=/etc/kubernetes/certs/proxy.crt --proxy-client-key-file=/etc/kubernetes/certs/proxy.key --requestheader-client-ca-file=/etc/kubernetes/certs/proxy-ca.crt --requestheader-allowed-names=aggregator --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User

KubeAPI 服务器

KubeAPIServer 主要提供 API Resource 操作的请求,为 kubernetes 中的许多 API 注册路由信息,暴露 RESTful API,向公众提供 kubernetes 服务。

它使集群内外的服务能够通过 RESTful API 操作 Kubernetes 中的资源。

APIExtensionServer

APIExtensionServer 是委托链的最后一层,是处理用户通过自定义资源定义的所有资源的资源服务器。

包含的控制器及其功能如下所示。

  • openapiController: 将 crd 资源的更改同步到提供的 OpenAPI 文档中,可以访问/openapi/v2.

  • crdController: 负责将 crd 信息注册到 apiVersions 和 apiResources 中,两者都可以通过$ kubectl api-versions和来查看$ kubectl api-resources

  • namingController:检查 crd obj 中的命名冲突,可在 crd 中查看.status.conditions

  • establishingController: 检查 crd 是否处于正常状态,在 crd 中可用.status.conditions

  • nonStructuralSchemaController: 检查 crd obj 结构是否正常,在 crd 中可用.status.conditions

  • apiApprovalController: 检查 crd 是否遵循 kubernetes API 声明策略,在 crd 中可用.status.conditions

  • finalizingController: 一个类似于finalizes的函数,与CRs的移除有关。

源码分析

源码版本

release-1.30

服务启动

启动入口

Run方法的主要逻辑是。

  • 调用NewConfig生成kube-apiserver 需要的配置。

  • 调用CreateServerChain构建服务调用链,判断是否启动非安全http服务器。http服务器链包含apiserver要启动的三台服务器,以及为每台服务器注册相应资源的路由。

  • 调用server.PrepareRun准备运行的服务,主要完成OpenAPI路由的健康检查、生存检查和注册。

  • 调用prepared.Run以启动 https 服务器。

服务器使用委托模式进行初始化,基本的API Server、CustomResource和Aggregator服务通过DelegationTarget接口链接在一起,对外提供服务。

//cmd/kube-apiserver/app/server.go
// Run runs the specified APIServer.  This should never exit.
func Run(opts options.CompletedOptions, stopCh <-chan struct{}) error {
    // To help debugging, immediately log version
    klog.Infof("Version: %+v", version.Get())

    klog.InfoS("Golang settings", "GOGC", os.Getenv("GOGC"), "GOMAXPROCS", os.Getenv("GOMAXPROCS"), "GOTRACEBACK", os.Getenv("GOTRACEBACK"))
    
    // 为 kubeAPIServer 创建配置
    config, err := NewConfig(opts)
    if err != nil {
        return err
    }
    completed, err := config.Complete()
    if err != nil {
        return err
    }
    // 构建服务调用链 
    server, err := CreateServerChain(completed)
    if err != nil {
        return err
    }
    // 备运行的服务,主要完成OpenAPI路由的健康检查、生存检查和注册。
    prepared, err := server.PrepareRun()
    if err != nil {
        return err
    }
    // 启动 https 服务器
    return prepared.Run(stopCh)
}

KubeAPI服务配置构建

CreateKubeAPIServerConfig

主要目的CreateKubeAPIServerConfig是调用buildGenericConfig创建 genericConfig

// cmd/kube-apiserver/app/server.go
// CreateKubeAPIServerConfig creates all the resources for running the API server, but runs none of them
func CreateKubeAPIServerConfig(opts options.CompletedOptions) (
    *controlplane.Config,
    aggregatorapiserver.ServiceResolver,
    []admission.PluginInitializer,
    error,
) {
    proxyTransport := CreateProxyTransport()
    // 构建 genericConfig
    genericConfig, versionedInformers, storageFactory, err := controlplaneapiserver.BuildGenericConfig(
        opts.CompletedOptions,
        []*runtime.Scheme{legacyscheme.Scheme, extensionsapiserver.Scheme, aggregatorscheme.Scheme},
        generatedopenapi.GetOpenAPIDefinitions,
    )
    if err != nil {
        return nil, nil, nil, err
    }
    // 初始化所支持的 capabilities
    capabilities.Setup(opts.AllowPrivileged, opts.MaxConnectionBytesPerSec)

    opts.Metrics.Apply()
    serviceaccount.RegisterMetrics()
    // 构建控制面配置结构
    config := &controlplane.Config{
        // 通用配置
        GenericConfig: genericConfig,
        // 扩展配置
        ExtraConfig: controlplane.ExtraConfig{
            APIResourceConfigSource: storageFactory.APIResourceConfigSource,
            StorageFactory:          storageFactory,
            EventTTL:                opts.EventTTL,
            KubeletClientConfig:     opts.KubeletConfig,
            EnableLogsSupport:       opts.EnableLogsHandler,
            ProxyTransport:          proxyTransport,

            ServiceIPRange:          opts.PrimaryServiceClusterIPRange,
            APIServerServiceIP:      opts.APIServerServiceIP,
            SecondaryServiceIPRange: opts.SecondaryServiceClusterIPRange,

            APIServerServicePort: 443,

            ServiceNodePortRange:      opts.ServiceNodePortRange,
            KubernetesServiceNodePort: opts.KubernetesServiceNodePort,

            EndpointReconcilerType: reconcilers.Type(opts.EndpointReconcilerType),
            MasterCount:            opts.MasterCount,

            ServiceAccountIssuer:        opts.ServiceAccountIssuer,
            ServiceAccountMaxExpiration: opts.ServiceAccountTokenMaxExpiration,
            ExtendExpiration:            opts.Authentication.ServiceAccounts.ExtendExpiration,

            VersionedInformers: versionedInformers,
        },
    }

    // ...

    return config, serviceResolver, pluginInitializers, nil
}

通用配置构建

BuildGenericConfig

主要逻辑如下。

  • 调用genericapiserver.NewConfig生成默认的genericConfig,genericConfig主要配置DefaultBuildHandlerChainDefaultBuildHandlerChain包含一系列用于认证、鉴权、鉴权的http过滤器链,以及一系列http过滤器链。

  • 调用master.DefaultAPIResourceConfigSource加载需要启用的API资源,在k8s.io/api代码目录下可以看到集群中所有的API资源,随着版本的迭代也会不断变化。

  • 为 genericConfig 中的一些字段设置默认值。

  • 调用completedStorageFactoryConfig.New创建 storageFactory,稍后会使用它为每个 API Resource 创建对应的 RESTStorage。

// pkg/controlplane/apiserver/config.go
// BuildGenericConfig takes the master server options and produces the genericapiserver.Config associated with it
func BuildGenericConfig(
    s controlplaneapiserver.CompletedOptions,
    schemes []*runtime.Scheme,
    getOpenAPIDefinitions func(ref openapicommon.ReferenceCallback) map[string]openapicommon.OpenAPIDefinition,
) (
    genericConfig *genericapiserver.Config,
    versionedInformers clientgoinformers.SharedInformerFactory,
    storageFactory *serverstorage.DefaultStorageFactory,

    lastErr error,
) {
    genericConfig = genericapiserver.NewConfig(legacyscheme.Codecs)
    //为 genericConfig 设置默认值
    genericConfig.MergedResourceConfig = controlplane.DefaultAPIResourceConfigSource()

    if lastErr = s.GenericServerRunOptions.ApplyTo(genericConfig); lastErr != nil {
        return
    }

    if lastErr = s.SecureServing.ApplyTo(&genericConfig.SecureServing, &genericConfig.LoopbackClientConfig); lastErr != nil {
        return
    }

    // 设置使用 protobufs 用来内部交互,并且禁用压缩功能
    // Use protobufs for self-communication.
    // Since not every generic apiserver has to support protobufs, we
    // cannot default to it in generic apiserver and need to explicitly
    // set it in kube-apiserver.
    genericConfig.LoopbackClientConfig.ContentConfig.ContentType = "application/vnd.kubernetes.protobuf"
    // Disable compression for self-communication, since we are going to be
    // on a fast local network
    genericConfig.LoopbackClientConfig.DisableCompression = true

    kubeClientConfig := genericConfig.LoopbackClientConfig
    clientgoExternalClient, err := clientgoclientset.NewForConfig(kubeClientConfig)
    if err != nil {
        lastErr = fmt.Errorf("failed to create real external clientset: %v", err)
        return
    }
    versionedInformers = clientgoinformers.NewSharedInformerFactory(clientgoExternalClient, 10*time.Minute)

    if lastErr = s.Features.ApplyTo(genericConfig, clientgoExternalClient, versionedInformers); lastErr != nil {
        return
    }
    if lastErr = s.APIEnablement.ApplyTo(genericConfig, controlplane.DefaultAPIResourceConfigSource(), legacyscheme.Scheme); lastErr != nil {
        return
    }
    if lastErr = s.EgressSelector.ApplyTo(genericConfig); lastErr != nil {
        return
    }
    if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) {
        if lastErr = s.Traces.ApplyTo(genericConfig.EgressSelector, genericConfig); lastErr != nil {
            return
        }
    }
    // wrap the definitions to revert any changes from disabled features
    getOpenAPIDefinitions = openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(getOpenAPIDefinitions)
    namer := openapinamer.NewDefinitionNamer(schemes...)
    genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(getOpenAPIDefinitions, namer)
    genericConfig.OpenAPIConfig.Info.Title = "Kubernetes"
    genericConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config(getOpenAPIDefinitions, namer)
    genericConfig.OpenAPIV3Config.Info.Title = "Kubernetes"

    genericConfig.LongRunningFunc = filters.BasicLongRunningRequestCheck(
        sets.NewString("watch", "proxy"),
        sets.NewString("attach", "exec", "proxy", "log", "portforward"),
    )

    kubeVersion := version.Get()
    genericConfig.Version = &kubeVersion

    if genericConfig.EgressSelector != nil {
        s.Etcd.StorageConfig.Transport.EgressLookup = genericConfig.EgressSelector.Lookup
    }
    if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) {
        s.Etcd.StorageConfig.Transport.TracerProvider = genericConfig.TracerProvider
    } else {
        s.Etcd.StorageConfig.Transport.TracerProvider = oteltrace.NewNoopTracerProvider()
    }
    //  // 初始化 storageFactory
    storageFactoryConfig := kubeapiserver.NewStorageFactoryConfig()
    storageFactoryConfig.APIResourceConfig = genericConfig.MergedResourceConfig
    storageFactory, lastErr = storageFactoryConfig.Complete(s.Etcd).New()
    if lastErr != nil {
        return
    }
    // 初始化 RESTOptionsGetter,后期根据其获取操作 Etcd 的句柄,同时添加 etcd 的健康检查方法
    if lastErr = s.Etcd.ApplyWithStorageFactoryTo(storageFactory, genericConfig); lastErr != nil {
        return
    }

    ctx := wait.ContextForChannel(genericConfig.DrainedNotify())
    // 创建认证实例,支持多种认证方式:请求 Header 认证、Auth 文件认证、CA 证书认证、
    // Bearer token 认证、
    // ServiceAccount 认证、BootstrapToken 认证、WebhookToken 认证等
    // Authentication.ApplyTo requires already applied OpenAPIConfig and EgressSelector if present
    if lastErr = s.Authentication.ApplyTo(ctx, &genericConfig.Authentication, genericConfig.SecureServing, genericConfig.EgressSelector, genericConfig.OpenAPIConfig, genericConfig.OpenAPIV3Config, clientgoExternalClient, versionedInformers, genericConfig.APIServerID); lastErr != nil {
        return
    }

    var enablesRBAC bool
    // 创建鉴权实例,包含:Node、RBAC、Webhook、ABAC、AlwaysAllow、AlwaysDeny
    genericConfig.Authorization.Authorizer, genericConfig.RuleResolver, enablesRBAC, err = BuildAuthorizer(
        ctx,
        s,
        genericConfig.EgressSelector,
        genericConfig.APIServerID,
        versionedInformers,
    )
    if err != nil {
        lastErr = fmt.Errorf("invalid authorization config: %v", err)
        return
    }
    if s.Authorization != nil && !enablesRBAC {
        genericConfig.DisabledPostStartHooks.Insert(rbacrest.PostStartHookName)
    }

    lastErr = s.Audit.ApplyTo(genericConfig)
    if lastErr != nil {
        return
    }

    if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.AggregatedDiscoveryEndpoint) {
        genericConfig.AggregatedDiscoveryGroupManager = aggregated.NewResourceManager("apis")
    }

    return
}

服务初始化

CreateServerChain

CreateServerChain是完成服务端初始化的方法,包含初始化,最后返回实例的所有过程APIExtensionsServer,初始化过程主要包括:http过滤链的配置,API Group的注册,http路径与handler的关联以及处理程序后端存储 etcd 的配置。主要逻辑如下。KubeAPIServerAggregatorServeraggregatorapiserver. APIAggregator

  • 调用创建KubeAPIServer需要的配置CreateKubeAPIServerConfig,主要是create master.Config,会调用buildGenericConfig生成genericConfig,其中包含apiserver核心配置的核心配置。

  • 确定是否启用了扩展 API 服务器并调用createAPIExtensionsConfig为其创建配置。apiExtensions server 是 kubeapiserver 中其他服务器的代理服务,例如 metric-server。

  • 调用createAPIExtensionsServer以创建 apiExtensionsServer 的实例。

  • 调用CreateKubeAPIServer以初始化 kubeAPIServer。

  • 调用createAggregatorConfig以创建 aggregatorServer 的配置并调用createAggregatorServer以初始化 aggregatorServer。

  • 配置并确定是否启动非安全http服务器。

  1. KubeAPIServer:

    1. KubeAPIServer 是 Kubernetes 的核心组件之一,它是 Kubernetes API 服务器的实现,负责处理集群中所有 API 请求。

    2. 它提供了对 Kubernetes 集群的访问和管理,包括创建、更新和删除资源对象,例如 Pods、Deployments、Services 等。

    3. KubeAPIServer 扮演着集群的“大脑”,负责控制整个集群的状态和配置。

  2. APIExtensionsServer:

    1. APIExtensionsServer 是负责管理 Kubernetes 的 API 扩展的组件,它允许用户通过自定义资源定义 (Custom Resource Definitions, CRDs) 来扩展 Kubernetes API。

    2. 使用 APIExtensionsServer,用户可以定义自己的资源类型,从而为 Kubernetes 添加新的 API 资源和控制器。

    3. 这些自定义资源类型可以像内置资源(如 Pods、Services 等)一样被创建、更新和删除,而它们的行为由用户编写的控制器来定义。

/// cmd/kube-apiserver/app/server.go
// CreateServerChain creates the apiservers connected via delegation.
func CreateServerChain(config CompletedConfig) (*aggregatorapiserver.APIAggregator, error) {
    notFoundHandler := notfoundhandler.New(config.ControlPlane.GenericConfig.Serializer, genericapifilters.NoMuxAndDiscoveryIncompleteKey)
    //判断是否配置了 APIExtensionsServer,创建 apiExtensionsConfig
    apiExtensionsServer, err := config.ApiExtensions.New(genericapiserver.NewEmptyDelegateWithCustomHandler(notFoundHandler))
    if err != nil {
        return nil, err
    }
    // 判断crd api 是否enbale, 表示是否支持用户扩展api
    crdAPIEnabled := config.ApiExtensions.GenericConfig.MergedResourceConfig.ResourceEnabled(apiextensionsv1.SchemeGroupVersion.WithResource("customresourcedefinitions"))

    // 初始化 KubeAPIServer
    kubeAPIServer, err := config.ControlPlane.New(apiExtensionsServer.GenericAPIServer)
    if err != nil {
        return nil, err
    }

    // 初始化 AggregatorServer 对API进行聚合
    // aggregator comes last in the chain
    aggregatorServer, err := createAggregatorServer(config.Aggregator, kubeAPIServer.GenericAPIServer, apiExtensionsServer.Informers, crdAPIEnabled)
    if err != nil {
        // we don't need special handling for innerStopCh because the aggregator server doesn't create any go routines
        return nil, err
    }

    return aggregatorServer, nil
}

KubeAPI服务初始化

kubeAPIServerConfig.Complete().New

主要逻辑如下。

  • 调用 GenericAPIServer 初始化 GenericAPIServer,c.GenericConfig.New上面分析了它的主要实现。

  • 判断是否支持日志相关路由,如果支持,添加/logs路由。

  • 调用m.InstallLegacyAPI将核心 API 资源添加到路由中,该路由对应 apiserver 作为以 . 开头的资源/api

  • 调用m.InstallAPIs以将扩展 API 资源添加到路由,在 apiserver 中是以 . 开头的资源/apis

// pkg/controlplane/instance.go
// New returns a new instance of Master from the given config.
// Certain config fields will be set to a default value if unset.
// Certain config fields must be specified, including:
// KubeletClientConfig
func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*Instance, error) {
    if reflect.DeepEqual(c.ExtraConfig.KubeletClientConfig, kubeletclient.KubeletClientConfig{}) {
        return nil, fmt.Errorf("Master.New() called with empty config.KubeletClientConfig")
    }
    
    // 初始化 GenericAPIServer
    s, err := c.GenericConfig.New("kube-apiserver", delegationTarget)
    if err != nil {
        return nil, err
    }
    
    // 注册 logs 相关的路由
    if c.ExtraConfig.EnableLogsSupport {
        routes.Logs{}.Install(s.Handler.GoRestfulContainer)
    }

    // Metadata and keys are expected to only change across restarts at present,
    // so we just marshal immediately and serve the cached JSON bytes.
    md, err := serviceaccount.NewOpenIDMetadata(
        c.ExtraConfig.ServiceAccountIssuerURL,
        c.ExtraConfig.ServiceAccountJWKSURI,
        c.GenericConfig.ExternalAddress,
        c.ExtraConfig.ServiceAccountPublicKeys,
    )
    if err != nil {
        // If there was an error, skip installing the endpoints and log the
        // error, but continue on. We don't return the error because the
        // metadata responses require additional, backwards incompatible
        // validation of command-line options.
        msg := fmt.Sprintf("Could not construct pre-rendered responses for"+
            " ServiceAccountIssuerDiscovery endpoints. Endpoints will not be"+
            " enabled. Error: %v", err)
        if c.ExtraConfig.ServiceAccountIssuerURL != "" {
            // The user likely expects this feature to be enabled if issuer URL is
            // set and the feature gate is enabled. In the future, if there is no
            // longer a feature gate and issuer URL is not set, the user may not
            // expect this feature to be enabled. We log the former case as an Error
            // and the latter case as an Info.
            klog.Error(msg)
        } else {
            klog.Info(msg)
        }
    } else {
        routes.NewOpenIDMetadataServer(md.ConfigJSON, md.PublicKeysetJSON).
            Install(s.Handler.GoRestfulContainer)
    }

    m := &Instance{
        GenericAPIServer:          s,
        ClusterAuthenticationInfo: c.ExtraConfig.ClusterAuthenticationInfo,
    }

    clientset, err := kubernetes.NewForConfig(c.GenericConfig.LoopbackClientConfig)
    if err != nil {
        return nil, err
    }
    // 安装 LegacyAPI, 注册了核心API
    // TODO: update to a version that caches success but will recheck on failure, unlike memcache discovery
    discoveryClientForAdmissionRegistration := clientset.Discovery()

    legacyRESTStorageProvider, err := corerest.New(corerest.Config{
        GenericConfig: corerest.GenericConfig{
            StorageFactory:              c.ExtraConfig.StorageFactory,
            EventTTL:                    c.ExtraConfig.EventTTL,
            LoopbackClientConfig:        c.GenericConfig.LoopbackClientConfig,
            ServiceAccountIssuer:        c.ExtraConfig.ServiceAccountIssuer,
            ExtendExpiration:            c.ExtraConfig.ExtendExpiration,
            ServiceAccountMaxExpiration: c.ExtraConfig.ServiceAccountMaxExpiration,
            APIAudiences:                c.GenericConfig.Authentication.APIAudiences,
            Informers:                   c.ExtraConfig.VersionedInformers,
        },
        Proxy: corerest.ProxyConfig{
            Transport:           c.ExtraConfig.ProxyTransport,
            KubeletClientConfig: c.ExtraConfig.KubeletClientConfig,
        },
        Services: corerest.ServicesConfig{
            ClusterIPRange:          c.ExtraConfig.ServiceIPRange,
            SecondaryClusterIPRange: c.ExtraConfig.SecondaryServiceIPRange,
            NodePortRange:           c.ExtraConfig.ServiceNodePortRange,
            IPRepairInterval:        c.ExtraConfig.RepairServicesInterval,
        },
    })
    if err != nil {
        return nil, err
    }

    // The order here is preserved in discovery.
    // If resources with identical names exist in more than one of these groups (e.g. "deployments.apps"" and "deployments.extensions"),
    // the order of this list determines which group an unqualified resource name (e.g. "deployments") should prefer.
    // This priority order is used for local discovery, but it ends up aggregated in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go
    // with specific priorities.
    // TODO: describe the priority all the way down in the RESTStorageProviders and plumb it back through the various discovery
    // handlers that we have.
    restStorageProviders := []RESTStorageProvider{
        legacyRESTStorageProvider,
        apiserverinternalrest.StorageProvider{},
        authenticationrest.RESTStorageProvider{Authenticator: c.GenericConfig.Authentication.Authenticator, APIAudiences: c.GenericConfig.Authentication.APIAudiences},
        authorizationrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorization.Authorizer, RuleResolver: c.GenericConfig.RuleResolver},
        autoscalingrest.RESTStorageProvider{},
        batchrest.RESTStorageProvider{},
        certificatesrest.RESTStorageProvider{},
        coordinationrest.RESTStorageProvider{},
        discoveryrest.StorageProvider{},
        networkingrest.RESTStorageProvider{},
        noderest.RESTStorageProvider{},
        policyrest.RESTStorageProvider{},
        rbacrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorization.Authorizer},
        schedulingrest.RESTStorageProvider{},
        storagerest.RESTStorageProvider{},
        svmrest.RESTStorageProvider{},
        flowcontrolrest.RESTStorageProvider{InformerFactory: c.GenericConfig.SharedInformerFactory},
        // keep apps after extensions so legacy clients resolve the extensions versions of shared resource names.
        // See https://github.com/kubernetes/kubernetes/issues/42392
        appsrest.StorageProvider{},
        admissionregistrationrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorization.Authorizer, DiscoveryClient: discoveryClientForAdmissionRegistration},
        eventsrest.RESTStorageProvider{TTL: c.ExtraConfig.EventTTL},
        resourcerest.RESTStorageProvider{},
    }
    // 安装API
    if err := m.InstallAPIs(c.ExtraConfig.APIResourceConfigSource, c.GenericConfig.RESTOptionsGetter, restStorageProviders...); err != nil {
        return nil, err
    }

    // ...

    return m, nil
}

KubeAPI安装API

InstallAPIs方法的主要作用是将核心API注册到路由中,是apiserver初始化过程中最核心的方法之一,但是它的调用链很深,下面会深入分析。将API注册到路由的最终目的是提供外部RESTful API来操作对应的资源,注册API主要分为两步,第一步是为API中的每个资源初始化RESTStorage来操作变更对于后端存储中的数据,第二步是根据每个资源的动词构建对应的路由第二步是根据每个资源的动词构建对应的路由。的主要逻辑如下。

  • 调用RESTStorage为 每个资源创建 RESTStorage。RESTStorage 的目的是对应每个资源的访问路径及其在后端存储中的操作。

  • 为资源创建 RESTStorage 后,调用m.GenericAPIServer.InstallAPIGroup为 APIGroup 注册路由信息。方法的调用链InstallAPIGroup很深,主要是InstallLegacyAPIGroup --> installAPIResources --> InstallREST --> Install --> registerResourceHandlers,而最终的核心路由构建在registerResourceHandlers方法中,比较复杂,它的主要作用是确定哪些操作(如创建、更新等)可以通过通过上一步构建的REST Storage获取资源,并将对应的操作存储到action中,每个action对应一个标准最后,根据actions数组,每个action都会添加一个handler方法并注册到路由中,然后该路由将注册到网络服务。Web 服务最终将按照 go-restful 设计模式注册到容器中。

// pkg/controlplane/instance.go
// InstallAPIs will install the APIs for the restStorageProviders if they are enabled.
func (m *Instance) InstallAPIs(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter, restStorageProviders ...RESTStorageProvider) error {
    nonLegacy := []*genericapiserver.APIGroupInfo{}

    // used later in the loop to filter the served resource by those that have expired.
    resourceExpirationEvaluator, err := genericapiserver.NewResourceExpirationEvaluator(*m.GenericAPIServer.Version)
    if err != nil {
        return err
    }

    for _, restStorageBuilder := range restStorageProviders {
        groupName := restStorageBuilder.GroupName()
        // 每个资源创建 RESTStorag
        apiGroupInfo, err := restStorageBuilder.NewRESTStorage(apiResourceConfigSource, restOptionsGetter)
        if err != nil {
            return fmt.Errorf("problem initializing API group %q : %v", groupName, err)
        }
        if len(apiGroupInfo.VersionedResourcesStorageMap) == 0 {
            // If we have no storage for any resource configured, this API group is effectively disabled.
            // This can happen when an entire API group, version, or development-stage (alpha, beta, GA) is disabled.
            klog.Infof("API group %q is not enabled, skipping.", groupName)
            continue
        }

        // Remove resources that serving kinds that are removed.
        // We do this here so that we don't accidentally serve versions without resources or openapi information that for kinds we don't serve.
        // This is a spot above the construction of individual storage handlers so that no sig accidentally forgets to check.
        resourceExpirationEvaluator.RemoveDeletedKinds(groupName, apiGroupInfo.Scheme, apiGroupInfo.VersionedResourcesStorageMap)
        if len(apiGroupInfo.VersionedResourcesStorageMap) == 0 {
            klog.V(1).Infof("Removing API group %v because it is time to stop serving it because it has no versions per APILifecycle.", groupName)
            continue
        }

        klog.V(1).Infof("Enabling API group %q.", groupName)

        if postHookProvider, ok := restStorageBuilder.(genericapiserver.PostStartHookProvider); ok {
            name, hook, err := postHookProvider.PostStartHook()
            if err != nil {
                klog.Fatalf("Error building PostStartHook: %v", err)
            }
            m.GenericAPIServer.AddPostStartHookOrDie(name, hook)
        }

        if len(groupName) == 0 {
            // the legacy group for core APIs is special that it is installed into /api via this special install method.
            if err := m.GenericAPIServer.InstallLegacyAPIGroup(genericapiserver.DefaultLegacyAPIPrefix, &apiGroupInfo); err != nil {
                return fmt.Errorf("error in registering legacy API: %w", err)
            }
        } else {
            // everything else goes to /apis
            nonLegacy = append(nonLegacy, &apiGroupInfo)
        }
    }
    //它的主要作用是确定哪些操作(如创建、更新等)可以通过通过上一步构建的REST Storage获取资源,
    //并将对应的操作存储到action中,每个action对应一个标准最后,根据actions数组,
    //每个action都会添加一个handler方法并注册到路由中,
    //然后该路由将注册到网络服务。Web 服务最终将按照 go-restful 设计模式注册到容器中。
    if err := m.GenericAPIServer.InstallAPIGroups(nonLegacy...); err != nil {
        return fmt.Errorf("error in registering group versions: %v", err)
    }
    return nil
}

APIExtensions服务初始化

APIExtensionsServer 最先被初始化,通过如下逻辑调用初始化apiextensionsConfig.Complete().New服务器createAPIExtensionsServer

  • 首先调用c.GenericConfig.New根据go-restful模式初始化Container,在c.GenericConfig.New调用NewAPIServerHandler中初始化handler,APIServerHandler是APIServer使用的Handler类型,包括go-restfuland non-go-restful,和一个你在两者之间选择的Director对象,go-restful用来处理注册的handler和non-go restful是用于处理不存在的处理程序,API URI 处理的选择过程是:FullHandlerChain-> Director -> {GoRestfulContainer, NonGoRestfulMux}. 在c.GenericConfig.New,installAPI中也调用添加路由信息包括/, /debug/*, /metrics,/version等。这三种服务器都是通过调用c.GenericConfig.New初始化genericServer然后注册API来初始化的。

  • 调用s.GenericAPIServer.InstallAPIGroup注册路由中的API资源,这个方法的调用链很深,主要是注册要暴露给服务端的API资源,从而可以通过http接口对资源进行REST操作。其他几个服务器也在InstallAPI初始化期间执行相应的操作。

  • 初始化服务器中需要用到的控制器,主要是openapiController, crdController, namingController, establishingController, nonStructuralSchemaController, apiApprovalController, finalizingController。

  • 将需要启动的控制器和通知器添加到 PostStartHook。

// staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go
// New returns a new instance of Cu
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值