欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語言kube-scheduler深度剖析開發(fā)之scheduler初始化

 更新時(shí)間:2023年04月23日 10:44:39   作者:俯仰之間  
這篇文章主要介紹了Go語言kube-scheduler深度剖析開發(fā)之scheduler初始化實(shí)現(xiàn)過程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

為了深入學(xué)習(xí) kube-scheduler,本系從源碼和實(shí)戰(zhàn)角度深度學(xué) 習(xí)kube-scheduler,該系列一共分6篇文章,如下:

  • kube-scheduler 整體架構(gòu)
  • 本文 :初始化一個(gè) scheduler
  • 一個(gè) Pod 是如何調(diào)度的
  • 如何開發(fā)一個(gè)屬于自己的scheduler插件
  • 開發(fā)一個(gè) prefilter 擴(kuò)展點(diǎn)的插件
  • 開發(fā)一個(gè) socre 擴(kuò)展點(diǎn)的插件

上一篇,我們說了 kube-scheduler 的整體架構(gòu),是從整體的架構(gòu)方面來考慮的,本文我們說說 kube-scheduler 是如何初始化出來的,kube-scheduler 里面都有些什么東西。

因?yàn)?kube-scheduler 源碼內(nèi)容比較多,對(duì)于那些不是關(guān)鍵的東西,就忽略不做討論。

Scheduler之Profiles

下面我們先看下 Scheduler 的結(jié)構(gòu)

type Scheduler struct {
   Cache internalcache.Cache
   Extenders []framework.Extender
   NextPod func() *framework.QueuedPodInfo
   FailureHandler FailureHandlerFn
   SchedulePod func(ctx context.Context, fwk framework.Framework, state  *framework.CycleState, pod *v1.Pod) (ScheduleResult, error)
   StopEverything <-chan struct{}
   SchedulingQueue internalqueue.SchedulingQueue
   Profiles profile.Map
   client clientset.Interface
   nodeInfoSnapshot *internalcache.Snapshot
   percentageOfNodesToScore int32
   nextStartNodeIndex int
}

上一篇我們說過,為一個(gè) Pod 選擇一個(gè) Node 是按照固定順序運(yùn)行擴(kuò)展點(diǎn)的;在擴(kuò)展點(diǎn)內(nèi),是按照插件注冊(cè)的順序運(yùn)行插件,如下圖

上面的這些擴(kuò)展點(diǎn)在 kube-scheduler 中是固定的,而且也不支持增加擴(kuò)展點(diǎn)(實(shí)際上有這些擴(kuò)展點(diǎn)已經(jīng)足夠了),而且擴(kuò)展點(diǎn)順序也是固定執(zhí)行的。

下圖是插件(以preFilter為例)運(yùn)行的順序,擴(kuò)展點(diǎn)內(nèi)的插件,你既可以調(diào)整插件的執(zhí)行順序(實(shí)際很少會(huì)修改默認(rèn)的插件執(zhí)行順序),可以關(guān)閉某個(gè)內(nèi)置插件,還可以增加自己開發(fā)的插件。

那么這些插件是怎么注冊(cè)的,注冊(cè)在哪里呢,自己開發(fā)的插件又是怎么加進(jìn)去的呢?

我們來看下 Scheduler 里面最重要的一個(gè)成員:Profiles profile.Map

// 路徑:pkg/scheduler/profile/profile.go
// Map holds frameworks indexed by scheduler name.
type Map map[string]framework.Framework

Profiles 是一個(gè) key 為 scheduler name,value 是 framework.Framework 的map,表示根據(jù) scheduler name 來獲取 framework.Framework 類型的值,所以可以有多個(gè)scheduler?;蛟S你在使用 k8s 的時(shí)候沒有關(guān)注過 pod 或 deploment 里面的 scheduler,因?yàn)槟銢]有指定的話,k8s 就會(huì)自動(dòng)設(shè)置為默認(rèn)的調(diào)度器,下圖是 deployment 中未指定 schedulerName 被設(shè)置了默認(rèn)調(diào)度器的一個(gè)deployment

假設(shè)現(xiàn)在我想要使用自己開發(fā)的一個(gè)名叫 my-scheduler-1 的調(diào)度器,這個(gè)調(diào)度器在 preFilter 擴(kuò)展點(diǎn)中增加了 zoneLabel 插件,怎么做?

使用 kubeadm 部署的 k8s 集群,會(huì)在 /etc/kubernetes/manifests 目錄下創(chuàng)建 kube-scheduler.yaml 文件,kubelet 會(huì)根據(jù)這個(gè)文件自動(dòng)拉起來一個(gè)靜態(tài) Pod,一個(gè) kube-scheduler pod就被創(chuàng)建了,而且這個(gè) kube-scheduler 運(yùn)行的參數(shù)是直接在命令行上指定的。

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    component: kube-scheduler
    tier: control-plane
  name: kube-scheduler
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-scheduler
    - --address=0.0.0.0
    - --authentication-kubeconfig=/etc/kubernetes/scheduler.conf
    - --authorization-kubeconfig=/etc/kubernetes/scheduler.conf
    - --bind-address=127.0.0.1
    - --kubeconfig=/etc/kubernetes/scheduler.conf
    - --leader-elect=true
    image: k8s.gcr.io/kube-scheduler:v1.16.8
    ....

其實(shí) kube-scheduler 運(yùn)行的時(shí)候可以指定配置文件,而不直接把參數(shù)寫在啟動(dòng)命令上,如下形式。

./kube-scheduler --config /etc/kube-scheduler.conf

于是乎,我們就可以在配置文件中配置我們調(diào)度器的插件了

apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
leaderElection:
  leaderElect: true
clientConnection:
  kubeconfig: "/etc/kubernetes/scheduler.conf"
profiles:
- schedulerName: my-scheduler
  plugins:
    preFilter:
      enabled:
        - name: zoneLabel
      disabled:
        - name: NodePorts

我們可以使用 enabled,disabled 開關(guān)來關(guān)閉或打開某個(gè)插件。 通過配置文件,還可以控制擴(kuò)展點(diǎn)的調(diào)用順序,規(guī)則如下:

  • 如果某個(gè)擴(kuò)展點(diǎn)沒有配置對(duì)應(yīng)的擴(kuò)展,調(diào)度框架將使用默認(rèn)插件中的擴(kuò)展
  • 如果為某個(gè)擴(kuò)展點(diǎn)配置且激活了擴(kuò)展,則調(diào)度框架將先調(diào)用默認(rèn)插件的擴(kuò)展,再調(diào)用配置中的擴(kuò)展
  • 默認(rèn)插件的擴(kuò)展始終被最先調(diào)用,然后按照 KubeSchedulerConfiguration 中擴(kuò)展的激活 enabled 順序逐個(gè)調(diào)用擴(kuò)展點(diǎn)的擴(kuò)展
  • 可以先禁用默認(rèn)插件的擴(kuò)展,然后在 enabled 列表中的某個(gè)位置激活默認(rèn)插件的擴(kuò)展,這種做法可以改變默認(rèn)插件的擴(kuò)展被調(diào)用時(shí)的順序

還可以添加多個(gè)調(diào)度器,在 deployment 等控制器中指定自己想要使用的調(diào)度器即可:

apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
leaderElection:
  leaderElect: true
clientConnection:
  kubeconfig: "/etc/kubernetes/scheduler.conf"
profiles:
- schedulerName: my-scheduler-1
  plugins:
    preFilter:
      enabled:
        - name: zoneLabel
- schedulerName: my-scheduler-2
  plugins:
    queueSort:
      enabled:
        - name: mySort

當(dāng)然了,現(xiàn)在我們?cè)谂渲梦募卸x的 mySort,zoneLabel 這樣的插件還不能使用,我們需要開發(fā)具體的插件注冊(cè)進(jìn)去,才能正常運(yùn)行,后面的文章會(huì)詳細(xì)講。

好了,現(xiàn)在 Profiles 成員(一個(gè)map)已經(jīng)包含了兩個(gè)元素,{"my-scheduler-1": framework.Framework ,"my-scheduler-2": framework.Framework}。當(dāng)一個(gè) Pod 需要被調(diào)度的時(shí)候,kube-scheduler 會(huì)先取出 Pod 的 schedulerName 字段的值,然后通過 Profiles[schedulerName],拿到 framework.Framework 對(duì)象,進(jìn)而使用這個(gè)對(duì)象開始調(diào)度,我們可以用下面這種張圖總結(jié)下上面描述的各個(gè)對(duì)象的關(guān)系。

那么重點(diǎn)就來到了 framework.Framework ,下面是 framework.Framework 的定義:

// pkg/scheduler/framework/interface.go
type Framework interface {
   Handle
   QueueSortFunc() LessFunc
   RunPreFilterPlugins(ctx context.Context, state *CycleState, pod *v1.Pod) (*PreFilterResult, *Status)
   RunPostFilterPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, filteredNodeStatusMap NodeToStatusMap) (*PostFilterResult, *Status)
   RunPreBindPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) *Status
   RunPostBindPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string)
   RunReservePluginsReserve(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) *Status
   RunReservePluginsUnreserve(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string)
   RunPermitPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) *Status
   WaitOnPermit(ctx context.Context, pod *v1.Pod) *Status
   RunBindPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) *Status
   HasFilterPlugins() bool
   HasPostFilterPlugins() bool
   HasScorePlugins() bool
   ListPlugins() *config.Plugins
   ProfileName() string
}

Framework 是一個(gè)接口,需要實(shí)現(xiàn)的方法大部分形式為:Run***Plugins,也就是運(yùn)行某個(gè)擴(kuò)展點(diǎn)的插件,那么只要實(shí)現(xiàn)這個(gè) Framework 接口就可以對(duì) Pod 進(jìn)行調(diào)度了。那么需要用戶自己實(shí)現(xiàn)么?答案是不用,kube-scheduler 已經(jīng)有一個(gè)該接口的實(shí)現(xiàn):frameworkImpl

// pkg/scheduler/framework/runtime/framework.go
type frameworkImpl struct {
	registry             Registry
	snapshotSharedLister framework.SharedLister
	waitingPods          *waitingPodsMap
	scorePluginWeight    map[string]int
	queueSortPlugins     []framework.QueueSortPlugin
	preFilterPlugins     []framework.PreFilterPlugin
	filterPlugins        []framework.FilterPlugin
	postFilterPlugins    []framework.PostFilterPlugin
	preScorePlugins      []framework.PreScorePlugin
	scorePlugins         []framework.ScorePlugin
	reservePlugins       []framework.ReservePlugin
	preBindPlugins       []framework.PreBindPlugin
	bindPlugins          []framework.BindPlugin
	postBindPlugins      []framework.PostBindPlugin
	permitPlugins        []framework.PermitPlugin
	clientSet       clientset.Interface
	kubeConfig      *restclient.Config
	eventRecorder   events.EventRecorder
	informerFactory informers.SharedInformerFactory
	metricsRecorder *metricsRecorder
	profileName     string
	extenders []framework.Extender
	framework.PodNominator
	parallelizer parallelize.Parallelizer
}

frameworkImpl 這個(gè)結(jié)構(gòu)體里面包含了每個(gè)擴(kuò)展點(diǎn)插件數(shù)組,所以某個(gè)擴(kuò)展點(diǎn)要被執(zhí)行的時(shí)候,只要遍歷這個(gè)數(shù)組里面的所有插件,然后執(zhí)行這些插件就可以了。我們看看 framework.FilterPlugin 是怎么定義的(其他的也類似):

type Plugin interface {
	Name() string
}
type FilterPlugin interface {
	Plugin
	Filter(ctx context.Context, state *CycleState, pod *v1.Pod, nodeInfo *NodeInfo) *Status
}

插件數(shù)組的類型是一個(gè)接口,那么某個(gè)插件只要實(shí)現(xiàn)了這個(gè)接口就可以被運(yùn)行。實(shí)際上,我們前面說的那些默認(rèn)插件,都實(shí)現(xiàn)了這個(gè)接口,在目錄 pkg/scheduler/framework/plugins 目錄下面包含了所有內(nèi)置插件的實(shí)現(xiàn),主要就是對(duì)上面說的這個(gè)插件接口的實(shí)現(xiàn)。我們可以簡單用圖描述下 Pod被調(diào)度的時(shí)候執(zhí)行插件的流程

那么這些默認(rèn)插件是怎么加到framework里面的,自定義插件又是怎么加進(jìn)來的呢?

分三步:

  • 根據(jù)配置文件(--config指定的)、系統(tǒng)默認(rèn)的插件,按照擴(kuò)展點(diǎn)生成需要被加載的插件數(shù)組(包括插件名字,權(quán)重信息),也就是初始化 KubeSchedulerConfiguration 中的 Profiles 成員。
type KubeSchedulerConfiguration struct {
  metav1.TypeMeta
  Parallelism int32
  LeaderElection componentbaseconfig.LeaderElectionConfiguration
  ClientConnection componentbaseconfig.ClientConnectionConfiguration
  HealthzBindAddress string
  MetricsBindAddress string
  componentbaseconfig.DebuggingConfiguration
  PercentageOfNodesToScore int32
  PodInitialBackoffSeconds int64
  PodMaxBackoffSeconds int64
  Profiles []KubeSchedulerProfile
  Extenders []Extender
}
  • 創(chuàng)建 registry 集合,這個(gè)集合內(nèi)是每個(gè)插件實(shí)例化函數(shù),也就是 插件名字->插件實(shí)例化函數(shù)的映射,通俗一點(diǎn)說就是告訴系統(tǒng):1.我叫王二; 2. 你應(yīng)該怎么把我創(chuàng)建出來。那么張三、李四、王五分別告訴系統(tǒng)怎么創(chuàng)建自己,就組成了這個(gè)集合。
type PluginFactory = func(configuration runtime.Object, f framework.Handle) (framework.Plugin, error)
type Registry map[string]PluginFactory

這個(gè)集合是內(nèi)置(叫inTree)默認(rèn)的插件映射和用戶自定義(outOfTree)的插件映射的并集,內(nèi)置的映射通過下面函數(shù)創(chuàng)建:

// pkg/scheduler/framework/plugins/registry.go
func NewInTreeRegistry() runtime.Registry {
	fts := plfeature.Features{
		EnableReadWriteOncePod:                       feature.DefaultFeatureGate.Enabled(features.ReadWriteOncePod),
		EnableVolumeCapacityPriority:                 feature.DefaultFeatureGate.Enabled(features.VolumeCapacityPriority),
		EnableMinDomainsInPodTopologySpread:          feature.DefaultFeatureGate.Enabled(features.MinDomainsInPodTopologySpread),
		EnableNodeInclusionPolicyInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.NodeInclusionPolicyInPodTopologySpread),
	}
	return runtime.Registry{
		selectorspread.Name:                  selectorspread.New,
		imagelocality.Name:                   imagelocality.New,
		tainttoleration.Name:                 tainttoleration.New,
		nodename.Name:                        nodename.New,
		nodeports.Name:                       nodeports.New,
		nodeaffinity.Name:                    nodeaffinity.New,
		podtopologyspread.Name:               runtime.FactoryAdapter(fts, podtopologyspread.New),
		nodeunschedulable.Name:               nodeunschedulable.New,
		noderesources.Name:                   runtime.FactoryAdapter(fts, noderesources.NewFit),
		noderesources.BalancedAllocationName: runtime.FactoryAdapter(fts, noderesources.NewBalancedAllocation),
		volumebinding.Name:                   runtime.FactoryAdapter(fts, volumebinding.New),
		volumerestrictions.Name:              runtime.FactoryAdapter(fts, volumerestrictions.New),
		volumezone.Name:                      volumezone.New,
		nodevolumelimits.CSIName:             runtime.FactoryAdapter(fts, nodevolumelimits.NewCSI),
		nodevolumelimits.EBSName:             runtime.FactoryAdapter(fts, nodevolumelimits.NewEBS),
		nodevolumelimits.GCEPDName:           runtime.FactoryAdapter(fts, nodevolumelimits.NewGCEPD),
		nodevolumelimits.AzureDiskName:       runtime.FactoryAdapter(fts, nodevolumelimits.NewAzureDisk),
		nodevolumelimits.CinderName:          runtime.FactoryAdapter(fts, nodevolumelimits.NewCinder),
		interpodaffinity.Name:                interpodaffinity.New,
		queuesort.Name:                       queuesort.New,
		defaultbinder.Name:                   defaultbinder.New,
		defaultpreemption.Name:               runtime.FactoryAdapter(fts, defaultpreemption.New),
	}
}

那么用戶自定義的插件怎么來的呢?這里咱們先不展開,在后面插件開發(fā)的時(shí)候再詳細(xì)講,不影響我們理解。我們假設(shè)用戶自定義的也已經(jīng)生成了 registry,下面的代碼就是把他們合并在一起

// pkg/scheduler/scheduler.go
registry := frameworkplugins.NewInTreeRegistry()
if err := registry.Merge(options.frameworkOutOfTreeRegistry); err != nil {
	return nil, err
}

現(xiàn)在內(nèi)置插件和系統(tǒng)默認(rèn)插件的實(shí)例化函數(shù)映射已經(jīng)創(chuàng)建好了

  • 將(1)中每個(gè)擴(kuò)展點(diǎn)的每個(gè)插件(就是插件名字)拿出來,去(2)的映射(map)中獲取實(shí)例化函數(shù),然后運(yùn)行這個(gè)實(shí)例化函數(shù),最后把這個(gè)實(shí)例化出來的插件(可以被運(yùn)行的)追加到上面提到過的 frameworkImpl 中對(duì)應(yīng)擴(kuò)展點(diǎn)數(shù)組中,這樣后面要運(yùn)行某個(gè)擴(kuò)展點(diǎn)插件的時(shí)候就可以遍歷運(yùn)行就可以了。我們可以把上述過程用下圖表示

Scheduler 之 SchedulingQueue

上面我們介紹了 Scheduler 第一個(gè)關(guān)鍵成員 Profiles 的初始化和作用,下面我們來談?wù)劦诙€(gè)關(guān)鍵成員:SchedulingQueue

// pkg/scheduler/scheduler.go
podQueue := internalqueue.NewSchedulingQueue(
	profiles[options.profiles[0].SchedulerName].QueueSortFunc(),
	informerFactory,
	// 1s
	internalqueue.WithPodInitialBackoffDuration(time.Duration(options.podInitialBackoffSeconds)*time.Second),
	// 10s
	internalqueue.WithPodMaxBackoffDuration(time.Duration(options.podMaxBackoffSeconds)*time.Second),
	internalqueue.WithPodNominator(nominator),
	internalqueue.WithClusterEventMap(clusterEventMap),
	// 5min
	internalqueue.WithPodMaxInUnschedulablePodsDuration(options.podMaxInUnschedulablePodsDuration),
)
func NewSchedulingQueue(
	lessFn framework.LessFunc,
	informerFactory informers.SharedInformerFactory,
	opts ...Option) SchedulingQueue {
	return NewPriorityQueue(lessFn, informerFactory, opts...)
}
type PriorityQueue struct {
  framework.PodNominator
  stop  chan struct{}
  clock clock.Clock
  podInitialBackoffDuration time.Duration
  podMaxBackoffDuration time.Duration
  podMaxInUnschedulablePodsDuration time.Duration
  lock sync.RWMutex
  cond sync.Cond
  activeQ *heap.Heap
  podBackoffQ *heap.Heap
  unschedulablePods *UnschedulablePods
  schedulingCycle int64
  moveRequestCycle int64
  clusterEventMap map[framework.ClusterEvent]sets.String
  closed bool
  nsLister listersv1.NamespaceLister
}

SchedulingQueue 是一個(gè) internalqueue.SchedulingQueue 的接口類型,PriorityQueue 對(duì)這個(gè)接口進(jìn)行了實(shí)現(xiàn),創(chuàng)建 Scheduler 的時(shí)候 SchedulingQueue 會(huì)被 PriorityQueue 類型對(duì)象賦值。

PriorityQueue 中有關(guān)鍵的3個(gè)成員:activeQ、podBackoffQ、unschedulablePods。

  • activeQ 是一個(gè)優(yōu)先隊(duì)列,用來存放待調(diào)度的 Pod,Pod 按照優(yōu)先級(jí)存放在隊(duì)列中
  • podBackoffQ 用來存放異常的 Pod, 該隊(duì)列里面的 Pod 會(huì)等待一定時(shí)間后被移動(dòng)到 activeQ 里面重新被調(diào)度
  • unschedulablePods 中會(huì)存放調(diào)度失敗的 Pod,它不是隊(duì)列,而是使用 map 來存放的,這個(gè) map 里面的 Pod 在一定條件下會(huì)被移動(dòng)到 activeQ 或 podBackoffQ 中

PriorityQueue 還有兩個(gè)方法:flushUnschedulablePodsLeftover 和 flushBackoffQCompleted

  • flushUnschedulablePodsLeftover:調(diào)度失敗的 Pod 如果滿足一定條件,這個(gè)函數(shù)會(huì)將這種 Pod 移動(dòng)到 activeQ 或 podBackoffQ
  • flushBackoffQCompleted:運(yùn)行異常的 Pod 等待時(shí)間完成后,flushBackoffQCompleted 將該 Pod 移動(dòng)到 activeQ

Scheduler 在啟動(dòng)的時(shí)候,會(huì)創(chuàng)建2個(gè)協(xié)程來定期運(yùn)行這兩個(gè)函數(shù)

func (p *PriorityQueue) Run() {
   go wait.Until(p.flushBackoffQCompleted, 1.0*time.Second, p.stop)
   go wait.Until(p.flushUnschedulablePodsLeftover, 30*time.Second, p.stop)
}

上面是定期對(duì) Pod 在這些隊(duì)列之間的轉(zhuǎn)換,那么除了定期刷新的方式,還有下面情況也會(huì)觸發(fā)隊(duì)列轉(zhuǎn)換:

  • 有新節(jié)點(diǎn)加入集群
  • 節(jié)點(diǎn)配置或狀態(tài)發(fā)生變化
  • 已經(jīng)存在的 Pod 發(fā)生變化
  • 集群內(nèi)有Pod被刪除

至于他們之間是如何轉(zhuǎn)換的,我們?cè)谙乱黄恼吕锩嬖敿?xì)介紹

Scheduler 之 cache

要說 cache 最大的作用就是提升 Scheduler 的效率,降低 kube-apiserver(本質(zhì)是 etcd)的壓力,在調(diào)用各個(gè)插件計(jì)算的時(shí)候所需要的 Node 信息和其他 Pod 信息都緩存在本地,在需要使用的時(shí)候直接從緩存獲取即可,而不需要調(diào)用 api 從 kube-apiserver 獲取。cache 類型是 internalcache.Cache 的接口,cacheImpl 實(shí)現(xiàn)了這個(gè)接口。

下面是 cacheImpl 的結(jié)構(gòu)

type Cache interface 
  NodeCount() int
  PodCount() (int, error)
  AssumePod(pod *v1.Pod) error
  FinishBinding(pod *v1.Pod) error
  ForgetPod(pod *v1.Pod) error
  AddPod(pod *v1.Pod) error
  UpdatePod(oldPod, newPod *v1.Pod) error
  RemovePod(pod *v1.Pod) error
  GetPod(pod *v1.Pod) (*v1.Pod, error)
  IsAssumedPod(pod *v1.Pod) (bool, error)
  AddNode(node *v1.Node) *framework.NodeInfo
  UpdateNode(oldNode, newNode *v1.Node) *framework.NodeInfo
  RemoveNode(node *v1.Node) error
  UpdateSnapshot(nodeSnapshot *Snapshot) error
  Dump() *Dump
}
type cacheImpl struct {
  stop   &lt;-chan struct{}
  ttl    time.Duration
  period time.Duration
  mu sync.RWMutex
  assumedPods sets.String
  podStates map[string]*podState
  nodes     map[string]*nodeInfoListItem
  headNode *nodeInfoListItem
  nodeTree *nodeTree
  imageStates map[string]*imageState
}

cacheImpl 中的 nodes 存放集群內(nèi)所有 Node 信息;podStates 存放所有 Pod 信息;,assumedPods 存放已經(jīng)調(diào)度成功但是還沒調(diào)用 kube-apiserver 的進(jìn)行綁定的(也就是還沒有執(zhí)行 bind 插件)的Pod,需要這個(gè)緩存的原因也是為了提升調(diào)度效率,將綁定和調(diào)度分開,因?yàn)榻壎ㄐ枰{(diào)用 kube-apiserver,這是一個(gè)重操作會(huì)消耗比較多的時(shí)間,所以 Scheduler 樂觀的假設(shè)調(diào)度已經(jīng)成功,然后返回去調(diào)度其他 Pod,而這個(gè) Pod 就會(huì)放入 assumedPods 中,并且也會(huì)放入到 podStates 中,后續(xù)其他 Pod 在進(jìn)行調(diào)度的時(shí)候,這個(gè) Pod 也會(huì)在插件的計(jì)算范圍內(nèi)(如親和性), 然后會(huì)新起協(xié)程進(jìn)行最后的綁定,要是最后綁定失敗了,那么這個(gè) Pod 的信息會(huì)從 assumedPods 和 podStates 移除,并且把這個(gè) Pod 重新放入 activeQ 中,重新被調(diào)度。

Scheduler 在啟動(dòng)時(shí)首先會(huì) list 一份全量的 Pod 和 Node 數(shù)據(jù)到上述的緩存中,后續(xù)通過 watch 的方式發(fā)現(xiàn)變化的 Node 和 Pod,然后將變化的 Node 或 Pod 更新到上述緩存中。

Scheduler 之 NextPod 和 SchedulePod

到了這里,調(diào)度框架 framework 和調(diào)度隊(duì)列 SchedulingQueue 都已經(jīng)創(chuàng)建出來了,現(xiàn)在是時(shí)候開始調(diào)度Pod了。

Scheduler 中有個(gè)成員 NextPod 會(huì)從 activeQ 隊(duì)列中嘗試獲取一個(gè)待調(diào)度的 Pod,該函數(shù)在 SchedulePod 中被調(diào)用,如下:

// 啟動(dòng) Scheduler
func (sched *Scheduler) Run(ctx context.Context) {
	sched.SchedulingQueue.Run()
	go wait.UntilWithContext(ctx, sched.scheduleOne, 0)
	&lt;-ctx.Done()
	sched.SchedulingQueue.Close()
}
// 嘗試調(diào)度一個(gè) Pod,所以 Pod 的調(diào)度入口
func (sched *Scheduler) scheduleOne(ctx context.Context) {
	// 會(huì)一直阻塞,直到獲取到一個(gè)Pod
	......
	podInfo := sched.NextPod()
    ......
}

NextPod 它被賦予如下函數(shù):

// pkg/scheduler/internal/queue/scheduling_queue.go
func MakeNextPodFunc(queue SchedulingQueue) func() *framework.QueuedPodInfo {
	return func() *framework.QueuedPodInfo {
		podInfo, err := queue.Pop()
		if err == nil {
			klog.V(4).InfoS("About to try and schedule pod", "pod", klog.KObj(podInfo.Pod))
			for plugin := range podInfo.UnschedulablePlugins {
				metrics.UnschedulableReason(plugin, podInfo.Pod.Spec.SchedulerName).Dec()
			}
			return podInfo
		}
		klog.ErrorS(err, "Error while retrieving next pod from scheduling queue")
		return nil
	}
}

Pop 會(huì)一直阻塞,直到 activeQ 長度大于0,然后去取出一個(gè) Pod 返回

// pkg/scheduler/internal/queue/scheduling_queue.go
func (p *PriorityQueue) Pop() (*framework.QueuedPodInfo, error) {
	p.lock.Lock()
	defer p.lock.Unlock()
	for p.activeQ.Len() == 0 {
		// When the queue is empty, invocation of Pop() is blocked until new item is enqueued.
		// When Close() is called, the p.closed is set and the condition is broadcast,
		// which causes this loop to continue and return from the Pop().
		if p.closed {
			return nil, fmt.Errorf(queueClosed)
		}
		p.cond.Wait()
	}
	obj, err := p.activeQ.Pop()
	if err != nil {
		return nil, err
	}
	pInfo := obj.(*framework.QueuedPodInfo)
	pInfo.Attempts++
	p.schedulingCycle++
	return pInfo, nil
}

到了這里我們就介紹完了 Scheduler 中最重要的幾個(gè)成員,簡單總結(jié)下:

  • Profiles: 存放插件對(duì)象,在運(yùn)行時(shí)可以遍歷擴(kuò)展點(diǎn)內(nèi)的所有插件運(yùn)行
  • SchedulerQueue:用來存放待調(diào)度 Pod,異常 Pod,調(diào)度失敗 Pod,他們相互可以轉(zhuǎn)換
  • cache:存放 Pod 和 Node 的信息,提升調(diào)度效率
  • NextPod 和 ScheduleOne:嘗試從 activeQ 獲取一個(gè) Pod,開始調(diào)度。

本文就到這,下一篇,我們會(huì)講一講一個(gè) Pod 提交后的調(diào)度流程。

以上就是Go語言kube-scheduler深度剖析開發(fā)之scheduler初始化的詳細(xì)內(nèi)容,更多關(guān)于go scheduler初始化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 一文探索Go中的函數(shù)使用方式

    一文探索Go中的函數(shù)使用方式

    在編程中,函數(shù)是基本構(gòu)建塊之一,Go語言以其簡潔明了的函數(shù)定義和調(diào)用語法而聞名,所以本文就來和大家聊聊Go中的函數(shù)概念及使用,感興趣的可以了解下
    2023-09-09
  • Golang結(jié)構(gòu)化日志包log/slog的使用詳解

    Golang結(jié)構(gòu)化日志包log/slog的使用詳解

    官方提供的用于打印日志的包是標(biāo)準(zhǔn)庫中的 log 包,該包雖然被廣泛使用,但是缺點(diǎn)也很多,所以Go 1.21新增的 log/slog 完美解決了以上問題,下面我們就來看看log/slog包的使用吧
    2023-09-09
  • Go語言函數(shù)的延遲調(diào)用(Deferred Code)詳解

    Go語言函數(shù)的延遲調(diào)用(Deferred Code)詳解

    本文將介紹Go語言函數(shù)和方法中的延遲調(diào)用,正如名稱一樣,這部分定義不會(huì)立即執(zhí)行,一般會(huì)在函數(shù)返回前再被調(diào)用,我們通過一些示例來了解一下延遲調(diào)用的使用場景
    2022-07-07
  • 使用Go語言實(shí)現(xiàn)谷歌翻譯功能

    使用Go語言實(shí)現(xiàn)谷歌翻譯功能

    這篇文章主要為大家詳細(xì)介紹了如何使用Go語言實(shí)現(xiàn)谷歌翻譯功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以參考下
    2024-02-02
  • Go語言LeetCode題解937重新排列日志文件

    Go語言LeetCode題解937重新排列日志文件

    這篇文章主要為大家介紹了Go語言LeetCode題解937重新排列日志文件,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • Go中時(shí)間與時(shí)區(qū)問題的深入講解

    Go中時(shí)間與時(shí)區(qū)問題的深入講解

    go語言中如果不設(shè)置指定的時(shí)區(qū),通過time.Now()獲取到的就是本地時(shí)區(qū),下面這篇文章主要給大家介紹了關(guān)于Go中時(shí)間與時(shí)區(qū)問題的相關(guān)資料,需要的朋友可以參考下
    2021-12-12
  • 一文帶你熟悉Go語言中函數(shù)的使用

    一文帶你熟悉Go語言中函數(shù)的使用

    這篇文章主要和大家分享一下Go語言中的函數(shù)的使用,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語言有一定的幫助,需要的小伙伴可以參考一下
    2022-11-11
  • Golang?基礎(chǔ)面試題集錦

    Golang?基礎(chǔ)面試題集錦

    這篇文章主要為大家介紹了Golang?基礎(chǔ)面試題集錦,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Golang中的Interface詳解

    Golang中的Interface詳解

    本文詳細(xì)講解了Golang中的Interface,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • Go構(gòu)建高性能的命令行工具使例詳解

    Go構(gòu)建高性能的命令行工具使例詳解

    這篇文章主要為大家介紹了Go構(gòu)建高性能的命令行工具使例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12

最新評(píng)論