Kubernetes Operator 提供了擴充套件 Kubernetes API 的機制,讓開發者能以宣告式的方式管理複雜應用程式。本文詳細說明如何使用 Kubebuilder 工具來建立一個 Operator,並定義自訂資源 EGApp,包含 Spec 和 Status 結構的設計與驗證規則設定。文章也解析了 CRD 檔案的結構,以及如何生成和安裝 CRD。此外,更進一步說明 Controller 的核心邏輯,包含 Reconcile 函式的運作流程、Deployment 的建立與更新,以及如何將 Pod 狀態同步到自訂資源的 Status 中,提供完整的 Operator 開發流程參考。
Kubernetes Operator 開發:API 定義與自訂資源
Kubernetes Operator 是擴充套件 Kubernetes 功能的重要工具,允許開發者建立自訂資源並自動化複雜應用程式的管理。本文將介紹如何使用 Kubebuilder 建立一個簡單的 Operator,並定義自訂 API 資源。
建立 Operator 專案
首先,使用 Kubebuilder 建立新的 Operator 專案:
$ kubebuilder init --domain platform.evillgenius.com --repo platformapp
這將建立基本的專案結構,包括 main.go、Makefile 和其他必要檔案。
建立 API 定義
接下來,建立新的 API 定義:
$ kubebuilder create api --group egplatform --version v1alpha1 --kind EGApp
這將建立 api/v1alpha1/egapp_types.go 和 controllers/egapp_controller.go 檔案,分別定義了自訂資源的結構和控制器邏輯。
修改 API 定義
在 api/v1alpha1/egapp_types.go 中,修改 EGAppSpec 和 EGAppStatus 結構以滿足應用程式需求:
type EGAppSpec struct {
// AppId 是內部目錄系統的唯一匹配 ID
AppId string `json:"appId,omitempty"`
// +kubebuilder:validation:Enum=java;python;go
Framework string `json:"framework"`
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Enum=lowMem;highMem;highCPU;balanced
// +kubebuilder:default="lowMem"
InstanceType string `json:"instanceType"`
// +kubebuilder:validation:Enum=dev;stage;prod
Environment string `json:"environment"`
// +kubebuilder:validation:Optional
// +kubebuilder:default:=1
ReplicaCount int32 `json:"replicaCount"`
}
type EGAppStatus struct {
// Pod 清單
Pods []string `json:"pods"`
}
內容解密:
EGAppSpec定義了自訂資源的期望狀態,包括應用程式 ID、框架型別、例項型別、環境和副本數量。EGAppStatus定義了自訂資源的觀察狀態,包含目前執行的 Pod 清單。- 使用 Kubebuilder 的 marker comments(如
+kubebuilder:validation:Enum)來驗證欄位值。
生成 CRD 和清單
修改完成後,執行以下命令生成 CRD 和其他清單:
$ make generate
$ make manifests
這將更新 config/crd/bases 下的 CRD 檔案。
CRD 結構解析
生成的 CRD 檔案定義了自訂資源的結構,以下是部分內容:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: egapps.egplatform.platform.evillgenius.com
spec:
group: egplatform.platform.evillgenius.com
names:
kind: EGApp
plural: egapps
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
properties:
spec:
properties:
appId:
type: string
environment:
enum:
- dev
- stage
- prod
type: string
圖表翻譯:
此圖示呈現了 CRD 的結構,包括 API 群組、版本、名稱和結構定義。使用 OpenAPI V3 Schema 驗證自訂資源的內容。
圖表翻譯: 此圖表展示了 CRD 的組成部分,包括 API 群組、版本、名稱和結構定義。結構定義進一步分為 Spec 和 Status,其中 Spec 包含應用程式的詳細設定,如 appId、environment 和 framework。
自訂資源定義與 Operator 實作詳解
在 Kubernetes 中,Operator 是一種用於管理和維護特定應用程式或資源的自訂控制器。本章節將探討如何使用 Kubebuilder 建立自訂資源定義(CRD)以及實作 Operator 的核心邏輯。
建立自訂資源定義
首先,我們使用 Kubebuilder 建立一個名為 EGApp 的自訂資源。EGApp 的結構定義如下:
type: object
properties:
environment:
type: string
framework:
type: string
status:
type: object
properties:
pods:
type: array
items:
type: string
required:
- pods
程式碼解密:
type: object定義了EGApp是一個物件型別的資源。properties欄位定義了資源的屬性,包括environment、framework和status。status欄位是一個物件,包含了pods屬性,代表與該EGApp相關的 Pod 清單。required欄位確保了status中必須包含pods屬性。
Kubebuilder 不僅生成了基本的 CRD 結構,還加入了 OpenAPI 驗證資訊,以確保自訂資源符合特定的規範。
安裝自訂資源定義
執行以下指令,可以將 EGApp CRD 安裝到 Kubernetes 叢集中:
$ make install
該指令會使用 Kubebuilder 生成必要的組態檔案,並透過 kubectl apply 將 CRD 安裝到叢集中。
程式碼解密:
make install指令會呼叫預先定義的 Makefile 規則,生成 CRD 組態檔案並安裝到叢集中。/home/eddiejv/dev/projects/operators/platformapp/bin/controller-gen是用於生成 CRD 和 Webhook 組態的工具。kubectl apply -f -將生成的 CRD 組態套用到叢集中。
驗證自訂資源
安裝完成後,可以使用以下指令檢視 EGApp 資源的結構和說明:
$ kubectl explain egapp --recursive
程式碼解密:
kubectl explain指令用於顯示 Kubernetes 資源的詳細說明。--recursive引數表示遞迴顯示所有巢狀欄位的說明。
Controller 調和邏輯
Operator 的核心是調和(Reconciliation)邏輯,用於確保自訂資源的實際狀態與預期狀態一致。調和邏輯主要實作在 controllers/<kind>_controller.go 檔案中的 Reconcile 方法。
調和邏輯的基本流程如下圖所示:
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Kubernetes Operator 開發 API 與自訂資源
package "Kubernetes Cluster" {
package "Control Plane" {
component [API Server] as api
component [Controller Manager] as cm
component [Scheduler] as sched
database [etcd] as etcd
}
package "Worker Nodes" {
component [Kubelet] as kubelet
component [Kube-proxy] as proxy
package "Pods" {
component [Container 1] as c1
component [Container 2] as c2
}
}
}
api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2
note right of api
核心 API 入口
所有操作經由此處
end note
@enduml圖表翻譯:
此圖示展示了 Operator 的調和邏輯流程。首先檢查是否存在自訂資源,若存在則進行驗證。若驗證透過,則檢查是否需要變更狀態,並進行相應的變更。最後結束調和流程。
資源驗證
資源驗證是 Operator 中非常重要的一環。驗證邏輯可以分為多個層級:
- OpenAPI 驗證:在 CRD 定義中指定驗證規則,防止無效資源進入叢集狀態。
- 驗證 Webhook:透過 Webhook 在 API 伺服器層級進行額外的驗證。
- 調和迴圈中的驗證:在調和邏輯中實作額外的驗證邏輯,以處理已存在資源的驗證。
func (r *EGAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// 取得 EGApp 資源
var egapp egplatformv1alpha1.EGApp
if err := r.Get(ctx, req.NamespacedName, &egapp); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 驗證資源
if !egapp.IsValid() {
return ctrl.Result{}, errors.New("invalid EGApp resource")
}
// 調和邏輯...
}
程式碼解密:
Reconcile方法是 Operator 調和邏輯的入口點。- 首先透過
r.Get方法取得指定的EGApp資源。 - 若資源不存在,則忽略錯誤並結束調和。
- 若資源存在,則呼叫
IsValid方法進行驗證。若驗證失敗,則傳回錯誤。 - 若驗證透過,則繼續執行調和邏輯。
Controller 實作詳解
在 Kubernetes Operator 的開發過程中,Controller 是核心元件,負責監控自訂資源(Custom Resource, CR)的狀態並確保叢集的實際狀態與預期狀態一致。本文將探討 Controller 的實作細節,並解析其關鍵程式碼。
Reconcile 函式解析
Reconcile 是 Controller 的核心函式,負責調節叢集狀態使其符合自訂資源的定義。以下為關鍵實作:
func (r *EGAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.Log.WithValues("EGApp", req.NamespacedName)
logger.Info("EGApp Reconcile started...")
// 取得 EGApp CR 例項
egApp := &egplatformv1alpha1.EGApp{}
err := r.Get(ctx, req.NamespacedName, egApp)
if err != nil {
// 處理資源不存在的情況
if errors.IsNotFound(err) {
logger.Info("EGApp resource not found. Object must be deleted")
return ctrl.Result{}, nil
}
logger.Error(err, "Failed to get EGApp")
return ctrl.Result{}, err
}
// 檢查 Deployment 是否存在
found := &appsv1.Deployment{}
err = r.Get(ctx, types.NamespacedName{Name: egApp.Name, Namespace: egApp.Namespace}, found)
if err != nil {
// 建立新的 Deployment
dep := r.deploymentForEGApp(egApp)
logger.Info("Creating a new deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
err = r.Create(ctx, dep)
if err != nil {
logger.Error(err, "Failed to create new deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
// 確保 Deployment 的副本數與規格一致
replicas := egApp.Spec.ReplicaCount
if *found.Spec.Replicas != replicas {
found.Spec.Replicas = &replicas
err = r.Update(ctx, found)
if err != nil {
logger.Error(err, "Failed to update Deployment", "Deployment.Namespace", found.Namespace, "Deployment.Name", found.Name)
return ctrl.Result{}, err
}
return ctrl.Result{Requeue: true}, nil
}
// 更新 EGApp 狀態中的 Pod 名稱
podList := &corev1.PodList{}
listOpts := []client.ListOption{
client.InNamespace(egApp.Namespace),
client.MatchingLabels(egApp.GetLabels()),
}
if err = r.List(ctx, podList, listOpts...); err != nil {
logger.Error(err, "Failed to list pods", "egApp.Namespace", egApp.Namespace, "egApp.Name", egApp.Name)
return ctrl.Result{}, err
}
podNames := getPodNames(podList.Items)
if !reflect.DeepEqual(podNames, egApp.Status.Pods) {
egApp.Status.Pods = podNames
err := r.Status().Update(ctx, egApp)
if err != nil {
logger.Error(err, "Failed to update egApp status")
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
內容解密:
Reconcile 流程
- 首先透過
req.NamespacedName取得對應的 EGApp 資源例項。 - 若資源不存在則直接傳回,表示資源已被刪除。
- 若資源存在,則檢查對應的 Deployment 是否存在。
- 首先透過
Deployment 管理
- 若 Deployment 不存在,則建立新的 Deployment。
- 若已存在,則檢查其副本數是否與 EGApp 規格中的
ReplicaCount一致,不一致則更新。
狀態更新
- 取得與 Deployment 相關聯的 Pod 清單。
- 將 Pod 名稱更新至 EGApp 的 Status 子資源中。
Deployment 建立邏輯
func (r *EGAppReconciler) deploymentForEGApp(m *egplatformv1alpha1.EGApp) *appsv1.Deployment {
ls := m.GetLabels()
replicas := m.Spec.ReplicaCount
deploy := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: m.Name,
Namespace: m.Namespace,
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: ls,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: ls,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{{
Image: "gcr.io/kuar-demo/kuard-amd64:1",
Name: m.Spec.AppId,
Ports: []corev1.ContainerPort{{
ContainerPort: 8080,
Name: "http",
}},
}},
},
},
},
}
ctrl.SetControllerReference(m, deploy, r.Scheme)
return deploy
}
內容解密:
Deployment 組態
- 使用 EGApp 的標籤和規格建立 Deployment。
- 設定容器映像和埠號組態。
- 使用
ctrl.SetControllerReference建立 CR 與 Deployment 的從屬關係。
重要設計考量
- 這種從屬關係確保當 EGApp 被刪除時,相關的 Deployment 也會被清理。
- 未來可將硬編碼的映像檔改為動態組態。
本地測試與佈署
完成 Controller 邏輯後,可以透過以下指令進行本地測試:
$ make run
該指令會:
- 生成必要的 CRD 和 RBAC 設定
- 編譯並執行 Controller 程式碼
- 在本地啟動 metrics 和 health probe 服務