最近在忙交接,一些缺的內容跟部署文章(Offline、testing 之後會補上)。這邊先把比較好寫的內容放上...
使用 Kubernetes 時,大家都能感受到其容器編配能力,當有一個容器發生異常時,Kubernetes 會透過自身機制幫你把容器遷移或重新啟動,或者能利用副本機制讓容器同時存在於叢集的不同節點上,甚至提供滾動升級(Rolling Update)容器機制。這些酷炫的功能,大家肯定都知道如何去使用,因為 Kubernetes 透過一些方式,將複雜的功能進行了抽象化與封裝,因此使用者只需要了解如何操作 API 物件,就能完成需要的功能,比如:Deployment 修改參數就會進行滾動升級。然而這些『抽象化』與『封裝』的過程究竟是如何實現呢?今天文章就是要針對這個部分進行探討。
Kubernetes 是個非常容易擴展的系統。Kubernetes 提供了多種方法讓我們能夠自定義 API,或擴展功能,比如:
過去 Cloud providers 屬於 kube-controller-manager 的部分控制器,現在已從核心程式碼移出。
而今天我們重點就是要放在探討自定義資源(Custom Resource)
與自定義控制器(Custom Controller)
上。
在 Kubernetes API 中,一個端點(Endpoint)就是一個資源,這個資源是被用於儲存某個類型的 API 物件的集合。比如說 Pod 有 /api/v1/pods API 端點。
一個 API 端點的組成如下所示:
其中每個 API 都可能存在著不同版本,其意味著不同層級穩定度與支援度:
而自定義資源就是預設不存在於 Kubernetes 原生的額外 API 資源,這包含了從當前叢集擴展新的資源物件(如:原本沒有 DaemonJob,我透過一些機制新增了),以及其他系統元件本身使用的(如: KubeadmConfig)。目前 Kubernetes 提供了兩種方式來新增自定義資源:
當利用 kubectl 建立一個 Pod 時,客戶端會透過 kubectl 與 kube-apiserver 進行溝通呼叫 Pod APIs,這時 API 物件經過驗證後,被成功建立到 Kubernetes 上,最後儲存到 etcd 中,然後過不久後,就會發現這個 Pod 在叢集中的某個節點上被執行了。到這邊一定會疑惑中間的過程,是怎麼判定建立到哪個節點上的,又是怎麼在該節點建立的呢?實際上,這需要由 Kubernetes 的kube-scheduler
與kubelet
元件完成的,其流程如下圖所示。
.spec.nodeName
欄位沒有被分配節點名稱時,kube-scheduler就會透過過濾(Filter)
與排名(Rank)
演算法來計算所有節點的權重,並從中找出一個最佳的節點,接著在 Pod 的.spec.nodeName
更新被選取的名稱,然後該狀態會被儲存到 etcd 中。.spec.nodeName
欄位是這個節點時,kubelet 就會呼叫容器 Runtime 來啟動這個 Pod 所定義的相關功能,如:掛載儲存、透過 system call 寫入環境變數等等。從這點了解 kube-scheduler 與 kubelet,才是實際上負責執行 Pod 邏輯的角色之一,而這些邏輯
就是所謂的控制器(Controller)
。因此儘管 Kubernetes 原生提供了許多的 API 資源可以使用(如下圖),如果當前 Kubernetes 叢集並沒有啟動或安裝相關的控制器,以執行實際的邏輯的話,這些 API 資源就形同空殼般存在於叢集中。這邊再舉幾個例子,比如:實現 Service 功能的就是 kube-proxy 與 kube-controller-manager 的 Endpoint 控制器(或 Endpoint Slice 控制器),這兩者分別監聽 Service 設定 NAT rules 與同步綁定 Service 與 Pod 的 IP。
那麼自定義控制器又跟自定義資源有什麼關析呢?就如同上面提到範例的 API 資源一樣,自定義資源本身只提供儲存與檢索結構化內容,因此當擴展時,並不會有實際功能,而這時就需要結合自定義控制器來完成功能的邏輯事情,並持續同步更新自定義資源。一般來說自定義控制器會有一個 Control Loop 邏輯,會持續監聽自定義資源在 API server 的事件變化(Create、Update 與 Delete),一但收到變化後,取出 API 物件內容,並執行預期結果。
(圖片擷取自:Programming Kubernetes)
在 Kubernetes 生態中,幾乎所有 API 物件功能都是以這樣形式來完成,讓使用者以宣告式 API(Declarative API)方式先定義該物件預期執行的需求
,最後再由控制器想辦法執行到預期的結果
。而在過去,這種模式並沒有盛行於開發者上,是直到 v1.7 版本 CRD(CustomResourceDefinitions) 的出現(當然 Kubernetes 成功也是原因),才出現越來越多基於此概念的各種控制器出現,甚至出現了新的名詞『Operator』。
自定義控制器除了能夠讓開發人員擴展與添加新功能以外,事實上也能替換現有的功能來優化(如利用 kbue-router 取代 kube-proxy)。當然也能用於執行一些自動化管理任務。接下來我將用一系列文章說明如何實作自定義控制器,並了解一些技巧。