浅谈 Kubernetes Scheduling-Framework 插件的实现
最近幾個(gè)月一直在研究 kubernetes 的 scheduling-framework 調(diào)度框架,發(fā)現(xiàn)還是十分有意思的,我自己也實(shí)現(xiàn)了一個(gè)基于 scheduling-framework 調(diào)度框架的自定義調(diào)度器,希望感興趣的同學(xué)一起學(xué)習(xí):https://github.com/NJUPT-ISL/Yoda-Scheduler
Scheduling-framework 調(diào)度框架
Kubernetes 的 scheduling-framework 調(diào)度框架(以下簡稱調(diào)度框架)是針對(duì)當(dāng)前 kubernetes 調(diào)度器的增強(qiáng),它不同于之前的 scheduler-extender,用戶可以編寫多個(gè)插件,這些插件可以在調(diào)度的不同階段作為原有調(diào)度器的擴(kuò)展,并且這些插件會(huì)和 kubernetes 原有的調(diào)度器源代碼會(huì)一起編譯到調(diào)度程序中。
調(diào)度框架設(shè)計(jì)目標(biāo)
增強(qiáng) kubernetes 原有調(diào)度器的可擴(kuò)展性。
通過將調(diào)度程序的某些功能移至插件,可以簡化調(diào)度程序核心。
調(diào)度框架中可設(shè)置多個(gè)擴(kuò)展點(diǎn)。
調(diào)度框架通過插件機(jī)制來接收插件結(jié)果,并根據(jù)接收到的結(jié)果繼續(xù)或中止。
提出一種處理錯(cuò)誤并將其與插件進(jìn)行通信的機(jī)制。
Proposal
調(diào)度框架在 kubernetes 調(diào)度器中定義了很多 Go 的接口和 Go API,用于用戶設(shè)計(jì)插件使用。這些用戶設(shè)計(jì)的插件將會(huì)被添加到調(diào)度程序中,并在編譯時(shí)包含在內(nèi)。可以通過配置調(diào)度程序的 ComponentConfig 將允許啟用、禁用和重新排序插件。自定義調(diào)度程序可以“ 在樹外 ” 編寫其插件并編譯包含其自己的插件的調(diào)度程序二進(jìn)制文件。
調(diào)度周期和綁定周期
調(diào)度器調(diào)度一個(gè) Pod 的過程分為兩個(gè)階段:調(diào)度周期和綁定周期。
在調(diào)度周期中,調(diào)度器會(huì)為 Pod 選擇一個(gè)最合適它運(yùn)行的節(jié)點(diǎn),然后調(diào)度過程將進(jìn)入綁定周期。
在綁定周期中,調(diào)度器會(huì)檢測(cè)調(diào)度周期中選中的那個(gè)“最合適的節(jié)點(diǎn)”是不是真的可以讓這個(gè) Pod 穩(wěn)定的運(yùn)行(比如檢測(cè) PV、檢測(cè)是否有端口沖突等),或者需不需要做一些初始化操作(比如設(shè)置這個(gè)節(jié)點(diǎn)上的 FPGA 板子的狀態(tài)、設(shè)置 GPU 顯卡的驅(qū)動(dòng)版本、CUDA 的版本等)。
擴(kuò)展點(diǎn)
kubernetes 調(diào)度框架在調(diào)度周期和綁定周期都為我們提供了豐富的擴(kuò)展點(diǎn),這些擴(kuò)展點(diǎn)可以“插上”我們自己設(shè)計(jì)的調(diào)度插件,一個(gè)插件可以在多個(gè)擴(kuò)展點(diǎn)注冊(cè)以執(zhí)行更復(fù)雜或有狀態(tài)的任務(wù),實(shí)現(xiàn)我們想要的調(diào)度功能:
下面闡述下各個(gè)擴(kuò)展點(diǎn)可以實(shí)現(xiàn)的功能。
Sort 排序
排序擴(kuò)展點(diǎn),由于調(diào)度器是按照 FIFO 的順序調(diào)度 Pod 的,因此當(dāng)隊(duì)列里出現(xiàn)多個(gè)等待調(diào)度的 Pod 時(shí),可以對(duì)這些 Pod 的先后順序進(jìn)行排序,把我們想要的 Pod(可能優(yōu)先級(jí)比較高)往出隊(duì)方向移動(dòng),讓它可以更快地被調(diào)度。
目前的 Sort 擴(kuò)展點(diǎn)只能啟用一個(gè),不可以啟用多個(gè) Sort 擴(kuò)展插件。
我們可以看下 Sort 的接口,代碼位于 kubernetes 項(xiàng)目的 /pkg/scheduler/framework/interface.go 中:
type?QueueSortPlugin?interface?{Plugin//?Less?are?used?to?sort?pods?in?the?scheduling?queue.Less(*PodInfo,?*PodInfo)?bool }也就是只需要實(shí)現(xiàn) Less 方法即可,比如如下的實(shí)現(xiàn):
func?Less(podInfo1,?podInfo2?*framework.PodInfo)?bool?{return?GetPodPriority(podInfo1)?>?GetPodPriority(podInfo2) }Pre-filter 預(yù)過濾
該擴(kuò)展點(diǎn)用于預(yù)處理有關(guān) Pod 的信息,或檢查集群或 Pod 必須滿足的某些條件。預(yù)過濾器插件應(yīng)實(shí)現(xiàn) PreFilter 函數(shù),如果 PreFilter 返回錯(cuò)誤,則調(diào)度周期將中止。注意,在每個(gè)調(diào)度周期中,只會(huì)調(diào)用一次 PreFilter。
Pre-filter 插件可以選擇實(shí)現(xiàn) PreFilterExtensions 接口,這個(gè)接口定義了 AddPod 和 RemovePod 方法以增量方式修改其預(yù)處理信息。
type?PreFilterPlugin?interface?{PluginPreFilter(ctx?context.Context,?state?*CycleState,?p?*v1.Pod)?*StatusPreFilterExtensions()?PreFilterExtensions }這里的 CycleState ,表示調(diào)度的上下文,其實(shí)是一個(gè) map 的封裝,結(jié)構(gòu)體內(nèi)部通過讀寫鎖實(shí)現(xiàn)了并發(fā)安全,開發(fā)者可以通過 CycleState 來實(shí)現(xiàn)多個(gè)調(diào)度插件直接的數(shù)據(jù)傳遞,也就是多個(gè)插件可以共享狀態(tài)或通過此機(jī)制進(jìn)行通信。
?type?CycleState?struct?{mx??????sync.RWMutexstorage?map[StateKey]StateDatarecordFrameworkMetrics?bool//?該值為?true,?則調(diào)度框架會(huì)記錄此次調(diào)度周期的數(shù)據(jù)}這里的 StateKey 是 string 類型,StateData 是一個(gè)接口類型:
?type?StateData?interface?{//?Clone?is?an?interface?to?make?a?copy?of?StateData.?For?performance?reasons,//?clone?should?make?shallow?copies?for?members?(e.g.,?slices?or?maps)?that?are?not//?impacted?by?PreFilter's?optional?AddPod/RemovePod?methods.Clone()?StateData}我們可以做一個(gè)簡單的接口實(shí)現(xiàn),來實(shí)現(xiàn) StateData:
?type?Data?struct?{Value?int64}func?(s?*Data)?Clone()?framework.StateData?{c?:=?&Data{Value:?s.Value,}return?c}那么當(dāng)插件在該擴(kuò)展點(diǎn)想傳遞數(shù)據(jù)時(shí)就可以使用如下類似的代碼實(shí)現(xiàn)數(shù)據(jù)的傳遞:
?Max?:=?Data{Value:?0}state.Lock()state.Write(framework.StateKey("Max"),?&Max)defer?state.Unlock()Filter 過濾
用于過濾不能滿足當(dāng)前被調(diào)度 Pod 運(yùn)行需求的節(jié)點(diǎn)。對(duì)于每個(gè)節(jié)點(diǎn),調(diào)度程序?qū)磁渲玫捻樞蛘{(diào)用該類插件。如果有任何過濾器插件將節(jié)點(diǎn)標(biāo)記為不可行,則不會(huì)為該節(jié)點(diǎn)調(diào)用其余插件。可以同時(shí)評(píng)估節(jié)點(diǎn),并且在同一調(diào)度周期中可以多次調(diào)用 Filter 插件。這塊其實(shí)是調(diào)度器會(huì)啟動(dòng)多個(gè) go 協(xié)程以實(shí)現(xiàn)對(duì)多個(gè)節(jié)點(diǎn)并發(fā)調(diào)用 filter,來提高過濾效率。過濾插件其實(shí)類似于上一代 Kubernetes 調(diào)度器中的預(yù)選環(huán)節(jié),即 Predicates。
我們看下接口定義:
type?FilterPlugin?interface?{PluginFilter(ctx?context.Context,?state?*CycleState,?pod?*v1.Pod,?nodeInfo?*schedulernodeinfo.NodeInfo)?*Status }我們可以對(duì)應(yīng)的實(shí)現(xiàn),比如我這里需要做 GPU 的調(diào)度,我需要檢查每個(gè)節(jié)點(diǎn)的 GPU 是否滿足 Pod 的運(yùn)行要求:
func?(y?*Yoda)?Filter(ctx?context.Context,?state?*framework.CycleState,?pod?*v1.Pod,?node?*nodeinfo.NodeInfo)?*framework.Status?{klog.V(3).Infof("filter?pod:?%v,?node:?%v",?pod.Name,?node.Node().Name)//?檢查節(jié)點(diǎn)?GPU?的健康狀態(tài)if?ok,?msg?:=?filter.CheckGPUHealth(node);?ok?{//?節(jié)點(diǎn)的?GPU?是否符合Pod?運(yùn)行等級(jí)if?!filter.PodFitsLevel(pod,?node)?{return?framework.NewStatus(framework.Unschedulable,?"Node:"+node.Node().Name+"?GPU?Level?Not?Fit")}//?節(jié)點(diǎn)的?GPU?顯存是否符合?Pod?運(yùn)行if?!filter.PodFitsMemory(pod,?node)?{return?framework.NewStatus(framework.Unschedulable,?"Node:"+node.Node().Name+"?GPU?Memory?Not?Fit")}//?節(jié)點(diǎn)的?GPU?數(shù)量是否符合?Pod?運(yùn)行if?!filter.PodFitsNumber(pod,?node)?{return?framework.NewStatus(framework.Unschedulable,?"Node:"+node.Node().Name+"?GPU?Number?Not?Fit")}return?framework.NewStatus(framework.Success,?"")}?else?{return?framework.NewStatus(framework.Unschedulable,?"Node:"+node.Node().Name+msg)} }Pre-Score 預(yù)打分 (v1alpha1 版本稱為 Post-Filter)
注意:Pre-Score 從 v1alpha2 開始可用。
該擴(kuò)展點(diǎn)將使用通過 Filter 階段的節(jié)點(diǎn)列表來調(diào)用插件。插件可以使用此數(shù)據(jù)來更新內(nèi)部狀態(tài)或生成日志、指標(biāo)。比如可以通過該擴(kuò)展點(diǎn)收集各個(gè)節(jié)點(diǎn)中性能指標(biāo),所有節(jié)點(diǎn)中最大的內(nèi)存的節(jié)點(diǎn),性能最好的 CPU 節(jié)點(diǎn)等。
我們繼續(xù)來看接口里長什么樣子(我這里是v1alpha1):
type?PostFilterPlugin?interface?{PluginPostFilter(ctx?context.Context,?state?*CycleState,?pod?*v1.Pod,?nodes?[]*v1.Node,?filteredNodesStatuses?NodeToStatusMap)?*Status }針對(duì)這個(gè)擴(kuò)展點(diǎn),通過傳遞的參數(shù)可以看出,接口傳入了節(jié)點(diǎn)的切片,因此開發(fā)者可以通過啟動(dòng)多個(gè)并發(fā)協(xié)程來獲取數(shù)據(jù),并且可以把這些數(shù)據(jù)存在 CycleState 中,給之后的插件擴(kuò)展點(diǎn)使用:
func?(y?*Yoda)?PostFilter(ctx?context.Context,?state?*framework.CycleState,?pod?*v1.Pod,?nodes?[]*v1.Node,?filteredNodesStatuses?framework.NodeToStatusMap)?*framework.Status?{klog.V(3).Infof("collect?info?for?scheduling??pod:?%v",?pod.Name)return?collection.ParallelCollection(collection.Workers,?state,?nodes,?filteredNodesStatuses) }并發(fā)這塊我們也可以參考 1.13 調(diào)度器中經(jīng)常使用的經(jīng)典并發(fā)模型:
func?ParallelCollection(workers?int,?state?*framework.CycleState,?nodes?[]*v1.Node,?filteredNodesStatuses?framework.NodeToStatusMap)?*framework.Status?{var?(stop?<-chan?struct{}mx???sync.RWMutexmsg??=?"")//?數(shù)據(jù)存入管道pieces?:=?len(Sum)toProcess?:=?make(chan?string,?pieces)for?_,?v?:=?range?Sum?{toProcess?<-?v}close(toProcess)//?并發(fā)協(xié)程數(shù)限制if?pieces?<?workers?{workers?=?pieces}wg?:=?sync.WaitGroup{}wg.Add(workers)for?i?:=?0;?i?<?workers;?i++?{go?func()?{//?協(xié)程消費(fèi)管道數(shù)據(jù)for?value?:=?range?toProcess?{select?{case?<-stop:returndefault://?state?并發(fā)安全,調(diào)用的時(shí)候可以不用加鎖if?re?:=?CollectMaxValue(value,?state,?nodes,?filteredNodesStatuses);?!re.IsSuccess()?{klog.V(3).Infof(re.Message())mx.Lock()//?message非并發(fā)安全,加鎖msg?+=?re.Message()mx.Unlock()}}}wg.Done()}()}wg.Wait()if?msg?!=?""?{return?framework.NewStatus(framework.Error,?msg)}return?framework.NewStatus(framework.Success,?"") }Score 打分
Score 擴(kuò)展點(diǎn)和上一代的調(diào)度器的優(yōu)選流程很像,它分為兩個(gè)階段:
第一階段稱為 “打分”,用于對(duì)已通過過濾階段的節(jié)點(diǎn)進(jìn)行排名。調(diào)度程序?qū)?Score 每個(gè)節(jié)點(diǎn)調(diào)用每個(gè)計(jì)分插件。
第二階段是 “歸一化”,用于在調(diào)度程序計(jì)算節(jié)點(diǎn)的最終排名之前修改分?jǐn)?shù),可以不實(shí)現(xiàn), 但是需要保證 Score 插件的輸出必須是 [MinNodeScore,MaxNodeScore]([0-100]) 范圍內(nèi)的整數(shù) 。如果不是,則調(diào)度器會(huì)報(bào)錯(cuò),你需要實(shí)現(xiàn) NormalizeScore 來保證最后的得分范圍。如果不實(shí)現(xiàn) NormalizeScore,則 Score 的輸出必須在此范圍內(nèi)。調(diào)度程序?qū)⒏鶕?jù)配置的插件權(quán)重合并所有插件的節(jié)點(diǎn)分?jǐn)?shù)。
看看接口的定義:
type?ScorePlugin?interface?{Plugin//?Score?is?called?on?each?filtered?node.?It?must?return?success?and?an?integer//?indicating?the?rank?of?the?node.?All?scoring?plugins?must?return?success?or//?the?pod?will?be?rejected.Score(ctx?context.Context,?state?*CycleState,?p?*v1.Pod,?nodeName?string)?(int64,?*Status)//?ScoreExtensions?returns?a?ScoreExtensions?interface?if?it?implements?one,?or?nil?if?does?not.ScoreExtensions()?ScoreExtensions }我們也可以做如下簡單的實(shí)現(xiàn):
func?(y?*Yoda)?Score(ctx?context.Context,?state?*framework.CycleState,?p?*v1.Pod,?nodeName?string)?(int64,?*framework.Status)?{nodeInfo,?err?:=?y.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)if?err?!=?nil?{return?0,?framework.NewStatus(framework.Error,?fmt.Sprintf("getting?node?%q?from?Snapshot:?%v",?nodeName,?err))}s,?err?:=?score.Score(state,?nodeInfo)if?err?!=?nil?{return?0,?framework.NewStatus(framework.Error,?fmt.Sprintf("Score?Node?Error:?%v",?err))}klog.V(3).Infof("node?:?%v?yoda-score:?%v",nodeName,s)return?s,?framework.NewStatus(framework.Success,?"") }如果最后的分?jǐn)?shù)不在范圍內(nèi),我們可能需要實(shí)現(xiàn) NormalizeScore 函數(shù)做進(jìn)一步處理:
func?(y?*Yoda)?NormalizeScore(ctx?context.Context,?state?*framework.CycleState,?p?*v1.Pod,?scores?framework.NodeScoreList)?*framework.Status?{var?(highest?int64?=?0)//?歸一化?for?i,?nodeScore?:=?range?scores?{scores[i].Score?=?nodeScore.Score?*?framework.MaxNodeScore?/?highest}return?framework.NewStatus(framework.Success,?"") }Reserve 保留
為給定的 Pod 保留節(jié)點(diǎn)上的資源時(shí),維護(hù)運(yùn)行時(shí)狀態(tài)的插件可以應(yīng)實(shí)現(xiàn)此擴(kuò)展點(diǎn),以由調(diào)度程序通知。這是在調(diào)度程序?qū)嶋H將 Pod 綁定到 Node 之前發(fā)生的,它的存在是為了防止在調(diào)度程序等待綁定成功時(shí)發(fā)生爭用情況。
type?ReservePlugin?interface?{Plugin//?Reserve?is?called?by?the?scheduling?framework?when?the?scheduler?cache?is//?updated.Reserve(ctx?context.Context,?state?*CycleState,?p?*v1.Pod,?nodeName?string)?*Status }這里和上面的 Score 類似,函數(shù)并沒有提供 nodeInfo 接口,我們可以通過調(diào)用 handle.SnapshotSharedLister 來獲取節(jié)點(diǎn)的信息。
nodeInfo,?err?:=?y.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)那么以上就是調(diào)度周期的插件與實(shí)現(xiàn),其實(shí)綁定周期的插件實(shí)現(xiàn)和上述的方法也都類似,實(shí)現(xiàn)相關(guān)的函數(shù)即可。
插件注冊(cè)
每個(gè)插件必須定義一個(gè)構(gòu)造函數(shù),并將其添加到硬編碼的注冊(cè)表中。
type?PluginFactory?=?func(runtime.Unknown,?FrameworkHandle)?(Plugin,?error)type?Registry?map[string]PluginFactoryfunc?NewRegistry()?Registry?{return?Registry{fooplugin.Name:?fooplugin.New,barplugin.Name:?barplugin.New,//?New?plugins?are?registered?here.} }那么在編譯的時(shí)候,編譯器會(huì)將我們的插件和調(diào)度源碼一起編譯成我們的自定義調(diào)度器。
在聲明插件的時(shí)候也需要實(shí)現(xiàn)構(gòu)造函數(shù)和對(duì)應(yīng)的方法:
type?Yoda?struct?{args???*Argshandle?framework.FrameworkHandle }func?(y?*Yoda)?Name()?string?{return?Name }func?New(configuration?*runtime.Unknown,?f?framework.FrameworkHandle)?(framework.Plugin,?error)?{args?:=?&Args{}if?err?:=?framework.DecodeInto(configuration,?args);?err?!=?nil?{return?nil,?err}klog.V(3).Infof("get?plugin?config?args:?%+v",?args)return?&Yoda{args:???args,handle:?f,},?nil }編譯小技巧
由于最終的調(diào)度器還是以容器的方式運(yùn)行的,我們可以寫一個(gè) Makefile 來簡化編譯流程:
all:?locallocal:GOOS=linux?GOARCH=amd64?go?build??-o=my-scheduler?./cmd/schedulerbuild:sudo?docker?build?--no-cache?.?-t?registry.cn-hangzhou.aliyuncs.com/my/schedulerpush:sudo?docker?push?registry.cn-hangzhou.aliyuncs.com/my/schedulerformat:sudo?gofmt?-l?-w?. clean:sudo?rm?-f?my-scheduler編寫調(diào)度器的Dockerfile:
FROM?debian:stretch-slimWORKDIR?/COPY?my-scheduler?/usr/local/binCMD?["my-scheduler"]那么編譯 -> 構(gòu)建就可以三步走了:
編譯
構(gòu)建鏡像
上傳鏡像
自定義調(diào)度器的配置
首先需要設(shè)置一個(gè) ConfigMap ,用于存放調(diào)度器的配置文件:
apiVersion:?v1 kind:?ConfigMap metadata:name:?scheduler-confignamespace:?kube-system data:scheduler-config.yaml:?|apiVersion:?kubescheduler.config.k8s.io/v1alpha1kind:?KubeSchedulerConfigurationschedulerName:?yoda-schedulerleaderElection:leaderElect:?truelockObjectName:?yoda-schedulerlockObjectNamespace:?kube-systemplugins:queueSort:enabled:-?name:?"yoda"filter:enabled:-?name:?"yoda"score:enabled:-?name:?"yoda"postFilter:enabled:-?name:?"yoda"pluginConfig:-?name:?"yoda"args:?{"master":?"master",?"kubeconfig":?"kubeconfig"}這里主要需要修改的就是 schedulerName 字段的調(diào)度器名稱和 plugins 字段中各個(gè)擴(kuò)展點(diǎn)的插件名稱,enable 才能保證該擴(kuò)展點(diǎn)運(yùn)行了你的插件。
接著為調(diào)度器創(chuàng)建 RBAC:
kind:?ClusterRole apiVersion:?rbac.authorization.k8s.io/v1 metadata:name:?yoda-cr rules:-?apiGroups:-?""resources:-?endpoints-?eventsverbs:-?create-?get-?update-?apiGroups:-?""resourceNames:-?yoda-schedulerresources:-?endpointsverbs:-?delete-?get-?patch-?update-?apiGroups:-?""resources:-?nodesverbs:-?get-?list-?watch-?apiGroups:-?""resources:-?podsverbs:-?delete-?get-?list-?watch-?update-?apiGroups:-?""resources:-?bindings-?pods/bindingverbs:-?create-?apiGroups:-?""resources:-?pods/statusverbs:-?patch-?update-?apiGroups:-?""resources:-?replicationcontrollers-?servicesverbs:-?get-?list-?watch-?apiGroups:-?apps-?extensionsresources:-?replicasetsverbs:-?get-?list-?watch-?apiGroups:-?appsresources:-?statefulsetsverbs:-?get-?list-?watch-?apiGroups:-?policyresources:-?poddisruptionbudgetsverbs:-?get-?list-?watch-?apiGroups:-?""resources:-?persistentvolumeclaims-?persistentvolumesverbs:-?get-?list-?watch-?apiGroups:-?""resources:-?configmapsverbs:-?get-?list-?watch-?apiGroups:-?"storage.k8s.io"resources:-?storageclasses-?csinodesverbs:-?watch-?list-?get-?apiGroups:-?"coordination.k8s.io"resources:-?leasesverbs:-?create-?get-?list-?update-?apiGroups:-?"events.k8s.io"resources:-?eventsverbs:-?create-?patch-?update --- apiVersion:?v1 kind:?ServiceAccount metadata:name:?yoda-sanamespace:?kube-system --- kind:?ClusterRoleBinding apiVersion:?rbac.authorization.k8s.io/v1 metadata:name:?yoda-crbnamespace:?kube-system roleRef:apiGroup:?rbac.authorization.k8s.iokind:?ClusterRolename:?yoda-cr subjects:-?kind:?ServiceAccountname:?yoda-sanamespace:?kube-system最后配置調(diào)度器的 Deployment:
apiVersion:?apps/v1 kind:?Deployment metadata:name:?yoda-schedulernamespace:?kube-systemlabels:component:?yoda-scheduler spec:replicas:?1selector:matchLabels:component:?yoda-schedulertemplate:metadata:labels:component:?yoda-schedulerspec:serviceAccount:?yoda-sapriorityClassName:?system-cluster-criticalvolumes:-?name:?scheduler-configconfigMap:name:?scheduler-configcontainers:-?name:?yoda-schedulerimage:?registry.cn-hangzhou.aliyuncs.com/geekcloud/yoda-schedulerimagePullPolicy:?Alwaysargs:-?yoda-scheduler-?--config=/scheduler/scheduler-config.yaml-?--v=3resources:requests:cpu:?"50m"volumeMounts:-?name:?scheduler-configmountPath:?/scheduler隨著云計(jì)算技術(shù)的不斷發(fā)展,kubernetes scheduler 也在根據(jù)各種復(fù)雜的需求不斷進(jìn)化,未來也會(huì)涌現(xiàn)更多各種各樣的豐富的、支持不同功能的調(diào)度器在不同的生產(chǎn)環(huán)境中發(fā)揮著更多強(qiáng)勁的作用,一起期待吧!
作者介紹
李俊江
kubernetes & istio member
南京郵電大學(xué)物聯(lián)網(wǎng)學(xué)院研究生,熱衷于 Kubernetes 與云原生相關(guān)技術(shù)。
微信:FUNKY-STARS 歡迎交流!
參考
Scheduling Framework
enhancements/624
scheduler-framework-sample
kubernetes 1.13 源碼分析
致謝
感謝 Scheduler-SIG Leader HuangWei 大佬在 kubecon 2018 的 Q&A 和指導(dǎo)!
感謝張磊、車漾大佬在 kubecon 2018 的分享和討論!
直播活動(dòng)
ServiceMesher 社區(qū)聯(lián)合 MOSN 社區(qū)推出的《云原生網(wǎng)絡(luò)代理 MOSN 多協(xié)議機(jī)解析》直播,教你如何在 MOSN 中接入新的協(xié)議,實(shí)現(xiàn)不同 RPC 協(xié)議的代理,以方便 Service Mesh 擴(kuò)展。查看詳情:https://mosn.io/zh/blog/news/mosn-channel-1/
點(diǎn)擊?閱讀原文?查看更多
總結(jié)
以上是生活随笔為你收集整理的浅谈 Kubernetes Scheduling-Framework 插件的实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用.Net Core编写命令行工具(C
- 下一篇: .NET Core开发实战(第25课:路