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