溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何閱讀kubernetes源代碼

發(fā)布時間:2021-12-24 15:06:59 來源:億速云 閱讀:186 作者:小新 欄目:云計算

這篇文章主要為大家展示了“如何閱讀kubernetes源代碼”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“如何閱讀kubernetes源代碼”這篇文章吧。

為什么要閱讀代碼?怎么閱讀k8s源代碼?

  • 源代碼中包含了所有信息。寫開源軟件,從文檔和其他地方拿到的是二手的信息,代碼就是最直接的一手信息。代碼就是黑客帝國中neo看到的世界本源。

  • 文本并不是代碼本身。文本只是在人類可讀的模式和編譯器可解析之間做了一個折中。代碼的本質是具有復雜拓撲的數(shù)據(jù)結構,就像樹或者電路一樣。所以讀代碼的過程是在腦中構建出這個世界,所謂腦補是也。

  • 閱讀好的代碼是一種享受。我最喜歡閱讀的是redis的代碼,用C寫的,極端簡潔但又威力強大。幾句話就把最高效、精妙的數(shù)據(jù)結構完成出來,就像一篇福爾摩斯的偵探小說。在看的時候我常常想,如果讓我實現(xiàn)這個功能,是否能像他這么簡單高效?

以閱讀k8s其中的一個模塊,scheduler為例子,來講講我是怎么讀代碼的

從用戶的角度出發(fā),scheduler模塊是干什么的?

scheduler是k8s的調度模塊,做的事情就是拿到pod之后在node中尋找合適的進行適配這么一個單純的功能。實際上,我已經(jīng)多次編譯和構建這個程序并運行起來。在我的腦中,sheduler在整個系統(tǒng)中是這樣的:

如何閱讀kubernetes源代碼

scheduler作為一個客戶端,從apiserver中讀取到需要分配的pod,和擁有的node,然后進行過濾和算分,最后把這個匹配信息通過apiserver寫入到etcd里面,供下一步的kubelet去拉起pod使用。這樣,立刻有幾個問題浮現(xiàn)出來

問1.scheduler讀取到的數(shù)據(jù)結構是怎么樣的?(輸入)
問2.scheduler寫出的的數(shù)據(jù)結構是怎么樣的?(輸出)
問3.在前面的測試中,scheduler成為了系統(tǒng)的瓶頸,為什么?
問4.社區(qū)有人說增加緩存能有效提高scheduler的效率,他的思路是可行的嗎?

讀scheduler代碼的整個經(jīng)歷

層1:cmd入口

kubernetes\plugin\cmd\kube-scheduler\scheduler.go

這段代碼比較短就全文貼出來了

package main

import (
	"runtime"

	"k8s.io/kubernetes/pkg/healthz"
	"k8s.io/kubernetes/pkg/util"
	"k8s.io/kubernetes/pkg/version/verflag"
	"k8s.io/kubernetes/plugin/cmd/kube-scheduler/app"

	"github.com/spf13/pflag"
)

func init() {
	healthz.DefaultHealthz()                //忽略……
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())    //忽略……
	s := app.NewSchedulerServer()           //關注,實際調用的初始化
	s.AddFlags(pflag.CommandLine)           //忽略,命令行解析

	util.InitFlags()                        
	util.InitLogs()
	defer util.FlushLogs()                  //忽略,開日志等

	verflag.PrintAndExitIfRequested()

	s.Run(pflag.CommandLine.Args())         //關注,實際跑的口子
}

可以看到,對于細枝末節(jié)我一概忽略掉,進入下一層,但是,我并不是不提出問題,提出的問題會寫在這里,然后從腦子里面“忘掉”,以減輕前進的負擔

kubernetes\plugin\cmd\kube-scheduler\app\server.go

如何閱讀kubernetes源代碼

進入這個文件后,重點看的就是數(shù)據(jù)結構和方法:

  • SchedulerServer這個結構存放了一堆配置信息,裸的,可以看到里面幾個成員變量都是基本類型,int, string等

  • 上一層調用的2個方法的主要目的是倒騰配置信息,從命令行參數(shù)和配置文件kubeconfig獲取信息后

  • Run方法啟動一些性能、健康的信息在http接口,然后實際調用的是下一層。

  • kubeconfig是為了kubeclient服務的。

  • 還用了一個工廠模式,按照名稱AlgorithmProvider來創(chuàng)建具體算法的調度器。

再下一層的入口在:

sched := scheduler.New(config)
sched.Run()

對于這層的問題是:
問5.幾個限流是怎么實現(xiàn)的?QPS和Brust有什么區(qū)別?
問6.算法提供者AlgorithmProvider是怎么被抽象出來的?需要完成什么事情?

答5.在翻了限流的代碼后,發(fā)現(xiàn)來自于kubernetes\Godeps\_workspace\src\github.com\juju\ratelimit,實現(xiàn)的是一個令牌桶的算法,burst指的是在n個請求內(nèi)保持qps平均值的度量。詳見這篇文章

層2: pkg外層接口

kubernetes\plugin\pkg\scheduler\scheduler.go

答2:在這里我看到了輸出的數(shù)據(jù)結構為:

	b := &api.Binding{
		ObjectMeta: api.ObjectMeta{Namespace: pod.Namespace, Name: pod.Name},
		Target: api.ObjectReference{
			Kind: "Node",
			Name: dest,
		},
	}

這個文件最重要的數(shù)據(jù)結構是:

type Config struct {
	// It is expected that changes made via modeler will be observed
	// by NodeLister and Algorithm.
	Modeler    SystemModeler
	NodeLister algorithm.NodeLister
	Algorithm  algorithm.ScheduleAlgorithm
	Binder     Binder

	// Rate at which we can create pods
	// If this field is nil, we don't have any rate limit.
	BindPodsRateLimiter util.RateLimiter

	// NextPod should be a function that blocks until the next pod
	// is available. We don't use a channel for this, because scheduling
	// a pod may take some amount of time and we don't want pods to get
	// stale while they sit in a channel.
	NextPod func() *api.Pod

	// Error is called if there is an error. It is passed the pod in
	// question, and the error
	Error func(*api.Pod, error)

	// Recorder is the EventRecorder to use
	Recorder record.EventRecorder

	// Close this to shut down the scheduler.
	StopEverything chan struct{}
}

數(shù)據(jù)結構是什么?數(shù)據(jù)結構就是舞臺上的角色,而函數(shù)方法就是這些角色之間演出的一幕幕戲。對象是有生命的,從創(chuàng)建到數(shù)據(jù)流轉,從產(chǎn)生到消亡。而作為開發(fā)者來說,首先是搞懂這些人物設定,是關公還是秦瓊,是紅臉還是黑臉?看懂了人,就看懂了戲。

這段代碼里面,結合下面的方法,我可以得出這么幾個印象:

  • Modeler是個所有node節(jié)點的模型,但具體怎么做pod互斥還不懂

  • NodeLister是用來列表節(jié)點的

  • Algorithm是用來做調度的

  • Binder是用來做實際綁定操作的

  • 其他的,Ratelimiter說了是做限流,其他的都不是很重要,略過

問7.結合觀看了modeler.go之后,發(fā)現(xiàn)這是在綁定后處理的,所謂的assuemPod,就是把綁定的pod放到一個隊列里面去,不是很理解為什么這個互斥操作是放在bind之后做?

問8.Binder是怎么去做綁定操作的?

下一層入口:

dest, err := s.config.Algorithm.Schedule(pod, s.config.NodeLister)

層3: pkg內(nèi)層實現(xiàn)

kubernetes\plugin\pkg\scheduler\generic_scheduler.go

在調到這一層的時候,我發(fā)現(xiàn)自己走過頭了,上面s.config.Algorithm.Schedule并不會直接調用generic_scheduler.go。對于一門面向對象的語言來說,最后的執(zhí)行可能是一層接口套一層接口,而接口和實現(xiàn)的分離也造成了當你閱讀到某個地方之后就無法深入下去。或者說,純粹的自頂向下的閱讀方式并不適合面向對象的代碼。所以,目前我的閱讀方法開始變成了碎片式閱讀,先把整個代碼目錄樹給看一遍,然后去最有可能解釋我心中疑問的地方去尋找答案,然后一片片把真相拼合起來。

問9.generic_scheduler.go是怎么和scehduler.go產(chǎn)生關系的?

這是代碼目錄樹:

如何閱讀kubernetes源代碼

從目錄樹中,可以看出調度算法的目錄在algrorithemalgrorithemprovider里面,而把對象組裝在一起的關鍵源代碼是在:

文件1:factory.go

答8.Binder的操作其實很簡單,就是把pod和node的兩個字段放到http請求中發(fā)送到apiserver去做綁定,這也和系統(tǒng)的整體架構是一致的

factory的最大作用,就是從命令行參數(shù)中獲取到--algorithm--policy-config-file來獲取到必要算法名稱和調度策略,來構建Config,Config其實是調度程序的核心數(shù)據(jù)結構。schduler這整個程序做的事情可以概括為:獲取配置信息——構建Config——運行Config。這個過程類似于java中的sping組裝對象,只不過在這里是通過代碼顯式進行的。從裝配工廠中,我們看到了關鍵的一行

algo := scheduler.NewGenericScheduler(predicateFuncs, priorityConfigs, extenders, f.PodLister, r)

這樣就把我上面的問9解答了

答9.scheduler.go是形式,generic_scheduler.go是內(nèi)容,通過factory組裝

也解答了問6

答6.factoryProvider僅僅是一個算法注冊的鍵值對表達地,大部分的實現(xiàn)還是放在generic_scheduler里面的

文件2:generic_scheduler.go

這就涉及到調度的核心邏輯,就2行

filteredNodes, failedPredicateMap, err := findNodesThatFit()....

priorityList, err := PrioritizeNodes()...
  • 先過濾,尋找不引起沖突的合法節(jié)點

  • 從合法節(jié)點中去打分,尋找分數(shù)最高的節(jié)點去做綁定

  • 為了避免分數(shù)最高的節(jié)點被幾次調度撞車,從分數(shù)高的隨機找一個出來

層4 調度算法的具體實現(xiàn)

這里我就不詳細敘述細節(jié)了,讀者可以按照我的路子去自己尋找答案。

以上是“如何閱讀kubernetes源代碼”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI