searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

深入剖析Kubernetes核心组件:apiserver、scheduler的源码级分析

2024-05-30 03:12:55
5
0

Kubernetes的架构中,apiserver、scheduler和controller-manager这三个核心组件扮演着至关重要的角色。它们共同协作,维护着整个集群的状态和运行。本文将从源码层面深入分析这三个组件的工作原理,揭示Kubernetes背后的设计思想和实现细节。

一、apiserver:Kubernetes的门户

apiserver是Kubernetes的核心组件之一,它提供了集群的统一入口,负责接收、验证和处理所有的REST请求。同时,它也是集群状态的唯一真实来源(Single Source of Truth),维护着所有资源对象的最新状态。

让我们从kubernetes/cmd/kube-apiserver/apiserver.goRun函数开始,追踪apiserver的启动流程。

func Run(completeOptions completedServerRunOptions, stopCh <-chan struct{}) error {

    // ...

    server, err := CreateServerChain(completeOptions, stopCh)

    if err != nil {

        return err

    }

 

    return server.PrepareRun().Run(stopCh)

}

CreateServerChain函数中,apiserver会创建一个名为GenericAPIServer的通用API服务器,并注册各种资源对象的RESTful API。

func CreateKubeAPIServerConfig(s completedConfig, nodeTunneler tunneler.Tunneler, proxyTransport *http.Transport) (*master.Config, error) {

    // ...

    genericConfig := genericapiserver.NewConfig(legacyscheme.Codecs)

    // ...

    config := &master.Config{

        GenericConfig: genericConfig,

        // ...

    }

    return config, nil

}

当一个请求到达apiserver时,会经过一系列的过滤器(Filter)和拦截器(Interceptor)的处理,例如认证、授权、准入控制等。这些插件式的扩展点使得Kubernetes能够灵活地控制请求的访问和操作。

k8s.io/apiserver/pkg/server/handler.go中,我们可以看到请求的处理链:

func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {

    handler := genericapifilters.WithAuthorization(apiHandler, c.Authorization.Authorizer, c.Serializer)

    handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.LongRunningFunc)

    handler = genericapifilters.WithImpersonation(handler, c.Authorization.Authorizer, c.Serializer)

    // ...

    handler = genericfilters.WithPanicRecovery(handler)

    return handler

}

经过一系列的处理后,请求最终会被转发到对应的REST资源上,由相应的处理函数进行操作。

二、scheduler:智能调度的核心

scheduler是Kubernetes中负责Pod调度的核心组件。它根据预定义的调度策略和算法,将新创建的Pod分配到最优的Node上运行。

让我们从kubernetes/cmd/kube-scheduler/app/server.goRun函数开始,追踪scheduler的启动流程。

func Run(cc schedulerserverconfig.CompletedConfig, stopCh <-chan struct{}) error {

    // ...

    sched, err := scheduler.New(cc.Client,

        cc.InformerFactory.Core().V1().Nodes(),

        cc.PodInformer,

        cc.InformerFactory.Core().V1().PersistentVolumes(),

        cc.InformerFactory.Core().V1().PersistentVolumeClaims(),

        cc.InformerFactory.Core().V1().ReplicationControllers(),

        cc.InformerFactory.Apps().V1().ReplicaSets(),

        cc.InformerFactory.Apps().V1().StatefulSets(),

        cc.InformerFactory.Core().V1().Services(),

        cc.InformerFactory.Policy().V1beta1().PodDisruptionBudgets(),

        cc.InformerFactory.Storage().V1().StorageClasses(),

        cc.Recorder,

        cc.ComponentConfig.AlgorithmSource,

        stopCh,

        scheduler.WithName(cc.ComponentConfig.SchedulerName),

        scheduler.WithHardPodAffinitySymmetricWeight(cc.ComponentConfig.HardPodAffinitySymmetricWeight),

        scheduler.WithPreemptionDisabled(cc.ComponentConfig.DisablePreemption),

        scheduler.WithPercentageOfNodesToScore(cc.ComponentConfig.PercentageOfNodesToScore),

        scheduler.WithBindTimeoutSeconds(*cc.ComponentConfig.BindTimeoutSeconds))

    // ...

    sched.Run()

    return nil

}

scheduler.New函数中,scheduler会创建一个名为Scheduler的对象,并配置各种调度算法和策略。Scheduler会监听Kubernetes的资源事件,当有新的Pod需要调度时,会执行预选(Predicates)和优选(Priorities)两个阶段的调度算法。

预选阶段会根据Pod的资源需求和约束条件,过滤掉不满足要求的Node。例如,如果Pod要求GPU,但Node没有GPU资源,则该Node会被过滤掉。

优选阶段则会对预选阶段筛选出的Node进行打分,根据Node的负载、亲和性、资源使用率等指标,选出得分最高的Node作为最优的调度结果。

k8s.io/kubernetes/pkg/scheduler/core/generic_scheduler.go中,我们可以看到调度算法的核心逻辑:

func (g *genericScheduler) Schedule(pod *v1.Pod, pluginContext *framework.PluginContext) (result ScheduleResult, err error) {

    // ...

    feasibleNodes, diagnosis, err := g.findNodesThatFitPod(ctx, extenders, fwk, state, pod)

    if err != nil {

        return result, err

    }

    // ...

    priorityList, err := g.prioritizeNodes(ctx, extenders, fwk, state, pod, feasibleNodes)

    if err != nil {

        return result, err

    }

    // ...

    host, err := g.selectHost(priorityList)

    return ScheduleResult{

        SuggestedHost:  host,

        EvaluatedNodes: len(feasibleNodes) + len(diagnosis.NodeToStatusMap),

        FeasibleNodes:  len(feasibleNodes),

    }, err

}

最终,scheduler会将调度结果通过apiserver更新到Pod对象上,将Pod分配到选定的Node上运行。

三、controller-manager:状态协调的守护者

controller-manager是Kubernetes中负责维护集群状态的核心组件。它运行着各种控制器(Controller),通过apiserver监听资源对象的变化,并触发相应的控制循环(Control Loop),使得集群的实际状态不断地向期望状态收敛。

让我们从kubernetes/cmd/kube-controller-manager/app/controllermanager.goRun函数开始,追踪controller-manager的启动流程。

func Run(c *config.CompletedConfig, stopCh <-chan struct{}) error {

    // ...

    run := func(ctx context.Context, startSATokenController InitFunc, initFuncs map[string]InitFunc) {

        controllerContext, err := CreateControllerContext(c, rootClientBuilder, clientBuilder, ctx.Done())

        if err != nil {

            klog.Fatalf("error building controller context: %v", err)

        }

        // ...

        for controllerName, initFn := range initFuncs {

            if !ctx.Err() {

                klog.V(1).Infof("Starting %q", controllerName)

                started, err := initFn(ctx, controllerContext)

                // ...

            }

        }

        // ...

    }

 

    run(context.TODO(), saTokenControllerInitFunc, NewControllerInitializers(completedConfig.ControllerClientBuilder))

    return nil

}

run函数中,controller-manager会为每个控制器创建一个上下文对象ControllerContext,并调用每个控制器的初始化函数initFn,启动控制循环。

例如,让我们以ReplicaSet控制器为例,看看它是如何工作的。

kubernetes/pkg/controller/replicaset/replica_set.go中,我们可以找到ReplicaSet控制器的核心逻辑:

func (rsc *ReplicaSetController) syncReplicaSet(key string) error {

    // ...

    rs, err := rsc.rsLister.ReplicaSets(namespace).Get(name)

    // ...

    selector, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector)

    // ...

    allPods, err := rsc.podLister.Pods(rs.Namespace).List(labels.Everything())

    // ...

    filteredPods := controller.FilterActivePods(allPods)

    filteredPods, err = rsc.claimPods(rs, selector, filteredPods)

    // ...

    diff := len(filteredPods) - int(*(rs.Spec.Replicas))

    if diff < 0 {

        diff *= -1

        if diff > rsc.burstReplicas {

            diff = rsc.burstReplicas

        }

        // ...

        rsc.expectations.ExpectCreations(rsKey, diff)

        // ...

    } else if diff > 0 {

        if diff > rsc.burstReplicas {

            diff = rsc.burstReplicas

        }

        // ...

        rsc.expectations.ExpectDeletions(rsKey, getPodKeys(podsToDelete))

        // ...

    }

    return nil

}

ReplicaSet控制器会通过apiserver监听ReplicaSet和Pod对象的变化,当实际的Pod数量与期望的副本数不一致时,会触发控制循环,根据差值创建或删除Pod,使得ReplicaSet的实际状态与期望状态保持一致。

类似地,Kubernetes中的其他控制器,如Deployment、DaemonSet、StatefulSet等,都遵循类似的工作模式,通过不断地协调集群状态,维护着Kubernetes的高可用和稳定性。

总结

本文从源码层面深入剖析了Kubernetes的三个核心组件:apiserver、scheduler和controller-manager。我们追踪了它们的启动流程,分析了其中的关键逻辑和设计思想。

apiserver作为Kubernetes的门户,提供了统一的REST API接口,并通过一系列的过滤器和拦截器,实现了灵活的准入控制和安全策略。

scheduler则负责Pod的调度,通过预选和优选两个阶段的调度算法,将Pod分配到最优的Node上运行,提高了集群的资源利用率和性能。

controller-manager通过各种控制器,不断地监听集群状态的变化,并触发控制循环,使得集群的实际状态向期望状态收敛,维护着Kubernetes的高可用和稳定性。

掌握了这三个核心组件的工作原理和源码实现,对于深入理解Kubernetes的架构设计和运行机制有着重要的意义。同时,这也为我们进一步扩展和优化Kubernetes,以满足更加复杂多变的业务场景提供了坚实的基础。

 

0条评论
0 / 1000
易乾
593文章数
0粉丝数
易乾
593 文章 | 0 粉丝
原创

深入剖析Kubernetes核心组件:apiserver、scheduler的源码级分析

2024-05-30 03:12:55
5
0

Kubernetes的架构中,apiserver、scheduler和controller-manager这三个核心组件扮演着至关重要的角色。它们共同协作,维护着整个集群的状态和运行。本文将从源码层面深入分析这三个组件的工作原理,揭示Kubernetes背后的设计思想和实现细节。

一、apiserver:Kubernetes的门户

apiserver是Kubernetes的核心组件之一,它提供了集群的统一入口,负责接收、验证和处理所有的REST请求。同时,它也是集群状态的唯一真实来源(Single Source of Truth),维护着所有资源对象的最新状态。

让我们从kubernetes/cmd/kube-apiserver/apiserver.goRun函数开始,追踪apiserver的启动流程。

func Run(completeOptions completedServerRunOptions, stopCh <-chan struct{}) error {

    // ...

    server, err := CreateServerChain(completeOptions, stopCh)

    if err != nil {

        return err

    }

 

    return server.PrepareRun().Run(stopCh)

}

CreateServerChain函数中,apiserver会创建一个名为GenericAPIServer的通用API服务器,并注册各种资源对象的RESTful API。

func CreateKubeAPIServerConfig(s completedConfig, nodeTunneler tunneler.Tunneler, proxyTransport *http.Transport) (*master.Config, error) {

    // ...

    genericConfig := genericapiserver.NewConfig(legacyscheme.Codecs)

    // ...

    config := &master.Config{

        GenericConfig: genericConfig,

        // ...

    }

    return config, nil

}

当一个请求到达apiserver时,会经过一系列的过滤器(Filter)和拦截器(Interceptor)的处理,例如认证、授权、准入控制等。这些插件式的扩展点使得Kubernetes能够灵活地控制请求的访问和操作。

k8s.io/apiserver/pkg/server/handler.go中,我们可以看到请求的处理链:

func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {

    handler := genericapifilters.WithAuthorization(apiHandler, c.Authorization.Authorizer, c.Serializer)

    handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.LongRunningFunc)

    handler = genericapifilters.WithImpersonation(handler, c.Authorization.Authorizer, c.Serializer)

    // ...

    handler = genericfilters.WithPanicRecovery(handler)

    return handler

}

经过一系列的处理后,请求最终会被转发到对应的REST资源上,由相应的处理函数进行操作。

二、scheduler:智能调度的核心

scheduler是Kubernetes中负责Pod调度的核心组件。它根据预定义的调度策略和算法,将新创建的Pod分配到最优的Node上运行。

让我们从kubernetes/cmd/kube-scheduler/app/server.goRun函数开始,追踪scheduler的启动流程。

func Run(cc schedulerserverconfig.CompletedConfig, stopCh <-chan struct{}) error {

    // ...

    sched, err := scheduler.New(cc.Client,

        cc.InformerFactory.Core().V1().Nodes(),

        cc.PodInformer,

        cc.InformerFactory.Core().V1().PersistentVolumes(),

        cc.InformerFactory.Core().V1().PersistentVolumeClaims(),

        cc.InformerFactory.Core().V1().ReplicationControllers(),

        cc.InformerFactory.Apps().V1().ReplicaSets(),

        cc.InformerFactory.Apps().V1().StatefulSets(),

        cc.InformerFactory.Core().V1().Services(),

        cc.InformerFactory.Policy().V1beta1().PodDisruptionBudgets(),

        cc.InformerFactory.Storage().V1().StorageClasses(),

        cc.Recorder,

        cc.ComponentConfig.AlgorithmSource,

        stopCh,

        scheduler.WithName(cc.ComponentConfig.SchedulerName),

        scheduler.WithHardPodAffinitySymmetricWeight(cc.ComponentConfig.HardPodAffinitySymmetricWeight),

        scheduler.WithPreemptionDisabled(cc.ComponentConfig.DisablePreemption),

        scheduler.WithPercentageOfNodesToScore(cc.ComponentConfig.PercentageOfNodesToScore),

        scheduler.WithBindTimeoutSeconds(*cc.ComponentConfig.BindTimeoutSeconds))

    // ...

    sched.Run()

    return nil

}

scheduler.New函数中,scheduler会创建一个名为Scheduler的对象,并配置各种调度算法和策略。Scheduler会监听Kubernetes的资源事件,当有新的Pod需要调度时,会执行预选(Predicates)和优选(Priorities)两个阶段的调度算法。

预选阶段会根据Pod的资源需求和约束条件,过滤掉不满足要求的Node。例如,如果Pod要求GPU,但Node没有GPU资源,则该Node会被过滤掉。

优选阶段则会对预选阶段筛选出的Node进行打分,根据Node的负载、亲和性、资源使用率等指标,选出得分最高的Node作为最优的调度结果。

k8s.io/kubernetes/pkg/scheduler/core/generic_scheduler.go中,我们可以看到调度算法的核心逻辑:

func (g *genericScheduler) Schedule(pod *v1.Pod, pluginContext *framework.PluginContext) (result ScheduleResult, err error) {

    // ...

    feasibleNodes, diagnosis, err := g.findNodesThatFitPod(ctx, extenders, fwk, state, pod)

    if err != nil {

        return result, err

    }

    // ...

    priorityList, err := g.prioritizeNodes(ctx, extenders, fwk, state, pod, feasibleNodes)

    if err != nil {

        return result, err

    }

    // ...

    host, err := g.selectHost(priorityList)

    return ScheduleResult{

        SuggestedHost:  host,

        EvaluatedNodes: len(feasibleNodes) + len(diagnosis.NodeToStatusMap),

        FeasibleNodes:  len(feasibleNodes),

    }, err

}

最终,scheduler会将调度结果通过apiserver更新到Pod对象上,将Pod分配到选定的Node上运行。

三、controller-manager:状态协调的守护者

controller-manager是Kubernetes中负责维护集群状态的核心组件。它运行着各种控制器(Controller),通过apiserver监听资源对象的变化,并触发相应的控制循环(Control Loop),使得集群的实际状态不断地向期望状态收敛。

让我们从kubernetes/cmd/kube-controller-manager/app/controllermanager.goRun函数开始,追踪controller-manager的启动流程。

func Run(c *config.CompletedConfig, stopCh <-chan struct{}) error {

    // ...

    run := func(ctx context.Context, startSATokenController InitFunc, initFuncs map[string]InitFunc) {

        controllerContext, err := CreateControllerContext(c, rootClientBuilder, clientBuilder, ctx.Done())

        if err != nil {

            klog.Fatalf("error building controller context: %v", err)

        }

        // ...

        for controllerName, initFn := range initFuncs {

            if !ctx.Err() {

                klog.V(1).Infof("Starting %q", controllerName)

                started, err := initFn(ctx, controllerContext)

                // ...

            }

        }

        // ...

    }

 

    run(context.TODO(), saTokenControllerInitFunc, NewControllerInitializers(completedConfig.ControllerClientBuilder))

    return nil

}

run函数中,controller-manager会为每个控制器创建一个上下文对象ControllerContext,并调用每个控制器的初始化函数initFn,启动控制循环。

例如,让我们以ReplicaSet控制器为例,看看它是如何工作的。

kubernetes/pkg/controller/replicaset/replica_set.go中,我们可以找到ReplicaSet控制器的核心逻辑:

func (rsc *ReplicaSetController) syncReplicaSet(key string) error {

    // ...

    rs, err := rsc.rsLister.ReplicaSets(namespace).Get(name)

    // ...

    selector, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector)

    // ...

    allPods, err := rsc.podLister.Pods(rs.Namespace).List(labels.Everything())

    // ...

    filteredPods := controller.FilterActivePods(allPods)

    filteredPods, err = rsc.claimPods(rs, selector, filteredPods)

    // ...

    diff := len(filteredPods) - int(*(rs.Spec.Replicas))

    if diff < 0 {

        diff *= -1

        if diff > rsc.burstReplicas {

            diff = rsc.burstReplicas

        }

        // ...

        rsc.expectations.ExpectCreations(rsKey, diff)

        // ...

    } else if diff > 0 {

        if diff > rsc.burstReplicas {

            diff = rsc.burstReplicas

        }

        // ...

        rsc.expectations.ExpectDeletions(rsKey, getPodKeys(podsToDelete))

        // ...

    }

    return nil

}

ReplicaSet控制器会通过apiserver监听ReplicaSet和Pod对象的变化,当实际的Pod数量与期望的副本数不一致时,会触发控制循环,根据差值创建或删除Pod,使得ReplicaSet的实际状态与期望状态保持一致。

类似地,Kubernetes中的其他控制器,如Deployment、DaemonSet、StatefulSet等,都遵循类似的工作模式,通过不断地协调集群状态,维护着Kubernetes的高可用和稳定性。

总结

本文从源码层面深入剖析了Kubernetes的三个核心组件:apiserver、scheduler和controller-manager。我们追踪了它们的启动流程,分析了其中的关键逻辑和设计思想。

apiserver作为Kubernetes的门户,提供了统一的REST API接口,并通过一系列的过滤器和拦截器,实现了灵活的准入控制和安全策略。

scheduler则负责Pod的调度,通过预选和优选两个阶段的调度算法,将Pod分配到最优的Node上运行,提高了集群的资源利用率和性能。

controller-manager通过各种控制器,不断地监听集群状态的变化,并触发控制循环,使得集群的实际状态向期望状态收敛,维护着Kubernetes的高可用和稳定性。

掌握了这三个核心组件的工作原理和源码实现,对于深入理解Kubernetes的架构设计和运行机制有着重要的意义。同时,这也为我们进一步扩展和优化Kubernetes,以满足更加复杂多变的业务场景提供了坚实的基础。

 

文章来自个人专栏
编程知识
593 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0