在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,以滿足更加復雜多變的業務場景提供了堅實的基礎。