Kubernetes APIServer 機制概述
斷斷續(xù)續(xù)研究Kubernetes代碼已經(jīng)大半年時間了,一直在看APIServer相關(guān)的代碼,因為API是一個系統(tǒng)的入口,是所有功能對外的抽象體現(xiàn),同時也是其它組件都依賴的一個組件,處于非常核心的地位,因此它被社區(qū)進行了精心的設(shè)計,了解了它,就可以順藤摸瓜去了解其他核心的功能。不過,經(jīng)過這么長時間的摸索,發(fā)現(xiàn)Kubernetes的代碼是真心復(fù)雜,明顯感覺要比看OpenStack的代碼費勁多了,感覺它的復(fù)雜性主要來自于以下幾個方面:
Kubernetes經(jīng)過這么多年的發(fā)展,功能不斷在擴展,不斷地在復(fù)雜化,為了應(yīng)對這種復(fù)雜化,代碼也不斷被重構(gòu)和抽象,逐漸往模塊化,插件化,自動化發(fā)展,典型的像apimachinery這個庫,就是api層面最高層的抽象,如果不結(jié)合它的使用,單看這個庫的代碼,幾乎是看不懂的,而且往往Kubernetes里面一個結(jié)構(gòu)體的內(nèi)容非常多,結(jié)構(gòu)體里又嵌套多層結(jié)構(gòu)體,圍繞這個結(jié)構(gòu)體又有一堆的方法,信息量巨大,此外代碼中還有大量的magic code,不了解背景的話,很難理解為什么寫這段代碼。 Kubernetes是用Golang寫的,Golang是沒有類似于C++, Java, Python那種類的概念的,也沒有繼承,多態(tài),這種面向?qū)ο蟮木幊谭绞剑某橄蠓绞剑挥幸环N,就是Interface,以及實現(xiàn)了這個Interface的結(jié)構(gòu)體,所以面對這種復(fù)雜的項目,代碼組織是非常凌亂的。 畢竟Golang沒有Python/Java這種編程語言老牌,Kubernetes項目中,用到的第三方庫比較少,很多都是自己寫的庫,典型的像APIServer中,處理REST請求的庫,雖然使用了第三方go-restful,但還是自己開發(fā)了一個NonGoRestfulMux,因為go-restful不能滿足它的一些功能要求,與之類似的,還有API對象的序列化,以及對數(shù)據(jù)庫的操作,都是自己寫的庫,這在Python里面都有成熟的強大的庫,可以屏蔽掉這些細節(jié),這些都顯著增加了它的復(fù)雜度。
面對它四五百萬行的代碼量,真心感覺羅馬不是一天建成的。單看APIServer,里面有各種各樣的機制,比如authentication, authorization, admission, storage, api group, extension, metric, log, audit, client, informer等等,本系列文章,打算介紹下Kubernetes APIServer一些主要機制的實現(xiàn)方式,包括如下幾個方面:
APIServer是怎么run起來的 APIServer是怎么跟數(shù)據(jù)庫打交道的 APIServer中定義的API的Group和Version是怎么組織的 APIServer的擴展機制是怎么實現(xiàn)的 APIServer的序列化機制
本篇文章,主要介紹下APIServer的大致脈絡(luò),即上面提到的第一個問題,APIServer是怎么run起來的。本質(zhì)上,APIServer是使用golang中net/http庫中的Server構(gòu)建起來的,所以在這之前,我們先來看看golang里面的http Server是怎么使用的。下面是一個非常簡單的例子:
package main
import (
"fmt"
"log"
"net/http"
"time"
)
type MyHandler struct {
foo string
}
func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Println(h.foo)
}
func main() {
handler := &MyHandler{
foo: "hello world",
}
s := &http.Server{
Addr: ":8080",
Handler: handler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())
}
Handler是其中一個非常重要的概念,它是最終處理HTTP請求的實體,在golang中,定義了Handler的接口:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
凡是實現(xiàn)了ServeHTTP()方法的結(jié)構(gòu)體,那么它就是一個Handler了,所以上面定義的MyHandler結(jié)構(gòu)體因為實現(xiàn)了ServeHTTP()方法,所以它是一個Handler,可以用來處理HTTP請求。然后在main()方法中,實例化一個MyHandler的對象,將其通過Handler參數(shù)傳給了http.Server,然后ListenAndServe(),將Server運行起來,這樣就完成了一個簡單的HTTP Server了。這其實就是Kubernetes APIServer的骨架了,只不過它有非常復(fù)雜的Handler。
宏觀上來看,APIServer就是一個實現(xiàn)了REST API的WebServer,最終是使用golang的net/http庫中的Server運行起來的,數(shù)據(jù)庫使用etcd,而且目前是唯一支持的后端存儲,所以簡單理解,APIServer所做的事情,就是對數(shù)據(jù)庫的增刪查改。但是,作為一個功能完備的Web Server,不能只有對數(shù)據(jù)庫的增刪查改,還需要比如:對外暴露API,必須要有認證和授權(quán),而且Kubernetes為了能夠讓管理員更進一步控制API,還實現(xiàn)了其獨有的Admission機制,此外,通過group和version(組和版本)來組織其API對象,為了保持兼容性,多個版本的對象可以共存,還有其擴展機制,即著名的CRD和Aggregation,等等這些,讓APIServer豐滿和復(fù)雜起來。APIServer啟動的過程,就是對這些機制setup的過程,其大致流程如下圖所示:

init()是在main()函數(shù)啟動之前,就進行的一些初始化操作,主要做的事情就是注冊各種API對象類型到APIServer中,這個后續(xù)會講到。
隨后就是進行命令行參數(shù)的解析,以及設(shè)置默認值,還有校驗了,APIServer使用cobra來構(gòu)建它的CLI,各種參數(shù)通過POSIX風格的參數(shù)傳給APIServer,比如下面的參數(shù)示例:
"--bind-address=0.0.0.0",
"--secure-port=6444",
"--tls-cert-file=/var/run/kubernetes/serving-kube-apiserver.crt",
"--tls-private-key-file=/var/run/kubernetes/serving-kube-apiserver.key",這些顯示指定的參數(shù),以及沒有指定,而使用默認值的參數(shù),最終都被解析,然后集成到了一個叫做
ServerRunOptions的結(jié)構(gòu)體中,而這個結(jié)構(gòu)體又包含了很多xxxOptions的結(jié)構(gòu)體,比如EtcdOptions,SecureServingOptions等,供后面使用。隨后就到了
CreateServerChain階段,這個是整個APIServer啟動過程中,最重要的也是最復(fù)雜的階段了,整個APIServer的核心功能就包含在這個里面,這里面最主要的其實干了兩件事:一個是構(gòu)建起各個API對象的Handler處理函數(shù),即針對REST的每一個資源的增刪查改方法的注冊,比如/pod,對應(yīng)的會有CREATE/DELETE/GET/LIST/UPDATE/WATCH等Handler去處理,這些處理方法其實主要是對數(shù)據(jù)庫的操作;第二個就是通過Chain的方式,或者叫Delegation的方式,實現(xiàn)了APIServer的擴展機制,如上圖所示,KubeAPIServer是主APIServer,這里面包含了Kubernetes的所有內(nèi)置的核心API對象,APIExtensions其實就是我們常說的CRD擴展,這里面包含了所有自定義的CRD,而Aggretgator則是另外一種高級擴展機制,可以擴展外部的APIServer,三者通過Aggregator–>KubeAPIServer–>APIExtensions這樣的方式順序串聯(lián)起來,當API對象在Aggregator中找不到時,會去KubeAPIServer中找,再找不到則會去APIExtensions中找,這就是所謂的delegation,通過這樣的方式,實現(xiàn)了APIServer的擴展功能。此外,還有認證,授權(quán),Admission等都在這個階段實現(xiàn)。然后是PrepareRun階段,這個階段主要是注冊一些健康檢查的API,比如Healthz, Livez, Readyz等;
最后就到了Run階段,經(jīng)過前面的步驟,已經(jīng)生成了讓Server Run起來的所有東西,其中最重要的就是Handler了,然后將其通過NonBlocking的方式run起來,即將http.Server在一個goroutine中運行起來;隨后啟動 PostStartHook,PostStartHook 是在 CreateServerChain 階段注冊的hook函數(shù),用來周期性執(zhí)行一些任務(wù),每一個Hook起在一個單獨的goroutine中;這之后就是通過channel的方式將關(guān)閉API Server的方法阻塞住,當channel收到os.Interrup或者
syscall.SIGTERMsignal時,就會將 APIServer 關(guān)閉。
以上,就是對 Kubernetes APIServer 機制的一個大概認識,了解下 APIServer 的本質(zhì),以及它啟動的一個大致流程,后續(xù)會對其中一些步驟進行深入剖析。
原文鏈接:https://hackerain.me/2020/08/09/kubernetes/kube-apiserver-overview.html
K8S 進階訓練營
點擊屏末 | 閱讀原文 | 即刻學習
