使用kubebuilder開發(fā)簡單的Operator
云原生技術(shù)通過方法論、工具集和最佳實踐重塑著整個軟件技術(shù)棧和生命周期,云原生對云計算服務(wù)方式與互聯(lián)網(wǎng)架構(gòu)進行整體性升級, 深刻改變著整個 IT 領(lǐng)域。云原生的核心是 kubernetes,圍繞 kubernetes 構(gòu)建滿足自身需求的 PaaS 平臺(應(yīng)用中心)是絕大數(shù)企業(yè)的訴求, 但是不同企業(yè)自身場景往往存在一定的差異,Operator 是最常見 kubernetes 拓展方式。本片博文,我將會給大家理清 Operator 的來龍去脈, 同時介紹如何通過 kubebuilder 快速開發(fā)一個簡單的 Operator。
Operator 誕生的背景
kubernetes 無法做到真正意義的開箱即用的,它與傳統(tǒng)的 PaaS 平臺不同,它僅僅只提供核心的基礎(chǔ)設(shè)施功能,但是還無法滿足用戶的最終需求,這里用戶主要指業(yè)務(wù)開發(fā)和業(yè)務(wù)運維, 比如說業(yè)務(wù)開發(fā)需要 CI/CD 工具實現(xiàn) Devops 的功能,原生 kubernetes 是不提供支持的,但是我們可以通過tekton這一個第三方工具實現(xiàn) DevOps 相關(guān)功能, 這也正是 kubernetes 區(qū)別傳統(tǒng)PaaS平臺的真正強大之處,其提供完善的擴展機制以及基于此而發(fā)展出來的海量的第三方工具和豐富的生態(tài)。
Operator pattern首先由 CoreOS 提出,通過結(jié)合 CRD 和 custom controller 將特定應(yīng)用的運維知識轉(zhuǎn)換為代碼,實現(xiàn)應(yīng)用運維的自動化和智能化。Operator 允許 kubernetes 來管理復(fù)雜的,有狀態(tài)的分布式應(yīng)用程序,并由 kubernetes 對其進行自動化管理,例如,etcd operator 能夠創(chuàng)建并管理一組 etcd 集群, 定制化的 controller 組件了解這些資源,知道如何維護這些特定的應(yīng)用。
隨著 kubernetes 的功能越來越復(fù)雜,其需要管理的資源在高速增長,對應(yīng)的 API 和 controller 的數(shù)量也愈發(fā)無法控制, kubernetes 變得很臃腫,很多不必要的 API 和功能將出現(xiàn)在每次安裝的集群中。
為了解決這個問題,CRD 應(yīng)運而生,CRD 由 TPR(Third Part Resource v1.2 版本引入)演化而來,v1.7 進入 beta,v1.8 進入穩(wěn)定, 通過 CRD,kubernetes 可以動態(tài)的添加并管理資源。CRD 解決了結(jié)構(gòu)化數(shù)據(jù)存儲的問題,Controller 則用來跟蹤這些資源, 保證資源的狀態(tài)滿足期望值。CRD+Controller=decalartive API,聲明式 API 設(shè)計是 kubernetes 重要的設(shè)計思想, 該設(shè)計保證能夠動態(tài)擴展 kubernetes API,這種模式也正是 Operator pattern。
kubernetes 本身也在通過 CRD 添加新功能,我們有什么理由不使用呢?
使用場景總結(jié)及舉例
CRD+custom controller 已經(jīng)被廣泛地使用,按使用場景可劃分為以下兩種:
通用型 controller: 這種場景和 kubernetes 內(nèi)置的 apps controller類似,主要解決特定通用類型應(yīng)用的管理Operator: 該場景用于解決一個特定應(yīng)用的自動化管理
通用型 controller 往往是 kubernetes 平臺側(cè)用戶,如各大云廠商和 kubernetes 服務(wù)提供商,Operator 則是各種軟件服務(wù)提供商, 他們設(shè)計時面向單一應(yīng)用,很多開源的應(yīng)用的 operator 可以在 operator hub 中獲取。我列舉一些示例供大家參考:
通用型 Controller
阿里的 cafedeploymentcontroller 解決金融場景下分布式應(yīng)用特殊需求。 oam-kubernetes-runtime 實現(xiàn)了 Application Model (OAM),以系統(tǒng)可持續(xù)的方式拓展 kubernetes
Operator
etcd operator Prometheus operator
通用型Controller與kubernetes自帶的幾個controller類似,旨在解決一些通用的應(yīng)用模型,而Operator則更加面向單個特定應(yīng)用, 這兩者沒有本質(zhì)的區(qū)別。
如何開發(fā) CRD
作為kubernetes開發(fā)者,如何開發(fā) CRD+Custom cntroller 呢?其實官方提供示例項目sample-controller供開發(fā)者參考,開發(fā)流程大致有以下幾個過程:
初始化項目結(jié)構(gòu)(可根據(jù) sample controller 修改) 定義 CRD 生成代碼 初始化 controller 實現(xiàn) controller 具體邏輯
如果你想要快速構(gòu)建 CRD 和 Custom controller,腳手架工具是個不錯的選擇,如果是學(xué)習(xí)目的,建議結(jié)合 sample-controller 和 kubernetes controller 相關(guān) 源碼。
kubebuilder 詳解
kubebuilder 是一個幫助開發(fā)者快速開發(fā) kubernetes API 的腳手架命令行工具,其依賴庫 controller-tools 和 controller-runtime, controller-runtime 簡化 kubernetes controller 的開發(fā),并且對 kubernetes 的幾個常用庫進行了二次封裝, 以簡化開發(fā)者使用。controller-tool 主要功能是代碼生成。下圖是使用 kubebuilder 的工作流程圖:
文章后面會結(jié)合一個簡單示例來介紹開發(fā)流程。
“kubebuilder 有非常良好的文檔,包括一個從零開始的示例,您應(yīng)該以文檔為主。
”
使用 kubebuilder 開發(fā)一個 CRD
本次示例創(chuàng)建一個通用的Application資源,Application 包含一個子資源 Deployment 以及一個 Count 資源, 每當(dāng) Application 進行被重新協(xié)調(diào)Reconcil,Count 會進行自增。
“全部代碼請參考代碼倉
”
前提(你需要提前了解的)
Golang 開發(fā)者,kubernetes 大量使用 Code Generate這一功能來自動生成重復(fù)性代碼閱讀 kubernetes controller 的代碼 閱讀 kubebuilder 的文檔 了解 kustomize
開發(fā)步驟及主要代碼展示
首先,根據(jù)你的開發(fā)環(huán)境安裝 kubebuilder 工具,mac 下通過 homebrew 安裝命令如下:
BASH
? ~ brew install kubebuilder
? ~ kubebuilder version
Version: version.Version{KubeBuilderVersion:"unknown", KubernetesVendor:"unknown", GitCommit:"$Format:%H$", BuildDate:"1970-01-01T00:00:00Z", GoOs:"unknown", GoArch:"unknown"}
安裝完畢后,首先創(chuàng)建項目目錄custom-controller并使用go mod初始化項目
BASH
? cd custom-controllers
? ls
? go mod init controllers.happyhack.io
接著,使用 kubebuilder 初始化項目,生成相關(guān)文件和目錄,并創(chuàng)建 CRD 資源
BASH
# 使用kubebuilder初始化項目
? custom-controllers kubebuilder init --domain controller.daocloud.io --license apache2 --owner "Holder"
# 創(chuàng)建CRD資源
? custom-controllers kubebuilder create api --group controller --version v1 --kind Application
Create Resource [y/n]
y
Create Controller [y/n]
y
Writing scaffold for you to edit...
api/v1/application_types.go
controllers/application_controller.go
Running make:
$ make
/Users/donggang/Documents/Code/golang/bin/controller-gen object:headerFile="hack/huilerplate.go.txt" paths="./..."
go fmt ./...
go vet ./...
go build -o bin/manager main.go
到目前,該項目就已經(jīng)能夠運行了,不過我們需要添加我們自己業(yè)務(wù)代碼,主要包括修改 CRD 定義和添加 controller 邏輯兩部分。首先修改 API 資源定義即 CRD 定義,Application 包含一個 Deployment,我們可以參考 kubernetes Deployment 與 Pod 這兩個類型之間的關(guān)系設(shè)計 Application 和 Deployment, Deployment 通過字段spec.template來描述如何創(chuàng)建 Pod,DeploymentTemplateSpec描述了該如何創(chuàng)建 Deployment,
GOLANG
// PodTemplateSpec describes the data a deployment should have when created from a template
type DeploymentTemplateSpec struct {
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Specification of the desired behavior of the pod.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
Spec v12.DeploymentSpec `json:"spec,omitempty"`
}
API 描述定義完成了,接下來需要我們來進行具體業(yè)務(wù)邏輯實現(xiàn)了,編寫具體 controller 實現(xiàn),首先我們簡單梳理 controller 的主要邏輯
當(dāng)一個 application 被創(chuàng)建時,需要創(chuàng)建對應(yīng)的 deployment,當(dāng) application 被刪除或更新時,對應(yīng) Deployment 也需要被刪除或更新 當(dāng) application 對應(yīng)的子資源 deployment 被其他客戶端刪除或更新時,controller 需要重建或恢復(fù)它 最后一步更新 application 的 status,這里即 count 加 1
我們在方法func(r *ApplicationReconciler) Reconcile(req ctrl.Request)(ctrl.Result,error)實現(xiàn)相關(guān)邏輯, 當(dāng)然當(dāng)業(yè)務(wù)邏輯比較復(fù)雜時,可以拆分為多個方法。
GOLANG
func (r *ApplicationReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ctx := context.Background()
log := r.Log.WithValues("application", req.NamespacedName)
var app controllerv1.Application
if err := r.Get(ctx, req.NamespacedName, &app); err != nil {
log.Error(err, "unable to fetch app")
return ctrl.Result{}, client.IgnoreNotFound(err)
}
selector, err := metav1.LabelSelectorAsSelector(app.Spec.Selector)
if err != nil {
log.Error(err, "unable to convert label selector")
return ctrl.Result{}, err
}
var deploys v12.DeploymentList
if err := r.List(ctx, &deploys, client.InNamespace(req.Namespace), client.MatchingLabelsSelector{Selector: selector}); err != nil {
if errors.IsNotFound(err) {
deploy, err := r.constructDeploymentForApplication(&app)
if err != nil {
log.Error(err, "unable to construct deployment")
return ctrl.Result{
RequeueAfter: time.Second * 1,
}, err
}
if err = r.Create(ctx, deploy); err != nil {
return ctrl.Result{RequeueAfter: time.Second * 1}, err
}
}
}
...
}
完成Reconcile方法后,我們可以修改config目錄的示例yaml,來進行本地測試了。
官方開發(fā)自定義 Controller 的指導(dǎo)
kubernetes開箱自帶了多個controller,這些controller在我們開發(fā)時具有非常重要的參考價值,同時社區(qū)也總結(jié)了的 controller 開發(fā)所需要遵循十一條原則, 但是請大家結(jié)合實際場景靈活運用這些原則:
總結(jié)及展望
本文簡單介紹了 CRD 以及如何使用腳手架工具 kubebuilder 幫助我們開發(fā)自定義 controller,當(dāng)然這個 controller 示例的邏輯比較簡單, 在實際場景中,我們會遇到很多的挑戰(zhàn),比如controller 的邏輯會比較復(fù)雜、需要通過多個controller等。作為kubernetes開發(fā)者, Controller開發(fā)是一項必不可少的技能。
“原文鏈接:https://blog.happyhack.io/2020/10/12/kubernetes-crd-day1/
”




