Kubernetes 架構(gòu)指南
了解 Kubernetes 架構(gòu)中不同組件是如何組合在一起的,這樣你就可以更好地排查問(wèn)題、維護(hù)一個(gè)健康的集群,以及優(yōu)化工作流。
使用 Kubernetes 來(lái)編排容器,這種描述說(shuō)起來(lái)簡(jiǎn)單,但理解它的實(shí)際含義以及如何實(shí)現(xiàn)它完全是另外一回事。如果你正在運(yùn)行或管理 Kubernetes 集群,那么你就會(huì)知道 Kubernetes 由一臺(tái)稱為 “控制平面control plane” 的機(jī)器和許多其他 工作節(jié)點(diǎn)worker node 機(jī)器組成。每種類型都有一個(gè)復(fù)雜但穩(wěn)定的堆棧,這使編排成為可能,熟悉每個(gè)組件有助于理解它是如何工作的。
(Nived Velayudhan, CC BY-SA 4.0)
控制平面組件
Kubernetes 安裝在一個(gè)稱為“控制平面control plane”的機(jī)器上,它會(huì)運(yùn)行 Kubernetes 守護(hù)進(jìn)程,并在啟動(dòng)容器和容器組pod時(shí)與之通信。下面介紹控制平面的各個(gè)組件。
etcd
etcd 是一種快速、分布式一致性鍵值存儲(chǔ)器,用作 Kubernetes 對(duì)象數(shù)據(jù)的持久存儲(chǔ),如容器組、副本控制器、密鑰和服務(wù)。etcd 是 Kubernetes 存儲(chǔ)集群狀態(tài)和元數(shù)據(jù)的唯一地方。唯一與 etcd 直連的組件是 Kubernetes API 服務(wù)器。其他所有組件都通過(guò) API 服務(wù)器間接的從 etcd 讀寫數(shù)據(jù)。
etcd 還實(shí)現(xiàn)了一個(gè)監(jiān)控功能,它提供了一個(gè)基于事件的接口,用于異步監(jiān)控鍵的更改。一旦你更改了一個(gè)鍵,它的監(jiān)控者就會(huì)收到通知。API 服務(wù)器組件嚴(yán)重依賴于此來(lái)獲得通知,并將 etcd 變更至期望狀態(tài)。
為什么 etcd 實(shí)例的數(shù)量應(yīng)該是奇數(shù)?
你通常會(huì)運(yùn)行三個(gè)、五個(gè)或七個(gè) etcd 實(shí)例實(shí)現(xiàn)高可用(HA)環(huán)境,但這是為什么呢?因?yàn)?etcd 是分布式數(shù)據(jù)存儲(chǔ),可以水平擴(kuò)展它,但你需要確保每個(gè)實(shí)例中的數(shù)據(jù)是一致的。因此,需要為系統(tǒng)當(dāng)前狀態(tài)達(dá)成共識(shí),etcd 為此使用 RAFT 共識(shí)算法。
RAFT 算法需要經(jīng)過(guò)選舉(或仲裁)集群才能進(jìn)入下一個(gè)狀態(tài)。如果你只有兩個(gè) etcd 實(shí)例并且他們其中一個(gè)失敗的話,那么 etcd 集群無(wú)法轉(zhuǎn)換到新的狀態(tài),因?yàn)椴淮嬖谶^(guò)半這個(gè)概念。如果你有三個(gè) etcd 實(shí)例,一個(gè)實(shí)例可能會(huì)失敗,但仍有 2 個(gè)實(shí)例可用于進(jìn)行選舉。
API 服務(wù)器
API 服務(wù)器是 Kubernetes 中唯一直接與 etcd 交互的組件。Kubernetes 中的其他所有組件都必須通過(guò) API 服務(wù)器來(lái)處理集群狀態(tài),包括客戶端(kubectl)。API 服務(wù)器具有以下功能:
- 提供在 etcd 中存儲(chǔ)對(duì)象的一致方式。
- 執(zhí)行驗(yàn)證對(duì)象,防止客戶端存儲(chǔ)配置不正確的對(duì)象(如果它們直接寫入 etcd 數(shù)據(jù)存儲(chǔ),可能會(huì)發(fā)生這種情況)。
- 提供 RESTful API 來(lái)創(chuàng)建、更新、修改或刪除資源。
- 提供樂(lè)觀并發(fā)鎖,在發(fā)生更新時(shí),其他客戶端永遠(yuǎn)不會(huì)有機(jī)會(huì)重寫對(duì)象。
- 對(duì)客戶端發(fā)送的請(qǐng)求進(jìn)行身份驗(yàn)證和授權(quán)。它使用插件提取客戶端的用戶名、ID、所屬組,并確定通過(guò)身份驗(yàn)證的用戶是否可以對(duì)請(qǐng)求的資源執(zhí)行請(qǐng)求的操作。
- 如果請(qǐng)求試圖創(chuàng)建、修改或刪除資源,則負(fù)責(zé)權(quán)限控制。例如,AlwaysPullImages、DefaultStorageClass 和 ResourceQuota。
- 實(shí)現(xiàn)了一種監(jiān)控機(jī)制(類似于 etcd),用戶客戶端監(jiān)控更改。這允許調(diào)度器和控制器管理器等組件以松耦合的方式與 API 服務(wù)器交互。
控制器管理器
在 Kubernetes 中,控制器持續(xù)監(jiān)控集群狀態(tài),然后根據(jù)需要進(jìn)行或請(qǐng)求更改。每個(gè)控制器都嘗試將當(dāng)前集群狀態(tài)變更至期望狀態(tài)??刂破髦辽俑櫼环N Kubernetes 資源類型,這些對(duì)象均有一個(gè)字段來(lái)表示期望的狀態(tài)。
控制器示例:
- 副本管理器(管理副本控制器ReplicationController資源的控制器)
- 副本集ReplicaSet、守護(hù)進(jìn)程集DaemonSet 和任務(wù)控制器
- 部署控制器
- 有狀態(tài)負(fù)載控制器
- 節(jié)點(diǎn)控制器
- 服務(wù)控制器
- 接入點(diǎn)控制器
- 命名空間控制器
- 持久卷PersistentVolume控制器
控制器通過(guò)監(jiān)控機(jī)制來(lái)獲得變更通知。它們監(jiān)視 API 服務(wù)器對(duì)資源的變更,對(duì)每次更改執(zhí)行操作,無(wú)論是新建對(duì)象還是更新或刪除現(xiàn)有對(duì)象。大多數(shù)時(shí)候,這些操作包括創(chuàng)建其他資源或更新監(jiān)控的資源本身。不過(guò),由于使用監(jiān)控并不能保證控制器不會(huì)錯(cuò)過(guò)任何事件,它們還會(huì)定期執(zhí)行一系列操作,確保沒(méi)有錯(cuò)過(guò)任何事件。
控制器管理器還執(zhí)行生命周期功能。例如命名空間創(chuàng)建和生命周期、事件垃圾收集、已終止容器組垃圾收集、級(jí)聯(lián)刪除垃圾收集? 和節(jié)點(diǎn)垃圾收集。有關(guān)更多信息,參考 云控制器管理器。
調(diào)度器
調(diào)度器是一個(gè)將容器組分配給節(jié)點(diǎn)的控制平面進(jìn)程。它會(huì)監(jiān)視新創(chuàng)建沒(méi)有分配節(jié)點(diǎn)的容器組。調(diào)度器會(huì)給每個(gè)發(fā)現(xiàn)的容器組分配運(yùn)行它的最佳節(jié)點(diǎn)。
滿足容器組調(diào)度要求的節(jié)點(diǎn)稱為可調(diào)度節(jié)點(diǎn)。如果沒(méi)有合適的節(jié)點(diǎn),那么容器組會(huì)一直處于未調(diào)度狀態(tài),直到調(diào)度器可以安置它。一旦找到可調(diào)度節(jié)點(diǎn),它就會(huì)運(yùn)行一組函數(shù)來(lái)對(duì)節(jié)點(diǎn)進(jìn)行評(píng)分,并選擇得分最高的節(jié)點(diǎn),然后它會(huì)告訴 API 服務(wù)器所選節(jié)點(diǎn)的信息。這個(gè)過(guò)程稱為綁定。
節(jié)點(diǎn)的選擇分為兩步:
- 過(guò)濾所有節(jié)點(diǎn)的列表,獲得可以調(diào)度容器組的節(jié)點(diǎn)列表(例如,PodFitsResources 過(guò)濾器檢查候選節(jié)點(diǎn)是否有足夠的可用資源來(lái)滿足容器組的特定資源請(qǐng)求)。
- 對(duì)第一步得到的節(jié)點(diǎn)列表進(jìn)行評(píng)分和排序,選擇最佳節(jié)點(diǎn)。如果得分最高的有多個(gè)節(jié)點(diǎn),循環(huán)過(guò)程可確保容器組會(huì)均勻地部署在所有節(jié)點(diǎn)上。
調(diào)度決策要考慮的因素包括:
- 容器組是否請(qǐng)求硬件/軟件資源?節(jié)點(diǎn)是否報(bào)告內(nèi)存或磁盤壓力情況?
- 節(jié)點(diǎn)是否有與容器組規(guī)范中的節(jié)點(diǎn)選擇器匹配的標(biāo)簽?
- 如果容器組請(qǐng)求綁定到特定地主機(jī)端口,該端口是否可用?
- 容器組是否容忍節(jié)點(diǎn)的污點(diǎn)?
- 容器組是否指定節(jié)點(diǎn)親和性或反親和性規(guī)則?
調(diào)度器不會(huì)指示所選節(jié)點(diǎn)運(yùn)行容器組。調(diào)度器所做的就是通過(guò) API 服務(wù)器更新容器組定義。然后 API 服務(wù)器通過(guò)監(jiān)控機(jī)制通知 kubelet 容器組已被調(diào)度,然后目標(biāo)節(jié)點(diǎn)上的 kubelet 服務(wù)看到容器組被調(diào)度到它的節(jié)點(diǎn),它創(chuàng)建并運(yùn)行容器組。
工作節(jié)點(diǎn)組件
工作節(jié)點(diǎn)運(yùn)行 kubelet 代理,這允許控制平面接納它們來(lái)處理負(fù)載。與控制平面類似,工作節(jié)點(diǎn)使用幾個(gè)不同的組件來(lái)實(shí)現(xiàn)這一點(diǎn)。 以下部分描述了工作節(jié)點(diǎn)組件。
Kubelet
Kubelet 是一個(gè)運(yùn)行在集群中每個(gè)節(jié)點(diǎn)上的代理,負(fù)責(zé)在工作節(jié)點(diǎn)上運(yùn)行的所有事情。它確保容器在吊艙中運(yùn)行。
kubelet服務(wù)的主要功能有:
- 通過(guò)在 API 服務(wù)器中創(chuàng)建節(jié)點(diǎn)資源來(lái)注冊(cè)它正在運(yùn)行的節(jié)點(diǎn)。
- 持續(xù)監(jiān)控 API 服務(wù)器上調(diào)度到節(jié)點(diǎn)的容器組。
- 使用配置的容器運(yùn)行時(shí)啟動(dòng)容器組的容器。
- 持續(xù)監(jiān)控正在運(yùn)行的容器,并將其狀態(tài)、事件和資源消耗報(bào)告給 API 服務(wù)器。
- 運(yùn)行容器存活探測(cè),在探測(cè)失敗時(shí)重啟容器,當(dāng) API 服務(wù)器中刪除容器組時(shí)終止(通知服務(wù)器容器組終止的消息)。
服務(wù)代理
服務(wù)代理(kube-proxy)在每個(gè)節(jié)點(diǎn)上運(yùn)行,確保一個(gè)容器組可以與另一個(gè)容器組通訊,一個(gè)節(jié)點(diǎn)可以與另一個(gè)節(jié)點(diǎn)對(duì)話,一個(gè)容器可以與另一個(gè)容器對(duì)話。它負(fù)責(zé)監(jiān)視 API 服務(wù)器對(duì)服務(wù)和容器組定義的更改,以保持整個(gè)網(wǎng)絡(luò)配置是最新的。當(dāng)一項(xiàng)服務(wù)得到多個(gè)容器組的支持時(shí),代理會(huì)在這些容器組之間執(zhí)行負(fù)載平衡。
kube-proxy 之所以叫代理,是因?yàn)樗畛鯇?shí)際上是一個(gè)代理服務(wù)器,用于接受連接并將它們代理到容器組。當(dāng)前的實(shí)現(xiàn)是使用 iptables 規(guī)則將數(shù)據(jù)包重定向到隨機(jī)選擇的后端容器組,而無(wú)需通過(guò)實(shí)際的代理服務(wù)器。
關(guān)于它工作原理的高級(jí)視圖:
- 當(dāng)你創(chuàng)建一個(gè)服務(wù)時(shí),會(huì)立即分配一個(gè)虛擬 IP 地址。
- API 服務(wù)器會(huì)通知在工作節(jié)點(diǎn)上運(yùn)行的 kube-proxy 代理有一個(gè)新服務(wù)。
- 每個(gè) kube-proxy 通過(guò)設(shè)置 iptables 規(guī)則使服務(wù)可尋址,確保截獲每個(gè)服務(wù) IP/端口對(duì),并將目的地址修改為支持服務(wù)的一個(gè)容器組。
- 監(jiān)控 API 服務(wù)器對(duì)服務(wù)或其端點(diǎn)對(duì)象的更改。
容器運(yùn)行時(shí)
容器運(yùn)行時(shí)有兩類:
- 較低級(jí)別的容器運(yùn)行時(shí):它們主要關(guān)注運(yùn)行中的容器并為容器設(shè)置命名空間和控制組cgroup。
- 更高級(jí)別的容器運(yùn)行時(shí)(容器引擎):它們專注于格式、解包、管理、共享鏡像以及為開發(fā)人員提供 API。
容器運(yùn)行時(shí)負(fù)責(zé):
- 如果容器鏡像本地不存在,則從鏡像倉(cāng)庫(kù)中提取。
- 將鏡像解壓到寫時(shí)復(fù)制文件系統(tǒng),所有容器層疊加創(chuàng)建一個(gè)合并的文件系統(tǒng)。
- 準(zhǔn)備一個(gè)容器掛載點(diǎn)。
- 設(shè)置容器鏡像的元數(shù)據(jù),如覆蓋命令、用戶輸入的入口命令,并設(shè)置 SECCOMP 規(guī)則,確保容器按預(yù)期運(yùn)行。
- 通知內(nèi)核將進(jìn)程、網(wǎng)絡(luò)和文件系統(tǒng)等隔離分配給容器。
- 通知內(nèi)核分配一些資源限制,如 CPU 或內(nèi)存限制。
- 將系統(tǒng)調(diào)用(syscall)傳遞給內(nèi)核啟動(dòng)容器。
- 確保 SElinux/AppArmor 設(shè)置正確。
協(xié)同
系統(tǒng)級(jí)組件協(xié)同工作,確保 Kubernetes 集群的每個(gè)部分都能實(shí)現(xiàn)其目和執(zhí)行其功能。當(dāng)你深入編輯 YAML 文件 時(shí),有時(shí)很難理解請(qǐng)求是如何在集群中通信的。現(xiàn)在你已經(jīng)了解了各個(gè)部分是如何組合在一起的,你可以更好地理解 Kubernetes 內(nèi)部發(fā)生了什么,這有助于診斷問(wèn)題、維護(hù)健康的集群并優(yōu)化你的工作流。