1. 程式人生 > 其它 >基於kubernetes排程框架的自定義排程器實現-優化篇

基於kubernetes排程框架的自定義排程器實現-優化篇

目錄

簡介

上一篇中實現了一個kubernetes排程框架的排程外掛。雖然功能已經可用,但是其核心打分階段使用了裸奔的http庫直接去請求prometheus api,沒有充分地利用kubernetes叢集的能力,稍顯笨拙。本文在上篇所實現的排程外掛基礎上,探索利用kubernetes原生能力獲取節點記憶體用量,優化排程外掛。

framework.Handle

首先檢視一下預設排程器外掛,觀察它們是如何獲取叢集資源資訊的。比如在pkg/scheduler/framework/plugins/nodevolumelimits/csi.go中,可以看到外掛例項化時,例項化了一個framework.Handle

SharedInformerFactory,然後構造出了core/v1組下一些核心資源的informer。

func NewCSI(_ runtime.Object, handle framework.Handle) (framework.Plugin, error) {
	informerFactory := handle.SharedInformerFactory()
	pvLister := informerFactory.Core().V1().PersistentVolumes().Lister()
	pvcLister := informerFactory.Core().V1().PersistentVolumeClaims().Lister()
	csiNodesLister := informerFactory.Storage().V1().CSINodes().Lister()
	scLister := informerFactory.Storage().V1().StorageClasses().Lister()

	return &CSILimits{
		csiNodeLister:        csiNodesLister,
		pvLister:             pvLister,
		pvcLister:            pvcLister,
		scLister:             scLister,
		randomVolumeIDPrefix: rand.String(32),
		translator:           csitrans.New(),
	}, nil
}

接下來在實際排程階段,可以直接使用排程外掛屬性裡的informer獲取叢集物件,比如

pvc, err := pl.pvcLister.PersistentVolumeClaims(namespace).Get(pvcName)

不幸的是,檢視k8s.io/client-go/informers/factory.go裡的SharedInformerFactory介面,沒有發現支援metrics相關的api組和資源。

kubectl top

既然informer無法使用,突然想到kubectl top命令可以獲取到由metrics-server提供的位於metrics.k8s.io/v1beta1組裡的節點資源用量資訊,那麼我們的排程外掛必然也是可以參考其實現的。因此觀察kubectl top的程式碼,可以總結其主體流程如下。

  1. kubectl 預設flag生成,使用者flag整合,flag版本匹配。

    //staging/src/k8s.io/kubectl/pkg/cmd/cmd.go
    func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
      ...
    	kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag()
    	kubeConfigFlags.AddFlags(flags)
    	matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
    	matchVersionKubeConfigFlags.AddFlags(cmds.PersistentFlags())
      ...
    }
    
  2. 用matchVersionKubeConfigFlags例項化一個factoryImpl。

    //staging/src/k8s.io/kubectl/pkg/cmd/cmd.go
    func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
      ...
    	f := cmdutil.NewFactory(matchVersionKubeConfigFlags)  
      ...
    }
    
  3. 呼叫factoryImpl的ToRESTConfig()方法拿到client-go中各種client連線叢集使用的引數物件config(k8s.io/client-go/rest.Config)。

    //staging/src/k8s.io/kubectl/pkg/cmd/top/top_node.go
    func (o *TopNodeOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
      ...
    	config, err := f.ToRESTConfig()  
      ...
    }
    
  4. 用上一步的config即可構造一個metricsClientSet,這是一個基於client-go RESTClient包裝出的用於訪問metrics.k8s.io/v1beta1組監控資料的client。

    //staging/src/k8s.io/kubectl/pkg/cmd/top/top_node.go
    import(
      ...
      metricsclientset "k8s.io/metrics/pkg/client/clientset/versioned"
      ...
    )
    
    func (o *TopNodeOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
    	...
    	o.MetricsClient, err = metricsclientset.NewForConfig(config)
    	...
    	return nil
    }
    
  5. 最後,使用上一步的metricsRestClient,即可拿到叢集節點metrics。

    //staging/src/k8s.io/kubectl/pkg/cmd/top/top_node.go
    func getNodeMetricsFromMetricsAPI(metricsClient metricsclientset.Interface, resourceName string, selector labels.Selector) (*metricsapi.NodeMetricsList, error) {
    	...
    	mc := metricsClient.MetricsV1beta1()
    	nm := mc.NodeMetricses()
    	...
    }
    

實現

綜上,我們可以在排程外掛的排程打分過程中,像執行kubectl top命令一樣使用基於client-go的metricsClientSet獲取節點的實際記憶體資訊,從而代替上一版本中從prometheus獲取節點記憶體資訊的過程。

具體實現細節:

  1. 為外掛引數結構體增加一個屬性,使其例項能夠持有一個metricsClientSet例項。

    type NodeAvailableMemoryPluginArg struct {
    	PrometheusEndpoint string `json:"prometheus_endpoint,omitempty"`
    	MaxMemory          int    `json:"max_memory,omitempty"`
    	MetricsClientSet   *metricsClientSet.Clientset
    
  2. 在外掛的New方法中,構造一個config例項(同時支援叢集外kubeconfig和叢集內serviceaccount),再用config構造出metricsClientSet例項,賦值給NodeAvailableMemoryPluginArg的例項。

  3. 打分階段根據PrometheusEndpoint引數的配置,決定從哪裡獲取節點記憶體資訊:如果PrometheusEndpoint為空則使用metrics server,否則仍然從prometheus獲取。