從零開始入門 K8s | Kubernetes API 程式設計利器:Operator 和 Operator Framework
作者 | 夙興 阿里巴巴高階工程師
本文整理自《CNCF x Alibaba 雲原生技術公開課》第 24 講,點選 “閱讀原文” 直達課程頁面。
關注 “阿里巴巴雲原生” 公眾號,回覆關鍵詞“入門”,即可下載從零入門 K8s 系列文章 PPT。
導讀:本文將從實踐出發,結合案例來說明,如何藉助 Operator 開發框架來擴充套件 Kubernetes API。內容主要分為三個部分:首先會簡單介紹一下 Operator 相關的知識;然後會介紹 Operator 開發框架並結合案例來詳細說明整個開發過程;最後會結合案例的工作流程來重新說明 Operator 是如何工作的。
一、operator 概述
基本概念
首先介紹一下本文內容所涉及到的基本概念。
CRD (Custom Resource Definition): 允許使用者自定義 Kubernetes 資源,是一個型別;
CR (Custom Resourse): CRD 的一個具體例項;
webhook: 它本質上是一種 HTTP 回撥,會註冊到 apiserver 上。在 apiserver 特定事件發生時,會查詢已註冊的 webhook,並把相應的訊息轉發過去。
按照處理型別的不同,一般可以將其分為兩類:一類可能會修改傳入物件,稱為 mutating webhook;一類則會只讀傳入物件,稱為 validating webhook。
工作佇列: controller 的核心元件。它會監控叢集內的資源變化,並把相關的物件,包括它的動作與 key,例如 Pod 的一個 Create 動作,作為一個事件儲存於該佇列中;
*controller *:它會迴圈地處理上述工作佇列,按照各自的邏輯把叢集狀態向預期狀態推動。不同的 controller 處理的型別不同,比如 replicaset controller 關注的是副本數,會處理一些 Pod 相關的事件;
operator:operator 是描述、部署和管理 kubernetes 應用的一套機制,從實現上來說,可以將其理解為 CRD 配合可選的 webhook 與 controller 來實現使用者業務邏輯,即 operator = CRD + webhook + controller。
常見的 operator 工作模式
工作流程:
- 使用者建立一個自定義資源 (CRD);
- apiserver 根據自己註冊的一個 pass 列表,把該 CRD 的請求轉發給 webhook;
- webhook 一般會完成該 CRD 的預設值設定和引數檢驗。webhook 處理完之後,相應的 CR 會被寫入資料庫,返回給使用者;
- 與此同時,controller 會在後臺監測該自定義資源,按照業務邏輯,處理與該自定義資源相關聯的特殊操作;
- 上述處理一般會引起叢集內的狀態變化,controller 會監測這些關聯的變化,把這些變化記錄到 CRD 的狀態中。
這裡是從 High-Level 大概介紹一下,後面會結合案例重新梳理。
二、operator framework 實戰
operator framework 概述
在開始之前,首先介紹一下 operator framework。 它實際上給使用者提供了 webhook 和 controller 的框架,它的主要意義在於幫助開發者遮蔽了一些通用的底層細節,不需要開發者再去實現訊息通知觸發、失敗重新入隊等,只需關注被管理應用的運維邏輯實現即可。
主流的 operator framework 主要有兩個:kubebuilder 和 operator-sdk。
兩者實際上並沒有本質的區別,它們的核心都是使用官方的 controller-tools 和 controller-runtime。不過細節上稍有不同,比如 kubebuilder 有著更為完善的測試與部署以及程式碼生成的腳手架等;而 operator-sdk 對 ansible operator 這類上層操作的支援更好一些。
kuberbuildere 實戰
這裡的實戰選用的是 kuberbuilder。案例選用的是阿里雲對外開放的 kruise 專案下的 SidercarSet。
SidercarSet 的功能就是負責給 Pod 插入 sidecar 容器(也稱為輔助容器),例如可以插入一些監控,日誌採集來豐富這個 Pod 的功能,然後根據插入的狀態、Pod 的狀態反過來更新 SidercarSet 以記錄這些輔助容器的狀態。
Step 1: 初始化
操作:新建一個 gitlab 專案,執行 "kubebuilder init --domain=kruise.io"。
引數解讀:domain 指定了後續註冊 CRD 物件的 Group 域名。
效果解讀:拉取依賴程式碼庫、生成程式碼框架、生成 Makefile/Dockerfile 等工具檔案。
我們來看一下它具體生成的內容_(為了方便演示,實際生成檔案做了部分刪減):
具體的內容大家可以在實戰的時候自己進行詳細的確認。
Step 2: 建立 API
操作:執行 "kubebuilder create api --group apps --version v1alpha1 --kind SidecarSet --namespace=false" 實際上不僅會建立 API,也就是 CRD,還會生成 Controller 的框架。
引數解讀:- group 加上之前的 domian 即此 CRD 的 Group: apps.kruise.io;
- version 一般分三種,按社群標準:
- v1alpha1: 此 api 不穩定,CRD 可能廢棄、欄位可能隨時調整,不要依賴;
- v1beta1: api 已穩定,會保證向後相容,特性可能會調整;
- v1: api 和特性都已穩定;
- kind: 此 CRD 的型別,類似於社群原生的 Service 的概念;
- namespaced: 此 CRD 是全域性唯一還是 namespace 唯一,類似 node 和 Pod。
它的引數基本可以分為兩類。group, version, kind 基本上對應了 CRD 元資訊的三個重要組成部分。這裡給出了一些常見的標準,大家實際使用的時候可以參考一下。namespaced 則用於指定我們剛剛建立的 CRD 時全域性唯一的(如 node)還是 namespace 唯一的(如 Pod)。這裡用了 false,即指定 SidecarSet 為全域性唯一的。
效果解讀:
生成了 CRD 和 controller 的框架,後面需要手工填充程式碼。
實際結果如下圖所示:
我們重點關注是圖中藍色字型部分。sidecarset_types.go 就是定義 CRD 的地方,需要我們填充。sidecarset_controller.go 則用於定義 controller,同樣需要進行填充。
Step 3: 填充 CRD
1. 生成的 CRD 位於 "pkg/apis/apps/v1alpha1/sidecarset_types.go",通常需要進行如下兩個操作:
(1) 調整註釋
code generator 依賴註釋生成程式碼,因此有時需要調整註釋。以下列出了本次實戰中 SidecarSet 需要調整的註釋:
+genclient:nonNamespaced: 生成非 namespace 物件; +kubebuilder:subresource:status: 生成 status 子資源; +kubebuilder:printcolumn:name="MATCHED",type='integer',JSONPath=".status.matchedPods",description="xxx": kubectl get sidecarset: 後續展示相關。
(2) 填充欄位
填充欄位是令使用者的 CRD 實際生效、實際有意義的重要部分。
- SidecarSetSpec: 填充 CRD 描述資訊;
- SidecarSetStatus: 填充 CRD 狀態資訊。
2. 填充完執行 make 重新生成程式碼即可
需要注意的是,研發人員無需參與 CRD 的 grpc 介面、編解碼等 controller 的底層實現。
實際的填充如下圖所示:
SidecarSet 的功能是給 Pod 注入 Sidecar,為了完成該功能,我們在 SidecarSetSpec(左圖) 定義了兩個主要資訊:一個是用於選擇哪些 Pod 需要被注入的 Selector;一個是定義 Sidecar 容器的 Containers。
在 SidecarSetStatus(右圖)中定義了狀態資訊,MatchedPods 反映的是該 SidecarSet 實際匹配了多少 Pod,UpdatedPods 反映的是已經注入了多少,ReadyPods 反映的則是有多少 Pod 已經正常工作了。
完整的內容可以參考該文件。
Step 4: 生成 webhook 框架
1. 生成 mutating webhook,執行:
"kubebuilder alpha webhook --group apps --version v1alpha1 --kind SidecarSet --type=mutating --operations=create"
"kubebuilder alpha webhook --group core --version v1 --kind Pod --type=mutating --operations=create"
2. 生成 validating webhook,執行:
"kubebuilder alpha webhook --group apps --version v1alpha1 --kind SidecarSet --type=validating --operations=create,update"
引數解讀:
- group/kind 描述需要處理的資源物件;
- type 描述需要生成哪種型別的框架;
- operations 描述關注資源物件的哪些操作。
效果解讀:
- 生成了 webhook 框架,後面需要手工填充程式碼
實際生成結果如下圖所示:
我們執行了三條命令,分別生成了三個不同的需要填充的 handler 中(上圖藍色字型部分)。這裡先不提,在下一步填充操作中再對其詳細講解。
Step 5: 填充 webhook
生成的 webhook handler 分別位於:
- - pkg/webhook/default_server/sidecarset/mutating/xxx_handler.go
- - pkg/webhook/default_server/sidecarset/validating/xxx_handler.go
- - pkg/webhook/default_server/pod/mutating/xxx_handler.go
需要改寫、填充的一般包括以下兩個部分:
是否需要注入 K8s client:取決於除了傳入的 CRD 以外是否還需要其它資源。在本實戰中,不僅要關注 SidecarSet,同時還要注入 Pod,因此需要注入 K8s client;
填充 webhook 的關鍵方法:即 mutatingSidecarSetFn 或 validatingSidecarSetFn。由於待操作資源物件指標已經傳入,因此直接調整該物件屬性即可完成 hook 的工作。
我們來看一下實際的填充結果。
因為第四步我們定義了三個:sidecarset mutating、sidecarset mutaing、pod mutating。
先來看上圖左側的 sidecarset mutating,首先是 setDefaultSidecarSet 把預設值設定好,這也是 mutaing 最常做的事情。
上圖右側 validating 也是非常的標準,也是對 SidecarSet 一些欄位進行校驗。
關於 pod mutaing 這裡沒有做展示,這裡面有些不同,這裡面的 mutaingSidecarSetFn 不是進行預設值設定,而是獲取 setDefaultSidecarSet 的數值,然後注入到 Pod 裡面。
Step 6: 填充 controller
生成的 controller 框架位於 pkg/controller/sidecarset/sidecarset_controller.go。主要有三點需要進行修改:
- 修改許可權註釋。框架會自動生成形如 //+kuberbuilder:rbac;groups=apps,resources=deployments/status,verbs=get;update;path 的註釋,我們可以按照自己的需求修改,該註釋最終會生成 rbac 規則;
- 增加入隊邏輯。預設的程式碼框架會填充 CRD 本身的入隊邏輯(如 SidecarSet 物件的增刪改都會加入工作佇列),如果需要關聯資源物件的觸發機制(如 SidecarSet 也需關注 Pod 的變化),則需手工新增它的入隊邏輯;
- 填充業務邏輯。修改 Reconcile 函式,迴圈處理工作佇列。Reconcile 函式主要完成「根據 Spec 完成業務邏輯」和「將業務邏輯結果反饋回 status」兩部分。需要注意的是,如果 Reconcile 函式出錯返回 err,預設會重新入隊。
我們來看一下 SidecarSet 的 Controller 的填充結果:
addPod 中先取回該 Pod 對應的 SidecarSet 並將其加入佇列以便 Reconcile 進行處理。
Reconcile 將 SidercarSet 取出之後,根據 Selector 選擇匹配的 Pod,最後根據 Pod 當前的狀態資訊計算出叢集的狀態,然後回填到 CRD 的狀態中。
三、SidecarSet 的工作流程
最後我們再來重新梳理一下 SidecarSet 的工作流程以便我們理解 operator 是如何工作的。
- 使用者建立一個 SidecarSet;
- webhook 收到該 SidecarSet 之後,會進行預設值設定和配置項校驗。這兩個操作完成之後,會完成真正的入庫,並返回給使用者;
- 使用者建立一個 Pod;
- webhook 會拿回對應的 SidecarSet,並從中取出 container 注入 Pod 中,因此 Pod 在實際入庫時就已帶有了剛剛的 sidecar;
- controller 在後臺不停地輪詢,檢視叢集的狀態變化。第 4 步中的注入會觸發 SidecarSet 的入隊,controller 就會令 SidecarSet 的 UpdatedPods 加 1。
以上就是 SidecarSet 前期一個簡單的功能實現。
這裡我們再補充一個問題。一般的 webhook 由 controller 來完成業務邏輯、狀態更新,但這個不是一定的,兩者之一可以不是必須的。在以上的示例中就是由 webhook 完成主要的業務邏輯,無需 controller 的參與。
四、本文總結
本文的主要內容就到此為止了,這裡為大家簡單總結一下:
- Operator 是 CRD 配合 可選的 webhook 和 controller,在 Kubernetes 體系下擴充套件使用者業務邏輯的一套機制;
- kubebuilder 是社群認可度很高的一種官方、標準化 Operator 框架;
- 按照上文實戰步驟,填充使用者自定義程式碼,就可以很方便的實現一個 Operator。
“阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術圈。”
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- 從零開始入門 K8s | Kubernetes API 程式設計正規化K8SAPI程式設計
- Kubernetes Operator 開發入門
- Kubernetes Operator基礎入門
- 從零開始入門 K8s | Kubernetes 網路模型進階K8S模型
- 12 . Kubernetes之Statefulset 和 Operator
- 從零開始入門 K8s | Kubernetes 網路概念及策略控制K8S
- 談談 Kubernetes Operator
- Kubernetes裡的Operator
- 從零開始入門 K8s| 詳解 Pod 及容器設計模式K8S設計模式
- 從零開始入門 K8s | Kubernetes 儲存架構及外掛使用K8S架構
- 從零開始入門 K8s | 理解 CNI 和 CNI 外掛K8S
- 基礎設施即程式碼(IAC),Zalando Postgres Operator UI 入門UI
- 從零開始入門 K8s | 深入剖析 Linux 容器K8SLinux
- 在 Kubernetes 上彈性深度學習訓練利器 -- Elastic Training Operator深度學習ASTAI
- 從零開始入門 K8s | GPU 管理和 Device Plugin 工作機制K8SGPUdevPlugin
- 從 Helm 到 Operator:Kubernetes應用管理的進化
- FPGA程式設計從零開始 使用VerilogFPGA程式設計
- 從零開始入門 K8s | Kata Containers 創始人帶你入門安全容器技術K8SAI
- 從零開始入門 K8s | K8s 安全之訪問控制K8S
- 從零開始入門 K8s | 理解 RuntimeClass 與使用多容器K8S
- 併發程式設計從入門到放棄系列開始和結束程式設計
- Photon物聯網程式設計從零開始程式設計
- 從0到1使用kubebuiler開發operatorUI
- 從零開始入門 K8s | etcd 效能最佳化實踐K8S
- 從零開始入門 K8s | 手把手帶你理解 etcdK8S
- 從零開始入門 K8s | 理解容器執行時介面 CRIK8S
- 風變程式設計,幫助你從零開始學習程式設計程式設計
- kubebuilder實戰之四:operator需求說明和設計UI
- python operator和Branching and cyclingPython
- 併發程式設計從零開始(十一)-Atomic類程式設計
- 從零開始入門 K8s | 有狀態應用編排 - StatefulSetK8S
- Spark in action on Kubernetes - Spark Operator的原理解析Spark
- Arduino程式設計從零開始 使用C和C++(第2版)UI程式設計C++
- kubernetes 1.15.1 高可用部署 -- 從零開始
- 從零開始的Java程式設計之抽象與介面Java程式設計抽象
- 併發程式設計從零開始(九)-ConcurrentSkipListMap&Set程式設計
- 併發程式設計從零開始(十四)-Executors工具類程式設計
- 從零開始入門 K8s | 可觀測性:監控與日誌K8S