kubebuilder實戰之四:operator需求說明和設計

程式設計師欣宸發表於2021-08-28

歡迎訪問我的GitHub

https://github.com/zq2599/blog_demos

內容:所有原創文章分類彙總及配套原始碼,涉及Java、Docker、Kubernetes、DevOPS等;

系列文章連結

  1. kubebuilder實戰之一:準備工作
  2. kubebuilder實戰之二:初次體驗kubebuilder
  3. kubebuilder實戰之三:基礎知識速覽
  4. kubebuilder實戰之四:operator需求說明和設計
  5. kubebuilder實戰之五:operator編碼
  6. kubebuilder實戰之六:構建部署執行
  7. kubebuilder實戰之七:webhook
  8. kubebuilder實戰之八:知識點小記

本篇概覽

  • 作為《kubebuilder實戰》系列的第四篇,經歷了前面的充分準備,從本篇開始,我們們來開發一個有實際作用的operator,該operator名為elasticweb,既彈性web服務;
  • 這將是一次完整的operator開發實戰,設計、編碼、部署等環節都會參與到,與《kubebuilder實戰之二:初次體驗kubebuilder》的不同之處在於,elasticweb從CRD設計再到controller功能都有明確的業務含義,能執行業務邏輯,而《kubebuilder實戰之二》僅僅是一次開發流程體驗;
  • 為了做好這個operator,本篇不急於編碼,而是認真的做好設計工作,我們們的operator有什麼功能,解決了什麼問題,有哪些核心內容,都將在本篇整理清楚,有了這樣的準備,才能在下一章寫出符合要求的程式碼;
  • 接下來我們們先聊一些背景知識,以便更好的進入正題;

需求背景

  • QPS:Queries-per-second,既每秒查詢率,就是說伺服器在一秒的時間內處理了多少個請求;
  • 背景:做過網站開發的同學對橫向擴容應該都瞭解,簡單的說,假設一個tomcat的QPS上限為500,如果外部訪問的QPS達到了600,為了保障整個網站服務質量,必須再啟動一個同樣的tomcat來共同分攤請求,如下圖所示(簡單起見,假設我們們的後臺服務是無狀態的,也就是說不依賴宿主機的IP、本地磁碟之類):

在這裡插入圖片描述

  • 以上是橫向擴容常規做法,在kubernetes環境,如果外部請求超過了單個pod的處理極限,我們可以增加pod數量來達到橫向擴容的目的,如下圖:

在這裡插入圖片描述

  • 以上就是背景資訊,接下來我們們聊聊elasticweb這個operator的具體功能;

需求說明

  • 為了說清楚需求,這裡虛構一個場景:小欣是個java開發者,就是下圖這個妹子:

在這裡插入圖片描述

  • 現在小欣要將springboot應用部署到kubernetes上,她的現狀和麵臨的問題如下:
  1. springboot應用已做成docker映象;
  2. 通過壓測得出單個pod的QPS為500;
  3. 估算得出上線後的總QPS會在800左右;
  4. 隨著運營策略變化,QPS還會有調整;
  5. 總的來說,小欣手裡只有三個資料:docker映象、單個pod的QPS、總QPS,她對kubernetes不瞭解,需要有個方案來幫她將服務部署好,並且在執行期間能支撐外部的高併發訪問;

以上就是小欣的需求了,我們們來小結一下:

  1. 我們們為小欣開發一個operator(名為elasticweb),對小欣來說,她只要將手裡的三個引數(docker映象、單個pod的QPS、總QPS)告訴elasticweb就完事兒了;
  2. elasticweb在kubernetes建立pod,至於pod數量當然是自動算出來的,要確保能滿足QPS要求,以前面的情況為例,需要兩個pod才能滿足800的QPS;
  3. 單個pod的QPS和總QPS都隨時可能變化,一旦有變,elasticweb也要自動調整pod數量,以確保服務質量;
  4. 為了確保服務可以被外部呼叫,我們們再順便幫小欣建立好service(她對kubernetes瞭解不多,這事兒我們們就順手做了吧);

自保宣告

  • 看過上述需求後,聰明的您一定會對我投來鄙視的眼光,其實kubernetes早就有現成的QPS調節方案了,例如修改deployment的副本數、單個pod縱向擴容、autoscale等都可以,本次使用operator來實現僅僅是為了展示operator的開發過程,並不是說自定義operator是唯一的解決方案;

  • 所以,如果您覺得我這種用operator實現擴容的方式很low,請不要把我罵得太慘,我這也只是為了展示operator開發過程而已,況且我們這個operator也不是一無是處,用了這個operator,您就不用關注pod數量了,只要聚焦單例項QPS和總QPS即可,這兩個引數更貼近業務;

  • 為了不把事情弄複雜,假設每個pod所需的CPU和記憶體是固定的,直接在operator程式碼中寫死,其實您也可以自己改程式碼,改成可以在外部配置,就像映象名稱引數那樣;

  • 把需求都交代清楚了,接下來進入設計環節,先把CRD設計出來,這可是核心的資料結構;

CRD設計之Spec部分

Spec是用來儲存使用者的期望值的,也就是小欣手裡的三個引數(docker映象、單個pod的QPS、總QPS),再加上埠號:

  1. image:業務服務對應的映象
  2. port:service佔用的宿主機埠,外部請求通過此埠訪問pod的服務
  3. singlePodQPS:單個pod的QPS上限
  4. totalQPS:當前整個業務的總QPS
  • 對小欣來說,輸入這四個引數就完事兒了;

CRD設計之Status部分

  • Status用來儲存實際值,這裡設計成只有一個欄位realQPS,表示當前整個operator實際能支援的QPS,這樣無論何時,只要小欣用kubectl describe命令就能知道當前系統實際上能支援多少QPS;

CRD原始碼

  • 把資料結構說明白的最好方法就是看程式碼:
package v1

import (
	"fmt"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"strconv"
)

// 期望狀態
type ElasticWebSpec struct {
	// 業務服務對應的映象,包括名稱:tag
	Image string `json:"image"`
	// service佔用的宿主機埠,外部請求通過此埠訪問pod的服務
	Port *int32 `json:"port"`

	// 單個pod的QPS上限
	SinglePodQPS *int32 `json:"singlePodQPS"`
	// 當前整個業務的總QPS
	TotalQPS *int32 `json:"totalQPS"`
}

// 實際狀態,該資料結構中的值都是業務程式碼計算出來的
type ElasticWebStatus struct {
	// 當前kubernetes中實際支援的總QPS
	RealQPS *int32 `json:"realQPS"`
}

// +kubebuilder:object:root=true

// ElasticWeb is the Schema for the elasticwebs API
type ElasticWeb struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   ElasticWebSpec   `json:"spec,omitempty"`
	Status ElasticWebStatus `json:"status,omitempty"`
}

func (in *ElasticWeb) String() string {
	var realQPS string

	if nil == in.Status.RealQPS {
		realQPS = "nil"
	} else {
		realQPS = strconv.Itoa(int(*(in.Status.RealQPS)))
	}

	return fmt.Sprintf("Image [%s], Port [%d], SinglePodQPS [%d], TotalQPS [%d], RealQPS [%s]",
		in.Spec.Image,
		*(in.Spec.Port),
		*(in.Spec.SinglePodQPS),
		*(in.Spec.TotalQPS),
		realQPS)
}

// +kubebuilder:object:root=true

// ElasticWebList contains a list of ElasticWeb
type ElasticWebList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []ElasticWeb `json:"items"`
}

func init() {
	SchemeBuilder.Register(&ElasticWeb{}, &ElasticWebList{})
}

業務邏輯設計

  • CRD的完成代表核心資料結構已經確定,接下來是業務邏輯的設計,主要是理清楚controller的Reconcile方法裡面做些啥,其實核心邏輯還是非常簡單的:算出需要多少個pod,然後通過更新deployment讓pod數量達到要求,在此核心的基礎上再把建立deployment和service、更新status這些瑣碎的事情做好,就完事兒了;

  • 這裡將整個業務邏輯的流程圖給出來如下所示,用於指導開發:

在這裡插入圖片描述

  • 至此,我們們完成了整個elasticweb的需求和設計,聰明的您肯定已經胸有成竹,而且迫不及待的想啟動開發了,好的,下一篇我們們正式開始編碼!

參考資料

  • 您可能會奇怪,小欣對kubernetes不瞭解,怎麼會知道docker映象的製作,還有單個pod的QPS她是怎麼測的呢?
  • 其實她是程式設計師欣宸的粉絲,已經閱讀過以下部落格:
  1. 《SpringBoot-2.3映象方案為什麼要做多個layer》
  2. 《體驗SpringBoot(2.3)應用製作Docker映象(官方方案)》
  3. 《詳解SpringBoot(2.3)應用製作Docker映象(官方方案)》
  4. 《Kubernetes下web服務的效能測試三部曲之一:準備工作》
  5. 《Kubernetes下web服務的效能測試三部曲之二:縱向擴容》
  6. 《Kubernetes下web服務的效能測試三部曲之三:橫向擴容》

你不孤單,欣宸原創一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 資料庫+中介軟體系列
  6. DevOps系列

歡迎關注公眾號:程式設計師欣宸

微信搜尋「程式設計師欣宸」,我是欣宸,期待與您一同暢遊Java世界...
https://github.com/zq2599/blog_demos

相關文章