11.1 了解架構(gòu)
在研究Kubernetes如何實(shí)現(xiàn)其功能之前,先具體了解下Kubernetes集群有哪些組件胰伍。在第一章中儿礼,可以看到板祝,Kubernetes集群分為兩部分:
- Kubernetes控制平面
- (工作)節(jié)點(diǎn)
讓我們具體看下這兩個(gè)部分做了什么,以及內(nèi)部運(yùn)行的內(nèi)容晚碾。
控制平面的組件
控制平面負(fù)責(zé)控制并使得整個(gè)集群正常運(yùn)轉(zhuǎn)抓半。回顧一下格嘁,控制平面包含如下組件:
- etcd分布式持久化存儲(chǔ)
- API服務(wù)器
- 調(diào)度器
- 控制器管理器
這些組件用來存儲(chǔ)笛求、管理集群狀態(tài),但它們不是運(yùn)行應(yīng)用的容器。
工作節(jié)點(diǎn)上運(yùn)行的組件
運(yùn)行容器的任務(wù)依賴于每個(gè)工作節(jié)點(diǎn)上運(yùn)行的組件:
- Kubelet
- Kubelet服務(wù)代理(kube-proxy)
- 容器運(yùn)行時(shí)(Docker探入、rkt或者其他)
附加組件
除了控制平面(和運(yùn)行在節(jié)點(diǎn)上的組件狡孔,還要有幾個(gè)附加組件,這樣才能提供所有之前討論的功能蜂嗽。包含:
- Kubernetes DNS服務(wù)器
- 儀表板
- Ingress控制器
- Heapster(容器集群監(jiān)控)苗膝,將在第14 章討論
- 容器網(wǎng)絡(luò)接口插件(本章后面會(huì)做討論)
11.1.1 Kubernetes組件的分布式特性
之前提到的組件都是作為單獨(dú)進(jìn)程運(yùn)行的。圖11.1描述了各個(gè)組件及它們之間的依賴關(guān)系植旧。
若要啟用Kubernetes提供的所有特性辱揭,需要運(yùn)行所有的這些組件。但是有幾個(gè)組件無須其他組件病附,單獨(dú)運(yùn)行也能提供非常有用的工作问窃。接下來會(huì)詳細(xì)查看每一個(gè)組件。
[圖片上傳失敗...(image-d7df9c-1627770873428)]image
圖11.1 Kubernetes控制平面以及工作節(jié)點(diǎn)的組件
檢查控制平面組件的狀態(tài)
API服務(wù)器對(duì)外暴露了一個(gè)名為ComponentStatus的API資源完沪,用來顯示每個(gè)控制平面組件的健康狀態(tài)域庇。可以通過kubectl列出各個(gè)組件以及它們的狀態(tài):
$ kubectl get componentstatuses
組件間如何通信
Kubernetes系統(tǒng)組件間只能通過API服務(wù)器通信覆积,它們之間不會(huì)直接通信听皿。API服務(wù)器是和etcd通信的唯一組件。其他組件不會(huì)直接和etcd通信宽档,而是通過API服務(wù)器來修改集群狀態(tài)尉姨。
API服務(wù)器和其他組件的連接基本都是由組件發(fā)起的,如圖 11.1 所示吗冤。但是啊送,當(dāng)你使用kubectl獲取日志、使用kubectl attach連接到一個(gè)運(yùn)行中的容器或運(yùn)行kubectl port-forward命令時(shí)欣孤,API服務(wù)器會(huì)向Kubelet發(fā)起連接。
注意 kubectl attach命令和kubectl exec命令類似昔逗,區(qū)別是:前者會(huì)附屬到容器中運(yùn)行著的主進(jìn)程上降传,而后者是重新運(yùn)行一個(gè)進(jìn)程。
單組件運(yùn)行多實(shí)例
盡管工作節(jié)點(diǎn)上的組件都需要運(yùn)行在同一個(gè)節(jié)點(diǎn)上勾怒,控制平面的組件可以被簡單地分割在多臺(tái)服務(wù)器上婆排。為了保證高可用性,控制平面的每個(gè)組件可以有多個(gè)實(shí)例笔链。etcd和API服務(wù)器的多個(gè)實(shí)例可以同時(shí)并行工作段只,但是,調(diào)度器和控制器管理器在給定時(shí)間內(nèi)只能有一個(gè)實(shí)例起作用鉴扫,其他實(shí)例處于待命模式赞枕。
組件是如何運(yùn)行的
控制平面的組件以及kube-proxy可以直接部署在系統(tǒng)上或者作為pod來運(yùn)行(如圖 11.1 所示)。聽到這個(gè)你可能比較驚訝,不過后面我們討論Kubelet時(shí)就都說得通了炕婶。
Kubelet是唯一一直作為常規(guī)系統(tǒng)組件來運(yùn)行的組件姐赡,它把其他組件作為pod來運(yùn)行。為了將控制平面作為pod來運(yùn)行柠掂,Kubelet被部署在master上项滑。下面的代碼清單展示了通過kubeadm(在附錄B中闡述)創(chuàng)建的集群里的kube-system命名空間里的pod。
代碼清單11.1 作為pod運(yùn)行的Kubernetes組件
$ kubectl get po -o custom-columns=POD:metadata.name,NODE:spec.nodeName --sort-by spec.nodeName -n kube-system
如代碼清單所示涯贞,所有的控制平面組件在主節(jié)點(diǎn)上作為pod運(yùn)行枪狂。這里有三個(gè)工作節(jié)點(diǎn),每一個(gè)節(jié)點(diǎn)運(yùn)行kube-proxy和一個(gè)Flannel pod宋渔,用來為pod提供重疊網(wǎng)絡(luò)(后面我們會(huì)再討論Flannel)州疾。
提示 如代碼清單所示,可以通過-o custom-columns選項(xiàng)自定義展示的列以及--sort-by對(duì)資源列表進(jìn)行排序傻谁。
現(xiàn)在孝治,讓我們對(duì)每一個(gè)組件進(jìn)行研究,從控制平面的底層組件——持久化存儲(chǔ)組件開始审磁。
11.1.2 Kubernetes如何使用etcd
本書讓你創(chuàng)建的所有對(duì)象——pod谈飒、ReplicationController、服務(wù)和私密憑據(jù)等态蒂,需要以持久化方式存儲(chǔ)到某個(gè)地方杭措,這樣它們的manifest在API服務(wù)器重啟和失敗的時(shí)候才不會(huì)丟失。為此钾恢,Kubernetes使用了etcd手素。etcd是一個(gè)響應(yīng)快、分布式瘩蚪、一致的key-value存儲(chǔ)泉懦。因?yàn)樗欠植际降模士梢赃\(yùn)行多個(gè)etcd實(shí)例來獲取高可用性和更好的性能疹瘦。
唯一能直接和etcd通信的是Kubernetes的API服務(wù)器崩哩。所有其他組件通過API服務(wù)器間接地讀取、寫入數(shù)據(jù)到etcd言沐。這帶來一些好處邓嘹,其中之一就是增強(qiáng)樂觀鎖系統(tǒng)、驗(yàn)證系統(tǒng)的健壯性险胰;并且汹押,通過把實(shí)際存儲(chǔ)機(jī)制從其他組件抽離,未來替換起來也更容易起便。值得強(qiáng)調(diào)的是棚贾,etcd是Kubernetes存儲(chǔ)集群狀態(tài)和元數(shù)據(jù)的唯一的地方窖维。
關(guān)于樂觀并發(fā)控制
樂觀并發(fā)控制(有時(shí)候指樂觀鎖)是指一段數(shù)據(jù)包含一個(gè)版本數(shù)字,而不是鎖住該段數(shù)據(jù)并阻止讀寫操作鸟悴。每當(dāng)更新數(shù)據(jù)陈辱,版本數(shù)就會(huì)增加。當(dāng)更新數(shù)據(jù)時(shí)细诸,就會(huì)檢查版本值是否在客戶端讀取數(shù)據(jù)時(shí)間和提交時(shí)間之間被增加過沛贪。如果增加過,那么更新會(huì)被拒絕震贵,客戶端必須重新讀取新數(shù)據(jù)利赋,重新嘗試更新。
兩個(gè)客戶端嘗試更新同一個(gè)數(shù)據(jù)條目猩系,只有第一個(gè)會(huì)成功媚送。 所有的Kubernetes包含一個(gè) metadata.resourceVersion
字段,當(dāng)更新對(duì)象時(shí)寇甸,客戶端需要返回該值到API服務(wù)器塘偎。如果版本值與etcd中存儲(chǔ)的不匹配,API服務(wù)器會(huì)拒絕該更新拿霉。
資源如何存儲(chǔ)在etcd中
當(dāng)筆者撰寫此書時(shí)吟秩,Kubernetes既可以用etcd版本2也可以用版本3,但目前更推薦版本3绽淘,它的性能更好涵防。etcd v2 把key存儲(chǔ)在一個(gè)層級(jí)鍵空間中,這使得鍵值對(duì)類似文件系統(tǒng)的文件沪铭。etcd中每個(gè)key要么是一個(gè)目錄壮池,包含其他key,要么是一個(gè)常規(guī)key杀怠,對(duì)應(yīng)一個(gè)值椰憋。etcd v3 不支持目錄,但是由于key格式保持不變(鍵可以包含斜杠)赔退,仍然可以認(rèn)為它們可以被組織為目錄熏矿。Kubernetes存儲(chǔ)所有數(shù)據(jù)到etcd的 /registry下。下面的代碼清單顯示 /registry下存儲(chǔ)的一系列key离钝。
代碼清單11.2 etcd中存儲(chǔ)的Kubernetes的頂層條目
$ etcdctl get /registry --prefix --keys-only
$ kubectl -n kube-system exec -it etcd-minikube -- sh -c "ETCDCTL_API=3 ETCDCTL_CACERT=/var/lib/minikube/certs/etcd/ca.crt ETCDCTL_CERT=/var/lib/minikube/certs/etcd/server.crt ETCDCTL_KEY=/var/lib/minikube/certs/etcd/server.key etcdctl endpoint health"
$ kubectl -n kube-system exec -it etcd-minikube -- sh -c "ETCDCTL_API=3 ETCDCTL_CACERT=/var/lib/minikube/certs/etcd/ca.crt ETCDCTL_CERT=/var/lib/minikube/certs/etcd/server.crt ETCDCTL_KEY=/var/lib/minikube/certs/etcd/server.key etcdctl get /registry --prefix --keys-only"
你可能會(huì)發(fā)現(xiàn),這些key和之前幾章中學(xué)習(xí)到的資源類型對(duì)應(yīng)褪储。
注意 如果使用etcd v3 的API卵渴,就無法使用ls命令來查看目錄的內(nèi)容。但是鲤竹,可以通過 etcdctl get /registry--prefix=true
列出所有以給定前綴開始的key浪读。
下面的代碼清單顯示了 /registry/pods
目錄的內(nèi)容昔榴。
代碼清單11.3 /registry/pods目錄下的key
$ etcdctl ls /registry/pods
從名稱可以看出,這兩個(gè)條目對(duì)應(yīng)default和kube-system命名空間碘橘,意味著pod按命名空間存儲(chǔ)互订。下面的代碼清單顯示 /registry/pods/default
目錄下的條目。
代碼清單11.4 default命名空間中pod的etcd條目
$ etcdctl get /registry/pods/default --prefix
每個(gè)條目對(duì)應(yīng)一個(gè)單獨(dú)的pod痘拆。這些不是目錄仰禽,而是鍵值對(duì)。下面的代碼清單展示了其中一條存儲(chǔ)的內(nèi)容纺蛆。
代碼清單11.5 一個(gè)etcd條目代表一個(gè)pod
$ etcdctl get /registry/pods/default/kubia-159041347-wt6ga
你可能發(fā)現(xiàn)了吐葵,這就是一個(gè)JSON格式的pod定義。API服務(wù)器將資源的完整JSON形式存儲(chǔ)到etcd中桥氏。由于etcd的層級(jí)鍵空間温峭,可以想象成把資源以JSON文件格式存儲(chǔ)到文件系統(tǒng)中。簡單易懂字支,對(duì)吧凤藏?
警告 Kubernetes 1.7 之前的版本,密鑰憑據(jù)的JSON內(nèi)容也像上面一樣存儲(chǔ)(沒有加密)堕伪。如果有人有權(quán)限直接訪問etcd揖庄,那么可以獲取所有的密鑰憑據(jù)。從 1.7 版本開始刃跛,密鑰憑據(jù)會(huì)被加密抠艾,這樣存儲(chǔ)起來更加安全。
確保存儲(chǔ)對(duì)象的一致性和可驗(yàn)證性
還記得第1章中提到的Kubernetes所依賴的谷歌的Borg和Omega系統(tǒng)嗎桨昙?和Kubernetes類似检号,Omega使用一個(gè)集中存儲(chǔ)模塊保存集群狀態(tài)。不同之處是蛙酪,多個(gè)控制平面組件可以直接訪問存儲(chǔ)模塊齐苛。所有這些組件需要確保它們都遵循同一個(gè)樂觀鎖機(jī)制,來保證能正確處理沖突桂塞。只要有一個(gè)組件沒有完全遵循該機(jī)制就可能導(dǎo)致數(shù)據(jù)不一致凹蜂。
Kubernetes對(duì)此做了改進(jìn),要求所有控制平面組件只能通過API服務(wù)器操作存儲(chǔ)模塊阁危。使用這種方式更新集群狀態(tài)總是一致的玛痊,因?yàn)锳PI服務(wù)器實(shí)現(xiàn)了樂觀鎖機(jī)制,如果有錯(cuò)誤的話狂打,也會(huì)更少擂煞。API服務(wù)器同時(shí)確保寫入存儲(chǔ)的數(shù)據(jù)總是有效的,只有授權(quán)的客戶端才能更改數(shù)據(jù)趴乡。
確保etcd集群一致性
為保證高可用性对省,常常會(huì)運(yùn)行多個(gè)etcd實(shí)例蝗拿。多個(gè)etcd實(shí)例需要保持一致。這種分布式系統(tǒng)需要對(duì)系統(tǒng)的實(shí)際狀態(tài)達(dá)成一致蒿涎。etcd使用RAFT一致性算法來保證這一點(diǎn)哀托,確保在任何時(shí)間點(diǎn),每個(gè)節(jié)點(diǎn)的狀態(tài)要么是大部分節(jié)點(diǎn)的當(dāng)前狀態(tài)劳秋,要么是之前確認(rèn)過的狀態(tài)仓手。
連接到etcd集群不同節(jié)點(diǎn)的客戶端,得到的要么是當(dāng)前的實(shí)際狀態(tài)俗批,要么是之前的狀態(tài)(在Kubernetes中俗或,etcd的唯一客戶端是API服務(wù)器,但有可能有多個(gè)實(shí)例)岁忘。
一致性算法要求集群大部分(法定數(shù)量)節(jié)點(diǎn)參與才能進(jìn)行到下一個(gè)狀態(tài)辛慰。結(jié)果就是,如果集群分裂為兩個(gè)不互聯(lián)的節(jié)點(diǎn)組干像,兩個(gè)組的狀態(tài)不可能不一致帅腌,因?yàn)橐獜闹盃顟B(tài)變化到新狀態(tài),需要有過半的節(jié)點(diǎn)參與狀態(tài)變更麻汰。如果一個(gè)組包含了大部分節(jié)點(diǎn)速客,那么另外一組只有少量節(jié)點(diǎn)成員。第一個(gè)組就可以更改集群狀態(tài)五鲫,后者則不可以溺职。當(dāng)兩個(gè)組重新恢復(fù)連接,第二個(gè)組的節(jié)點(diǎn)會(huì)更新為第一個(gè)組的節(jié)點(diǎn)的狀態(tài)位喂。
圖11.2 在腦裂場(chǎng)景中浪耘,只有擁有大部分(法定數(shù)量)節(jié)點(diǎn)的組會(huì)接受狀態(tài)變更
為什么etcd實(shí)例數(shù)量應(yīng)該是奇數(shù)
etcd通常部署奇數(shù)個(gè)實(shí)例。你一定想知道為什么塑崖。讓我們比較有一個(gè)實(shí)例和有兩個(gè)實(shí)例的情況時(shí)七冲。有兩個(gè)實(shí)例時(shí),要求另一個(gè)實(shí)例必須在線规婆,這樣才能符合超過半數(shù)的數(shù)量要求澜躺。如果有一個(gè)宕機(jī),那么etcd集群就不能轉(zhuǎn)換到新狀態(tài)抒蚜,因?yàn)闆]有超過半數(shù)掘鄙。兩個(gè)實(shí)例的情況比一個(gè)實(shí)例的情況更糟。對(duì)比單節(jié)點(diǎn)宕機(jī)嗡髓,在有兩個(gè)實(shí)例的情況下通铲,整個(gè)集群掛掉的概率增加了 100%。
比較 3 節(jié)點(diǎn)和 4 節(jié)點(diǎn)也是同樣的情況器贩。3 節(jié)點(diǎn)情況下颅夺,一個(gè)實(shí)例宕機(jī),但超過半數(shù)(2個(gè))的節(jié)點(diǎn)仍然運(yùn)行著蛹稍。對(duì)于 4 節(jié)點(diǎn)情況吧黄,需要 3個(gè)節(jié)點(diǎn)才能超過半數(shù)(2個(gè)不夠)。對(duì)于 3 節(jié)點(diǎn)和 4 節(jié)點(diǎn)唆姐,假設(shè)只有一個(gè)實(shí)例會(huì)宕機(jī)拗慨。當(dāng)以 4 節(jié)點(diǎn)運(yùn)行時(shí),一個(gè)節(jié)點(diǎn)失敗后奉芦,剩余節(jié)點(diǎn)宕機(jī)的可能性會(huì)更大(對(duì)比 3 節(jié)點(diǎn)集群赵抢,一個(gè)節(jié)點(diǎn)宕機(jī)還剩兩個(gè)節(jié)點(diǎn)的情況)。
通常声功,對(duì)于大集群烦却,etcd集群有5個(gè)或7個(gè)節(jié)點(diǎn)就足夠了∠劝停可以允許 2 ~ 3個(gè)節(jié)點(diǎn)宕機(jī)其爵,這對(duì)于大多數(shù)場(chǎng)景來說足夠了。
11.1.3 API服務(wù)器做了什么
Kubernetes API服務(wù)器作為中心組件伸蚯,其他組件或者客戶端(如kubectl)都會(huì)去調(diào)用它摩渺。以RESTful API的形式提供了可以查詢、修改集群狀態(tài)的CRUD(Create剂邮、Read摇幻、Update、Delete)接口挥萌。它將狀態(tài)存儲(chǔ)到etcd中绰姻。
API服務(wù)器除了提供一種一致的方式將對(duì)象存儲(chǔ)到etcd,也對(duì)這些對(duì)象做校驗(yàn)瑞眼,這樣客戶端就無法存入非法的對(duì)象了(直接寫入存儲(chǔ)的話是有可能的)龙宏。除了校驗(yàn),還會(huì)處理樂觀鎖伤疙,這樣對(duì)于并發(fā)更新的情況银酗,對(duì)對(duì)象做更改就不會(huì)被其他客戶端覆蓋。
API服務(wù)器的客戶端之一就是本書一開始就介紹使用的命令行工具kubectl徒像。舉個(gè)例子黍特,當(dāng)以JSON文件創(chuàng)建一個(gè)資源,kubectl通過一個(gè)HTTP POST請(qǐng)求將文件內(nèi)容發(fā)布到API服務(wù)器锯蛀。圖 11.3 顯示了接收到請(qǐng)求后API服務(wù)器內(nèi)部發(fā)生了什么灭衷,后面會(huì)做更詳細(xì)的介紹。
image
圖11.3 API服務(wù)器的操作
通過認(rèn)證插件認(rèn)證客戶端
首先旁涤,API服務(wù)器需要認(rèn)證發(fā)送請(qǐng)求的客戶端翔曲。這是通過配置在API服務(wù)器上的一個(gè)或多個(gè)認(rèn)證插件來實(shí)現(xiàn)的迫像。API服務(wù)器會(huì)輪流調(diào)用這些插件,直到有一個(gè)能確認(rèn)是誰發(fā)送了該請(qǐng)求瞳遍。這是通過檢查HTTP請(qǐng)求實(shí)現(xiàn)的闻妓。
根據(jù)認(rèn)證方式,用戶信息可以從客戶端證書或者第8章使用的HTTP標(biāo)頭(例如Authorization)獲取掠械。插件抽取客戶端的用戶名由缆、用戶ID和歸屬組。這些數(shù)據(jù)在下一階段猾蒂,認(rèn)證的時(shí)候會(huì)用到均唉。
通過授權(quán)插件授權(quán)客戶端
除了認(rèn)證插件,API服務(wù)器還可以配置使用一個(gè)或多個(gè)授權(quán)插件肚菠。它們的作用是決定認(rèn)證的用戶是否可以對(duì)請(qǐng)求資源執(zhí)行請(qǐng)求操作舔箭。例如,當(dāng)創(chuàng)建pod時(shí)案糙,API服務(wù)器會(huì)輪詢所有的授權(quán)插件限嫌,來確認(rèn)該用戶是否可以在請(qǐng)求命名空間創(chuàng)建pod。一旦插件確認(rèn)了用戶可以執(zhí)行該操作时捌,API服務(wù)器會(huì)繼續(xù)下一步操作怒医。
通過準(zhǔn)入控制插件驗(yàn)證AND/OR修改資源請(qǐng)求
如果請(qǐng)求嘗試創(chuàng)建、修改或者刪除一個(gè)資源奢讨,請(qǐng)求需要經(jīng)過準(zhǔn)入控制插件的驗(yàn)證稚叹。同理,服務(wù)器會(huì)配置多個(gè)準(zhǔn)入控制插件拿诸。這些插件會(huì)因?yàn)楦鞣N原因修改資源扒袖,可能會(huì)初始化資源定義中漏配的字段為默認(rèn)值甚至重寫它們。插件甚至?xí)バ薷牟⒉辉谡?qǐng)求中的相關(guān)資源亩码,同時(shí)也會(huì)因?yàn)槟承┰蚓芙^一個(gè)請(qǐng)求季率。資源需要經(jīng)過所有準(zhǔn)入控制插件的驗(yàn)證。
注意 如果請(qǐng)求只是嘗試讀取數(shù)據(jù)描沟,則不會(huì)做準(zhǔn)入控制的驗(yàn)證飒泻。
準(zhǔn)入控制插件包括:
-
AlwaysPullImages
—— 重寫pod的imagePullPolicy為Always,強(qiáng)制每次部署pod時(shí)拉取鏡像吏廉。 -
ServiceAccount
—— 未明確定義服務(wù)賬戶的使用默認(rèn)賬戶泞遗。 -
NamespaceLifecycle
—— 防止在命名空間中創(chuàng)建正在被刪除的pod,或在不存在的命名空間中創(chuàng)建pod。 -
ResourceQuota
—— 保證特定命名空間中的pod只能使用該命名空間分配數(shù)量的資源,如CPU和內(nèi)存署驻。我們將會(huì)在第14章做深入了解诡挂。
更多的準(zhǔn)入控制插件可以在https://kubernetes.io/docs/admin/admission-controllers/中查看Kubernetes文檔。
驗(yàn)證資源以及持久化存儲(chǔ)
請(qǐng)求通過了所有的準(zhǔn)入控制插件后蜗帜,API服務(wù)器會(huì)驗(yàn)證存儲(chǔ)到etcd的對(duì)象来惧,然后返回一個(gè)響應(yīng)給客戶端洽蛀。
11.1.4 API服務(wù)器如何通知客戶端資源變更
除了前面討論的耙蔑,API服務(wù)器沒有做其他額外的工作结序。例如,當(dāng)你創(chuàng)建一個(gè)ReplicaSet資源時(shí)纵潦,它不會(huì)去創(chuàng)建pod,同時(shí)它不會(huì)去管理服務(wù)的端點(diǎn)垃环。那是控制器管理器的工作邀层。
API服務(wù)器甚至也沒有告訴這些控制器去做什么。它做的就是遂庄,啟動(dòng)這些控制器寥院,以及其他一些組件來監(jiān)控已部署資源的變更√文浚控制平面可以請(qǐng)求訂閱資源被創(chuàng)建秸谢、修改或刪除的通知。這使得組件可以在集群元數(shù)據(jù)變化時(shí)候執(zhí)行任何需要做的任務(wù)霹肝。
客戶端通過創(chuàng)建到API服務(wù)器的HTTP連接來監(jiān)聽變更估蹄。通過此連接,客戶端會(huì)接收到監(jiān)聽對(duì)象的一系列變更通知沫换。每當(dāng)更新對(duì)象臭蚁,服務(wù)器把新版本對(duì)象發(fā)送至所有監(jiān)聽該對(duì)象的客戶端。圖 11.4 顯示客戶端如何監(jiān)聽pod的變更讯赏,以及如何將pod的變更存儲(chǔ)到etcd垮兑,然后通知所有監(jiān)聽該pod的客戶端。
[圖片上傳失敗...(image-7c6e54-1627770873428)]image
圖11.4 更新對(duì)象時(shí)漱挎,API服務(wù)器給所有監(jiān)聽者發(fā)送更新過的對(duì)象
kubectl工具作為API服務(wù)器的客戶端之一系枪,也支持監(jiān)聽資源。例如磕谅,當(dāng)部署pod時(shí)私爷,不需要重復(fù)執(zhí)行kubectl get pods來定期查詢pod列表×梗可以使用-watch標(biāo)志当犯,每當(dāng)創(chuàng)建、修改割疾、刪除pod時(shí)就會(huì)通知你嚎卫,如下面的代碼清單所示。
代碼清單11.6 監(jiān)聽創(chuàng)建刪除pod事件
$ kubectl get pods --watch
甚至可以讓kubectl打印出整個(gè)監(jiān)聽事件的YAML文件,如下:
$ kubectl get pods -o yaml --watch
監(jiān)聽機(jī)制同樣也可以用于調(diào)度器拓诸。調(diào)度器是下一個(gè)要著重講解的控制平面組件侵佃。
11.1.5 了解調(diào)度器
前面已經(jīng)學(xué)習(xí)過,我們通常不會(huì)去指定pod應(yīng)該運(yùn)行在哪個(gè)集群節(jié)點(diǎn)上奠支,這項(xiàng)工作交給調(diào)度器馋辈。宏觀來看,調(diào)度器的操作比較簡單倍谜。就是利用API服務(wù)器的監(jiān)聽機(jī)制等待新創(chuàng)建的pod迈螟,然后給每個(gè)新的、沒有節(jié)點(diǎn)集的pod分配節(jié)點(diǎn)尔崔。
調(diào)度器不會(huì)命令選中的節(jié)點(diǎn)(或者節(jié)點(diǎn)上運(yùn)行的Kubelet)去運(yùn)行pod答毫。調(diào)度器做的就是通過API服務(wù)器更新pod的定義。然后API服務(wù)器再去通知Kubelet(同樣季春,通過之前描述的監(jiān)聽機(jī)制)該pod已經(jīng)被調(diào)度過洗搂。當(dāng)目標(biāo)節(jié)點(diǎn)上的Kubelet發(fā)現(xiàn)該pod被調(diào)度到本節(jié)點(diǎn),它就會(huì)創(chuàng)建并且運(yùn)行pod的容器载弄。
盡管宏觀上調(diào)度的過程看起來比較簡單耘拇,但實(shí)際上為pod選擇最佳節(jié)點(diǎn)的任務(wù)并不簡單。當(dāng)然宇攻,最簡單的調(diào)度方式是不關(guān)心節(jié)點(diǎn)上已經(jīng)運(yùn)行的pod惫叛,隨機(jī)選擇一個(gè)節(jié)點(diǎn)。另一方面尺碰,調(diào)度器可以利用高級(jí)技術(shù)挣棕,例如機(jī)器學(xué)習(xí),來預(yù)測(cè)接下來幾分鐘或幾小時(shí)哪種類型的pod將會(huì)被調(diào)度亲桥,然后以最大的硬件利用率洛心、無須重新調(diào)度已運(yùn)行pod的方式來調(diào)度。Kubernetes的默認(rèn)調(diào)度器實(shí)現(xiàn)方式處于最簡單和最復(fù)雜程度之間题篷。
默認(rèn)的調(diào)度算法
選擇節(jié)點(diǎn)操作可以分解為兩部分词身,如圖11.5 所示:
過濾所有節(jié)點(diǎn),找出能分配給pod的可用節(jié)點(diǎn)列表番枚。
對(duì)可用節(jié)點(diǎn)按優(yōu)先級(jí)排序法严,找出最優(yōu)節(jié)點(diǎn)。如果多個(gè)節(jié)點(diǎn)都有最高的優(yōu)先級(jí)分?jǐn)?shù)葫笼,那么則循環(huán)分配深啤,確保平均分配給pod。
[圖片上傳失敗...(image-d51c27-1627770873428)]image
圖11.5 調(diào)度器為pod找到可用節(jié)點(diǎn)路星,然后選擇最優(yōu)節(jié)點(diǎn)
查找可用節(jié)點(diǎn)
為了決定哪些節(jié)點(diǎn)對(duì)pod可用溯街,調(diào)度器會(huì)給每個(gè)節(jié)點(diǎn)下發(fā)一組配置好的預(yù)測(cè)函數(shù)。這些函數(shù)檢查:
- 節(jié)點(diǎn)是否能滿足pod對(duì)硬件資源的請(qǐng)求。第14 章會(huì)學(xué)習(xí)如何定義它們呈昔。
- 節(jié)點(diǎn)是否耗盡資源(是否報(bào)告過內(nèi)存/硬盤壓力參數(shù))挥等?
- pod是否要求被調(diào)度到指定節(jié)點(diǎn)(通過名字),是否是當(dāng)前節(jié)點(diǎn)堤尾?
- 節(jié)點(diǎn)是否有和pod規(guī)格定義里的節(jié)點(diǎn)選擇器一致的標(biāo)簽(如果定義了的話)肝劲?
- 如果pod要求綁定指定的主機(jī)端口(第13 章中討論)那么這個(gè)節(jié)點(diǎn)上的這個(gè)端口是否已經(jīng)被占用?
- 如果pod要求有特定類型的卷郭宝,該節(jié)點(diǎn)是否能為此pod加載此卷辞槐,或者說該節(jié)點(diǎn)上是否已經(jīng)有pod在使用該卷了?
- pod是否能夠容忍節(jié)點(diǎn)的污點(diǎn)粘室。污點(diǎn)以及容忍度在第16 章講解催蝗。
- pod是否定義了節(jié)點(diǎn)、pod的親緣性以及非親緣性規(guī)則育特?如果是,那么調(diào)度節(jié)點(diǎn)給該pod是否會(huì)違反規(guī)則先朦?這個(gè)也會(huì)在第16 章介紹缰冤。
所有這些測(cè)試都必須通過,節(jié)點(diǎn)才有資格調(diào)度給pod喳魏。在對(duì)每個(gè)節(jié)點(diǎn)做過這些檢查后棉浸,調(diào)度器得到節(jié)點(diǎn)集的一個(gè)子集。任何這些節(jié)點(diǎn)都可以運(yùn)行pod刺彩,因?yàn)樗鼈兌加凶銐虻目捎觅Y源迷郑,也確認(rèn)過滿足pod定義的所有要求。
為pod選擇最佳節(jié)點(diǎn)
盡管所有這些節(jié)點(diǎn)都能運(yùn)行pod创倔,其中的一些可能還是優(yōu)于另外一些嗡害。假設(shè)有一個(gè) 2 節(jié)點(diǎn)集群,兩個(gè)節(jié)點(diǎn)都可用畦攘,但是其中一個(gè)運(yùn)行 10個(gè)pod霸妹,而另一個(gè),不知道什么原因知押,當(dāng)前沒有運(yùn)行任何pod叹螟。本例中,明顯調(diào)度器應(yīng)該選第二個(gè)節(jié)點(diǎn)台盯。
或者說罢绽,如果兩個(gè)節(jié)點(diǎn)是由云平臺(tái)提供的服務(wù),那么更好的方式是静盅,pod調(diào)度給第一個(gè)節(jié)點(diǎn)良价,將第二個(gè)節(jié)點(diǎn)釋放回云服務(wù)商以節(jié)省資金。
pod高級(jí)調(diào)度
考慮另外一個(gè)例子。假設(shè)一個(gè)pod有多個(gè)副本棚壁。理想情況下杯矩,你會(huì)期望副本能夠分散在盡可能多的節(jié)點(diǎn)上,而不是全部分配到單獨(dú)一個(gè)節(jié)點(diǎn)上袖外。該節(jié)點(diǎn)的宕機(jī)會(huì)導(dǎo)致pod支持的服務(wù)不可用史隆。但是如果pod分散在不同的節(jié)點(diǎn)上,單個(gè)節(jié)點(diǎn)宕機(jī)曼验,并不會(huì)對(duì)服務(wù)造成什么影響泌射。
默認(rèn)情況下,歸屬同一服務(wù)和ReplicaSet的pod會(huì)分散在多個(gè)節(jié)點(diǎn)上鬓照。但不保證每次都是這樣熔酷。不過可以通過定義pod的親緣性、非親緣規(guī)則強(qiáng)制pod分散在集群內(nèi)或者集中在一起豺裆,相關(guān)內(nèi)容會(huì)在第16 章中介紹拒秘。
僅通過這兩個(gè)簡單的例子就說明了調(diào)度有多復(fù)雜,因?yàn)樗蕾囉诖罅康囊蜃映舨隆R虼颂删疲{(diào)度器既可以配置成滿足特定的需要或者基礎(chǔ)設(shè)施特性,也可以整體替換為一個(gè)定制的實(shí)現(xiàn)蔑歌「Γ可以拋開調(diào)度器運(yùn)行一個(gè)Kubernetes,不過那樣的話次屠,就需要手動(dòng)實(shí)現(xiàn)調(diào)度了园匹。
使用多個(gè)調(diào)度器
可以在集群中運(yùn)行多個(gè)調(diào)度器而非單個(gè)。然后劫灶,對(duì)每一個(gè)pod裸违,可以通過在pod特性中設(shè)置schedulerName屬性指定調(diào)度器來調(diào)度特定的pod。
未設(shè)置該屬性的pod由默認(rèn)調(diào)度器調(diào)度本昏,因此其schedulerName被設(shè)置為default-scheduler累颂。其他設(shè)置了該屬性的pod會(huì)被默認(rèn)調(diào)度器忽略掉,它們要么是手動(dòng)調(diào)用凛俱,要么被監(jiān)聽這類pod的調(diào)度器調(diào)用紊馏。
可以實(shí)現(xiàn)自己的調(diào)度器,部署到集群蒲犬,或者可以部署有不同配置項(xiàng)的額外Kubernetes調(diào)度器實(shí)例朱监。
11.1.6 介紹控制器管理器中運(yùn)行的控制器
如前面提到的,API服務(wù)器只做了存儲(chǔ)資源到etcd和通知客戶端有變更的工作原叮。調(diào)度器則只是給pod分配節(jié)點(diǎn)赫编,所以需要有活躍的組件確保系統(tǒng)真實(shí)狀態(tài)朝API服務(wù)器定義的期望的狀態(tài)收斂巡蘸。這個(gè)工作由控制器管理器里的控制器來實(shí)現(xiàn)。
單個(gè)控制器擂送、管理器進(jìn)程當(dāng)前組合了多個(gè)執(zhí)行不同非沖突任務(wù)的控制器悦荒。這些控制器最終會(huì)被分解到不同的進(jìn)程,如果需要的話嘹吨,我們能夠用自定義實(shí)現(xiàn)替換它們每一個(gè)搬味。控制器包括:
- Replication管理器(ReplicationController資源的管理器)
- ReplicaSet蟀拷、DaemonSet以及Job控制器
- Deployment 控制器
- StatefulSet 控制器
- Node 控制器
- Service控制器
- Endpoints控制器
- Namespace 控制器
- PersistentVolume 控制器
- 其他
每個(gè)控制器做什么通過名字顯而易見碰纬。通過上述列表,幾乎可以知道創(chuàng)建每個(gè)資源對(duì)應(yīng)的控制器是什么问芬。資源描述了集群中應(yīng)該運(yùn)行什么悦析,而控制器就是活躍的Kubernetes組件,去做具體工作部署資源此衅。
了解控制器做了什么以及如何做的
控制器做了許多不同的事情强戴,但是它們都通過API服務(wù)器監(jiān)聽資源(部署、服務(wù)等)變更挡鞍,并且不論是創(chuàng)建新對(duì)象還是更新酌泰、刪除已有對(duì)象,都對(duì)變更執(zhí)行相應(yīng)操作匕累。大多數(shù)情況下,這些操作涵蓋了新建其他資源或者更新監(jiān)聽的資源本身(例如默伍,更新對(duì)象的status)欢嘿。
總的來說,控制器執(zhí)行一個(gè)“調(diào)和”循環(huán)也糊,將實(shí)際狀態(tài)調(diào)整為期望狀態(tài)(在資源spec部分定義)炼蹦,然后將新的實(shí)際狀態(tài)寫入資源的status部分±晏辏控制器利用監(jiān)聽機(jī)制來訂閱變更掐隐,但是由于使用監(jiān)聽機(jī)制并不保證控制器不會(huì)漏掉時(shí)間,所以仍然需要定期執(zhí)行重列舉操作來確保不會(huì)丟掉什么钞馁。
控制器之間不會(huì)直接通信虑省,它們甚至不知道其他控制器的存在。每個(gè)控制器都連接到API服務(wù)器僧凰,通過 11.1.3 節(jié)描述的監(jiān)聽機(jī)制探颈,請(qǐng)求訂閱該控制器負(fù)責(zé)的一系列資源的變更。
我們概括地了解了每個(gè)控制器做了什么训措,但是如果你想深入了解它們做了什么伪节,建議直接看源代碼光羞。邊欄闡述了如何上手看源代碼。
瀏覽控制器源代碼的幾個(gè)要點(diǎn)
如果你對(duì)控制器如何運(yùn)作感興趣怀大,強(qiáng)烈推薦看一遍源代碼纱兑。為了更容易上手,下面有幾個(gè)小建議:
控制器的源代碼可以從https://github.com/kubernetes/kubernetes/blob/master/pkg/controller獲取化借。
每個(gè)控制器一般有一個(gè)構(gòu)造器潜慎,內(nèi)部會(huì)創(chuàng)建一個(gè)Informer,其實(shí)是個(gè)監(jiān)聽器屏鳍,每次API對(duì)象有更新就會(huì)被調(diào)用勘纯。通常,Informer會(huì)監(jiān)聽特定類型的資源變更事件钓瞭。查看構(gòu)造器可以了解控制器監(jiān)聽的是哪個(gè)資源驳遵。
接下來,去看worker()方法山涡。其中定義了每次控制器需要工作的時(shí)候都會(huì)調(diào)用worker()方法堤结。實(shí)際的函數(shù)通常保存在一個(gè)叫syncHandler或類似的字段里。該字段也在構(gòu)造器里初始化鸭丛,可以在那里找到被調(diào)用函數(shù)名竞穷。該函數(shù)是所有魔法發(fā)生的地方。
Replication管理器
啟動(dòng)ReplicationController資源的控制器叫作 Replication管理器鳞溉。第4 章我們介紹過ReplicationController是如何工作的瘾带,其實(shí)不是ReplicationController做了實(shí)際的工作,而是Replication管理器熟菲。讓我們快速回顧下該控制器做了什么看政,這有助于你理解其他控制器。
在第4 章中抄罕,我們說過允蚣,ReplicationController的操作可以理解為一個(gè)無限循環(huán),每次循環(huán)呆贿,控制器都會(huì)查找符合其pod選擇器定義的pod 的數(shù)量嚷兔,并且將該數(shù)值和期望的復(fù)制集(replica)數(shù)量做比較。
既然你知道了API服務(wù)器可以通過監(jiān)聽機(jī)制通知客戶端做入,那么明顯地冒晰,控制器不會(huì)每次循環(huán)去輪詢pod,而是通過監(jiān)聽機(jī)制訂閱可能影響期望的復(fù)制集(replica)數(shù)量或者符合條件pod數(shù)量的變更事件(見圖11.6)竟块。任何該類型的變化翩剪,將觸發(fā)控制器重新檢查期望的以及實(shí)際的復(fù)制集數(shù)量,然后做出相應(yīng)操作彩郊。
你已經(jīng)知道前弯,當(dāng)運(yùn)行的pod實(shí)例太少時(shí)蚪缀,ReplicationController會(huì)運(yùn)行額外的實(shí)例,但它自己實(shí)際上不會(huì)去運(yùn)行pod恕出。它會(huì)創(chuàng)建新的pod清單询枚,發(fā)布到API服務(wù)器,讓調(diào)度器以及Kubelet來做調(diào)度工作并運(yùn)行pod浙巫。
[圖片上傳失敗...(image-fb67f2-1627770873428)]image
圖11.6 Replication管理器監(jiān)聽API對(duì)象變更
Replication管理器通過API服務(wù)器操縱pod API對(duì)象來完成其工作金蜀。所有控制器就是這樣運(yùn)作的。
RerlicaSet的畴、DaemonSet以及Job控制器
ReplicaSet控制器基本上做了和前面描述的Replication管理器一樣的事情渊抄,所以這里不再贅述。DaemonSet以及Job控制器比較相似丧裁,從它們各自資源集中定義的pod模板創(chuàng)建pod資源护桦。與Replication管理器類似,這些控制器不會(huì)運(yùn)行pod煎娇,而是將pod定義到發(fā)布API服務(wù)器二庵,讓Kubelet創(chuàng)建容器并運(yùn)行。
Deployment控制器
Deployment控制器負(fù)責(zé)使deployment的實(shí)際狀態(tài)與對(duì)應(yīng)Deployment API對(duì)象的期望狀態(tài)同步缓呛。
每次Deployment對(duì)象修改后(如果修改會(huì)影響到部署的pod),Deployment控制器都會(huì)滾動(dòng)升級(jí)到新的版本催享。通過創(chuàng)建一個(gè)ReplicaSet,然后按照Deployment中定義的策略同時(shí)伸縮新哟绊、舊RelicaSet因妙,直到舊pod被新的代替。并不會(huì)直接創(chuàng)建任何pod票髓。
StatefulSet控制器
StatefulSet控制器攀涵,類似于ReplicaSet控制器以及其他相關(guān)控制器,根據(jù)StatefulSet資源定義創(chuàng)建炬称、管理、刪除pod涡拘。其他的控制器只管理pod玲躯,而StatefulSet控制器會(huì)初始化并管理每個(gè)pod實(shí)例的持久卷聲明字段。
Node控制器
Node控制器管理Node資源鳄乏,描述了集群工作節(jié)點(diǎn)跷车。其中,Node控制器使節(jié)點(diǎn)對(duì)象列表與集群中實(shí)際運(yùn)行的機(jī)器列表保持同步橱野。同時(shí)監(jiān)控每個(gè)節(jié)點(diǎn)的健康狀態(tài)朽缴,刪除不可達(dá)節(jié)點(diǎn)的pod。
Node控制器不是唯一對(duì)Node對(duì)象做更改的組件水援。Kubelet也可以做更改密强,那么顯然可以由用戶通過REST API調(diào)用做更改茅郎。
Service控制器
在第5章,當(dāng)我們討論服務(wù)時(shí)或渤,你已經(jīng)了解了存在不同服務(wù)類型系冗。其中一個(gè)是LoadBalancer服務(wù),從基礎(chǔ)設(shè)施服務(wù)請(qǐng)求一個(gè)負(fù)載均衡器使得服務(wù)外部可以用薪鹦。Service控制器就是用來在 LoadBalancer類型服務(wù)被創(chuàng)建或刪除時(shí)掌敬,從基礎(chǔ)設(shè)施服務(wù)請(qǐng)求、釋放負(fù)載均衡器的池磁。
Endpoint控制器
你會(huì)想起來奔害,Service不會(huì)直接連接到pod,而是包含一個(gè)端點(diǎn)列表(IP和端口)地熄,列表要么是手動(dòng)华临,要么是根據(jù)Service定義的pod選擇器自動(dòng)創(chuàng)建、更新离斩。Endpoint控制器作為活動(dòng)的組件银舱,定期根據(jù)匹配標(biāo)簽選擇器的pod的IP、端口更新端點(diǎn)列表跛梗。
如圖11.7所示寻馏,控制器同時(shí)監(jiān)聽了Service和pod。當(dāng)Service被添加核偿、修改诚欠,或者pod被添加、修改或刪除時(shí)漾岳,控制器會(huì)選中匹配Service的pod選擇器的pod轰绵,將其IP和端口添加到Endpoint資源中。請(qǐng)記住尼荆,Endpoint對(duì)象是個(gè)獨(dú)立的對(duì)象左腔,所以當(dāng)需要的時(shí)候控制器會(huì)創(chuàng)建它。同樣地捅儒,當(dāng)刪除Service時(shí)液样,Endpoint對(duì)象也會(huì)被刪除。
[圖片上傳失敗...(image-57db6a-1627770873428)]image
圖11.7 Endpoint控制器監(jiān)聽Service和pod資源并管理Endpoint
Namespace控制器
想起命名空間了嗎(第3章里討論過)巧还?大部分資源歸屬于某個(gè)特定命名空間鞭莽。當(dāng)刪除一個(gè)Namespace資源時(shí),該命名空間里的所有資源都會(huì)被刪除麸祷。這就是Namespace控制器做的事情澎怒。當(dāng)收到刪除Namespace對(duì)象的通知時(shí),控制器通過API服務(wù)器刪除所有歸屬該命名空間的資源阶牍。
PersistentVolume控制器
第6章學(xué)習(xí)過持久卷以及持久卷聲明喷面。一旦用戶創(chuàng)建了一個(gè)持久卷聲明星瘾,Kubernetes必須找到一個(gè)合適的持久卷同時(shí)將其和聲明綁定。這些由持久卷控制器實(shí)現(xiàn)乖酬。
對(duì)于一個(gè)持久卷聲明死相,控制器為聲明查找最佳匹配項(xiàng),通過選擇匹配聲明中的訪問模式咬像,并且聲明的容量大于需求的容量的最小持久卷算撮。實(shí)現(xiàn)方式是保存一份有序的持久卷列表,對(duì)于每種訪問模式按照容量升序排列县昂,返回列表的第一個(gè)卷肮柜。
當(dāng)用戶刪除持久卷聲明時(shí),會(huì)解綁卷倒彰,然后根據(jù)卷的回收策略進(jìn)行回收(原樣保留审洞、刪除或清空)。
喚醒控制器
現(xiàn)在待讳,總體來說你應(yīng)該對(duì)每個(gè)控制器做了什么芒澜,以及是如何工作的有個(gè)比較好的感覺了。再一次強(qiáng)調(diào)创淡,所有這些控制器是通過API服務(wù)器來操作API對(duì)象的痴晦。它們不會(huì)直接和Kubelet通信或者發(fā)送任何類型的指令。實(shí)際上琳彩,它們不知道Kubelet的存在誊酌。控制器更新API服務(wù)器的一個(gè)資源后露乏,Kubelet和Kubernetes Service Proxy(也不知道控制器的存在)會(huì)做它們的工作碧浊,例如啟動(dòng)pod容器、加載網(wǎng)絡(luò)存儲(chǔ)瘟仿,或者就服務(wù)而言箱锐,創(chuàng)建跨pod的負(fù)載均衡。
控制平面處理了整個(gè)系統(tǒng)的一部分操作劳较,為了完全理解Kubernetes集群的內(nèi)部運(yùn)作方式驹止,還需要理解Kubelet和Kubernetes Service Proxy做了什么。下面將學(xué)習(xí)這些內(nèi)容兴想。
11.1.7 Kubelet做了什么
所有Kubernetes控制平面的控制器都運(yùn)行在主節(jié)點(diǎn)上幢哨,而Kubelet以及Service Proxy都運(yùn)行在工作節(jié)點(diǎn)(實(shí)際pod容器運(yùn)行的地方)上赡勘。Kubelet究竟做了什么事情?
了解Kubelet的工作內(nèi)容
簡單地說闸与,Kubelet就是負(fù)責(zé)所有運(yùn)行在工作節(jié)點(diǎn)上內(nèi)容的組件。它第一個(gè)任務(wù)就是在API服務(wù)器中創(chuàng)建一個(gè)Node資源來注冊(cè)該節(jié)點(diǎn)厂画。然后需要持續(xù)監(jiān)控API服務(wù)器是否把該節(jié)點(diǎn)分配給pod凸丸,然后啟動(dòng)pod容器袱院。具體實(shí)現(xiàn)方式是告知配置好的容器運(yùn)行時(shí)(Docker、CoreOS的Rkt忽洛,或者其他一些東西)來從特定容器鏡像運(yùn)行容器腻惠。Kubelet隨后持續(xù)監(jiān)控運(yùn)行的容器,向API服務(wù)器報(bào)告它們的狀態(tài)集灌、事件和資源消耗复哆。
Kubelet也是運(yùn)行容器存活探針的組件梯找,當(dāng)探針報(bào)錯(cuò)時(shí)它會(huì)重啟容器。最后一點(diǎn)初肉,當(dāng)pod從API服務(wù)器刪除時(shí)牙咏,Kubelet終止容器,并通知服務(wù)器pod已經(jīng)被終止了摔握。
拋開API服務(wù)器運(yùn)行靜態(tài)pod
盡管Kubelet一般會(huì)和API服務(wù)器通信并從中獲取pod清單氨淌,它也可以基于本地指定目錄下的pod清單來運(yùn)行pod伊磺,如圖 11.8 所示屑埋。如本章開頭所示,該特性用于將容器化版本的控制平面組件以pod形式運(yùn)行续崖。
不但可以按照原有的方式運(yùn)行Kubernetes系統(tǒng)組件,也可以將pod清單放到Kubelet的清單目錄中多艇,讓Kubelet運(yùn)行和管理它們像吻。
image
圖11.8 Kubelet基于API服務(wù)器/本地文件目錄中的pod定義運(yùn)行pod
也可以同樣的方式運(yùn)行自定義的系統(tǒng)容器,不過推薦用DaemonSet來做這項(xiàng)工作奸披。
11.1.8 Kubernetes Service Proxy的作用
除了Kubelet阵面,每個(gè)工作節(jié)點(diǎn)還會(huì)運(yùn)行kube-proxy洪鸭,用于確崩谰簦客戶端可以通過Kubernetes API連接到你定義的服務(wù)。kube-proxy確保對(duì)服務(wù)IP和端口的連接最終能到達(dá)支持服務(wù)(或者其他箕母,非pod服務(wù)終端)的某個(gè)pod處俱济。如果有多個(gè)pod支撐一個(gè)服務(wù)蛛碌,那么代理會(huì)發(fā)揮對(duì)pod的負(fù)載均衡作用。
為什么被叫作代理
kube-proxy最初實(shí)現(xiàn)為userspace代理希太。利用實(shí)際的服務(wù)器集成接收連接誊辉,同時(shí)代理給pod亡脑。為了攔截發(fā)往服務(wù)IP的連接,代理配置了iptables規(guī)則(iptables是一個(gè)管理Linux內(nèi)核數(shù)據(jù)包過濾功能的工具),重定向連接到代理服務(wù)器躯护。userspace代理模式大致如圖11.9所示棺滞。
[圖片上傳失敗...(image-dd5697-1627770873428)]image
圖11.9 userspace代理模式
kube-proxy之所以叫這個(gè)名字是因?yàn)樗_實(shí)就是一個(gè)代理器继准,不過當(dāng)前性能更好的實(shí)現(xiàn)方式僅僅通過iptables規(guī)則重定向數(shù)據(jù)包到一個(gè)隨機(jī)選擇的后端pod,而不會(huì)傳遞到一個(gè)實(shí)際的代理服務(wù)器室谚。這個(gè)模式稱為iptables代理模式秒赤,如圖11.10 所示憎瘸。
[圖片上傳失敗...(image-5db389-1627770873428)]image
圖11.10 iptables代理模式
兩種模式的主要區(qū)別是:數(shù)據(jù)包是否會(huì)傳遞給kube-proxy幌甘,是否必須在用戶空間處理锅风,或者數(shù)據(jù)包只會(huì)在內(nèi)核處理(內(nèi)核空間)皱埠。這對(duì)性能有巨大的影響漱逸。
另外一個(gè)小的區(qū)別是:userspace代理模式以輪詢模式對(duì)連接做負(fù)載均衡,而iptables代理模式不會(huì)肮砾,它隨機(jī)選擇pod仗处。當(dāng)只有少數(shù)客戶端使用一個(gè)服務(wù)時(shí),可能不會(huì)平均分布在pod中吃环。例如郁轻,如果一個(gè)服務(wù)有兩個(gè)pod支持文留,但有 5個(gè)左右的客戶端燥翅,如果你看到 4個(gè)連接到pod A,而只有一個(gè)連接到pod B靶端,不必驚訝躲查。對(duì)于客戶端數(shù)量更多的pod译柏,這個(gè)問題就不會(huì)特別明顯鄙麦。
在11.5節(jié)你會(huì)學(xué)習(xí)iptables代理模式具體是如何工作的。
11.1.9 介紹Kubernetes插件
我們已經(jīng)討論了Kubernetes集群正常工作所需要的一些核心組件介衔。但在開頭的幾章中炎咖,我們也羅列了一些插件寒波,它們不是必需的俄烁;這些插件用于啟用Kubernetes服務(wù)的DNS查詢页屠,通過單個(gè)外部IP地址暴露多個(gè)HTTP服務(wù)蓖柔、Kubernetes web儀表板等特性况鸣。
如何部署插件通過提交YAML清單文件到API服務(wù)器(本書的通用做法)竹观,這些組件會(huì)成為插件并作為pod部署栈幸。有些組件是通過Deployment資源或者ReplicationController資源部署的速址,有些是通過DaemonSet由驹。
例如蔓榄,寫作本書時(shí),在Minikube中逃魄,Ingress控制器和儀表板插件按照ReplicationController部署伍俘,如下面的代碼清單所示勉躺。
代碼清單11.7 插件在Minikube中 作為ReplicationController部署
$ kubectl get rc -n kube-system
DNS插件作為Deployment部署饵溅,如下面的代碼清單所示蜕企。
代碼清單11.8 kube-dns Deployment
$ kubectl get deploy -n kube-system
讓我們看看DNS和Ingress控制器是如何工作的。
DNS服務(wù)器如何工作
集群中的所有pod默認(rèn)配置使用集群內(nèi)部DNS服務(wù)器萍丐。這使得pod能夠輕松地通過名稱查詢到服務(wù)逝变,甚至是無頭服務(wù)pod的IP地址壳影。
DNS服務(wù)pod通過kube-dns服務(wù)對(duì)外暴露,使得該pod能夠像其他pod一樣在集群中移動(dòng)根灯。服務(wù)的IP地址在集群每個(gè)容器的 /etc/reslv.conf
文件的nameserver中定義烙肺。kube-dns pod利用API服務(wù)器的監(jiān)控機(jī)制來訂閱Service和Endpoint的變動(dòng)氧卧,以及DNS記錄的變更,使得其客戶端(相對(duì)地)總是能夠獲取到最新的DNS信息告喊。客觀地說虚循,在Service和Endpoint資源發(fā)生變化到DNS pod收到訂閱通知時(shí)間點(diǎn)之間样傍,DNS記錄可能會(huì)無效铭乾。
Ingress控制器如何工作
和DNS插件相比炕檩,Ingress控制器的實(shí)現(xiàn)有點(diǎn)不同,但它們大部分的工作方式相同泉沾。Ingress控制器運(yùn)行一個(gè)反向代理服務(wù)器(例如跷究,類似Nginx)敲霍,根據(jù)集群中定義的Ingress、Service以及Endpoint資源來配置該控制器解寝。所以需要訂閱這些資源(通過監(jiān)聽機(jī)制)聋伦,然后每次其中一個(gè)發(fā)生變化則更新代理服務(wù)器的配置觉增。
盡管Ingress資源的定義指向一個(gè)Service,Ingress控制器會(huì)直接將流量轉(zhuǎn)到服務(wù)的pod而不經(jīng)過服務(wù)IP翻斟。當(dāng)外部客戶端通過Ingress控制器連接時(shí)杨赤,會(huì)對(duì)客戶端IP進(jìn)行保存疾牲,這使得在某些用例中衙解,控制器比Service更受歡迎蚓峦。
使用其他插件
你已經(jīng)了解了DNS服務(wù)器和Ingress控制器插件同控制器管理器中運(yùn)行的控制器比較相似暑椰,除了它們不會(huì)僅通過API服務(wù)器監(jiān)聽一汽、修改資源,也會(huì)接收客戶端的連接岩喷。
其他插件也類似纱意。它們都需要監(jiān)聽集群狀態(tài)偷霉,當(dāng)有變更時(shí)執(zhí)行相應(yīng)動(dòng)作。我們會(huì)在剩余的章節(jié)中介紹一些其他的插件跑筝。
11.1.10 總結(jié)概覽
你已經(jīng)了解了整個(gè)Kubernetes系統(tǒng)由相對(duì)小的曲梗、完善功能劃分的松耦合組件構(gòu)成虏两。API服務(wù)器世剖、調(diào)度器旁瘫、控制器管理器中運(yùn)行的控制器酬凳、Kubelet以及kube-proxy一起合作來保證實(shí)際的狀態(tài)和你定義的期望狀態(tài)一致宁仔。
例如,向API服務(wù)器提交一個(gè)pod配置會(huì)觸發(fā)Kubernetes組件間的協(xié)作权埠,這會(huì)導(dǎo)致pod的容器運(yùn)行攘蔽。這里的細(xì)節(jié)將會(huì)在接下來的部分詳細(xì)說明满俗。