在Kubernetes的架构中,apiserver、scheduler和controller-manager这三个核心组件扮演着至关重要的角色。它们共同协作,维护着整个集群的状态和运行。本文将从源码层面深入分析这三个组件的工作原理,揭示Kubernetes背后的设计思想和实现细节。
一、apiserver:Kubernetes的门户
apiserver是Kubernetes的核心组件之一,它提供了集群的统一入口,负责接收、验证和处理所有的REST请求。同时,它也是集群状态的唯一真实来源(Single Source of Truth),维护着所有资源对象的最新状态。
让我们从kubernetes/cmd/kube-apiserver/apiserver.go的Run函数开始,追踪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.go的Run函数开始,追踪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.go的Run函数开始,追踪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,以满足更加复杂多变的业务场景提供了坚实的基础。