編寫自己的kubernetes operater

在k8s中有許多編排工具,目前比較熱門的是包管理工具Helm,如果說docker是奠定的單實例的標準化交付,那麼Helm則是集羣化多實例、多資源的標準化交付,但是helm 只能實現簡單的編排能力,一些特定場合的應用編排並不能依靠helm實現,比如當一個被依賴服務掛掉感知到之後,如何做一個特定的操作(重啓各個依賴服務,操作數據庫,發送特定的請求等等),再比如,自動初始化數據庫,自動解決服務依賴 實現一鍵化部署, 再比如節點自愈。這些功能並不能使用helm去實現。

所以helm只是實現了自動化,以及標準化。但僅僅是這些並不能滿足我們現有的業務架構。

operator則在實現自動化和標準化的同時實現了智能化,operator對於資源的管理則不僅是創建和交付。由於其可以通過watch的方式獲取相關資源的變化事件,因此可以實現高可用、可擴展、故障恢復等運維操作。因此operator對於生命週期的管理不僅包括創建,故障恢復,高可用,升級,擴容縮容,異常處理,以及最終的清理等等。

其主要的工作流程是根據當前的狀態,進行智能分析判斷,並最終進行創建、恢復、升級等操作。而位於容器中的腳本,因爲缺乏很多全局的信息,僅靠自身是無法無法達實現這些全部的功能的。而處於第三方視角的operator,則可以解決這個問題。他可以通過側面的觀察,獲取所有的資源的狀態和信息,並且跟預想/聲明的狀態進行比較。通過預置的分析流程進行判斷,從而進行相應的操作,並最終達到聲明狀態的一個目的。這樣所有的運維邏輯就從鏡像中抽取出來,集中到operator裏去。層次和邏輯也就更加清楚,容易維護,也更容易交付和傳承。

operator可以說是另外一種controller。目前的controller manager集合的主要是基礎的、通用的資源概念,比如rs/deployment,而對於特定的應用或者服務(如etcdcluster,都可以認爲是一種資源),則放權給了第三方,也就是CRD。用戶可以通過自定義的資源描述,以及自研的controller/operator進行接入。因此controller和operator的關係有點類似於標準庫和第三方庫的關係。

一般來說,對於不同的應用一般來說需要不同的operator進行處理。這時我們再來想下,以replicaset controller爲例。rs的主要功能是保持副本數。當有pod因某種原因掛掉/刪除,對於無狀態的應用來說,恢復的方式就是再增加對應的pod數量。那麼從這個角度來說,對於無狀態的應用來說,rs controller其實就是無狀態應用的operator。

 

使用kubebuilde編寫自己的operater:

主要結構:

pkg/apis - 包含定義的 API 和自定義資源(CRD)的目錄樹,這些文件允許 sdk 爲 CRD 生成代碼並註冊對應的類型,以便正確解碼自定義資源對象。該目錄下存在type文件,通過該文件下 會有一個類似於***Spec的struct 通過這個自定義字段

pkg/controller - 用於編寫所有的操作業務邏輯的地方,該目錄下的*_controller的文件 ,通過該文件下的Reconcile方法實現主要的operater邏輯

controller把輪訓與事件監聽都封裝在這一個接口裏了.你不需要關心怎麼事件監聽的.所有邏輯控制需要我們去更改的地方也不是很多,核心的就是Reconcile方法,該方法就是去不斷的 watch 資源的狀態,然後根據狀態的不同去實現各種操作邏輯

 

operater要點:

CRD級聯刪除

在很多場景 當我們刪除一個crd資源時,需要刪除整個crd所產生的所有資源例如我們在創建deployment資源的時候,剛好這個deployment的功能有可能需要訪問kubernetes的api接口 這個時候需要創建serviceaccount role rolebinding等相關資源,如果只是創建的話我們可以直接在Reconcile 編寫相關資源直接創建,但刪除的時候如果想在刪除crd的同時刪除所有該cr產生的資源 這個時候需要引入OwnerReferences,默認情況下 只有sts depolyment pod這些資源在使用operater創建的時候纔會帶有OwnerReferences,並且需要指定OwnerReferences的值爲該crd的name

帶有OwnerReferences 如圖:

如圖在編寫該mysql資源的時候 我們需要在ObjectMeta裏指定:

OwnerReferences: []metav1.OwnerReference{

*metav1.NewControllerRef(m.GetObjectMeta(), m.GroupVersionKind()),

},

訂閱刪除前的事件

 

如果在etcd中已經刪除了資源後operator才watch到該事件,此時由於資源已經不復存在,很多邏輯操作無法得到足夠的參數來執行處理

因此,因此我們需要的是在執行最終刪除之前就能夠watch到該事件,並執行一些銷燬資源的操作。好在k8s api-server已經爲我們提供了finalizer機制。

如果某個資源的finalizers不爲空,當執行刪除之前,會被operator watch到操作。此時,其meta.DeletionTimestamp不爲null,對應operator應該在該次事件的handler中刪除掉其註冊上來的finalizer對象;並執行其他業務邏輯handler

finalizer比較典型的例子: 在刪除namespace的時候 如果該namesapce下的還存在資源 這個時候執行delete操作的時候 是無法刪除該namespace的,通過查看namespace的相關配置 我們也可以看到它是有帶finalizer標籤的

處理事件風暴

k8s operator 中reconcile方法 的作用就是不斷的watch,當資源變化時 就會觸發reconcile方法,理論上有多少次的變化就會執行多少次的reconcile方法,如果沒有做好基於狀態來終結循環的邏輯,那麼就形成了死循環,產生事件風暴

例如服務依賴於mysql 在mysql沒有啓動完成之前 operater需要不斷的讀取myslq的狀態,只有當mysql 啓動完成之後 才能啓動服務。

在kubebuilder中也爲我們提供瞭解決方案:

在判斷狀態邏輯的時候 如果狀態一直沒有走到最終狀態 我們在watch到該事件的時候 可以return reconcile.Result{RequeueAfter: SyncBuildStatusInterval} 這樣就不會不會執行到下面的狀態更新操作,而是直接返回,operator會將該資源變動的event重新放入隊列,然後等到RequeueAfter參數指定的時間間隔之後重新取出來再調用reconcile處理。這樣的優點是,到達的效果一樣,但不會頻繁的寫etcd,從而保障k8s集羣不受影響。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章